欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用JavaScript實現(xiàn)按鈕的漣漪效果實例代碼

 更新時間:2022年11月04日 16:02:50   作者:nnmax  
近來看到個不錯的按鈕點擊效果,當(dāng)點擊時產(chǎn)生一次水波漣漪效果,挺好玩的,下面這篇文章主要給大家介紹了關(guān)于使用JavaScript實現(xiàn)按鈕漣漪效果的相關(guān)資料,需要的朋友可以參考下

前言

不知道你們有沒有使用過 Material UI。這是一個 React UI 組件庫,它實現(xiàn)了 Google 的 Material Design。

Material Design 設(shè)計規(guī)范中包含了很多關(guān)于點擊的漣漪效果,類似于一塊石頭跌落水中所產(chǎn)生的波浪效果。

以下是效果圖

速度放慢之后的效果:

我們把 overflow: hidden 去掉之后:

本文就以 Material Design 中的漣漪效果作為目標(biāo),來使用原生的 JavaScript、CSS、HTML 來實現(xiàn)此效果。

分析

通過觀察,我們可以發(fā)現(xiàn)點擊的漣漪效果是在鼠標(biāo)點擊的點開始以一個正圓往外擴散。當(dāng)圓形擴散到正好能將 Button 全部包圍住的時候停止,在擴散的過程中顏色逐漸變淺直到消失,并且此效果可以疊加。

長按效果也是一個圓往外擴散,只不過是在長按結(jié)束之后,圓才會消失。

除了鼠標(biāo)點擊效果外,還有鍵盤焦點事件的效果。當(dāng)使用鍵盤的 Tab 鍵切換到按鈕上的時候,會有一個抖動的效果,類似于呼吸效果。

我們提煉出幾個比較關(guān)鍵的點:

  • 從鼠標(biāo)點擊的位置開始擴散;
  • 是一個正圓形;
  • 圓形擴散到正好能將 Button 全部包圍住的時候停止;
  • 長按效果;
  • 效果疊加;

第一點,我們可以通過 JavaScript 計算當(dāng)前鼠標(biāo)的坐標(biāo)信息;第三點,獲取 Button 四個頂點的坐標(biāo)信息,再選一個距離鼠標(biāo)最遠的點,以它們的距離作為半徑來畫一個圓;第五點,每一個效果都是一個 dom 元素,每點擊一次就追加一個 dom 元素,在動畫結(jié)束的時候,移除此 dom;

實現(xiàn)

創(chuàng)建一個 index.html 文件,包含以下內(nèi)容;

<!-- index.html -->
<style>
  @import 'button.css';
  @import 'ripple.css';
</style>

<button class="button-root" id="ripple-example-button" type="button">
  Button
  <!-- 用來裝漣漪效果的 DOM 的容器 -->
  <span class="ripple-root"></span>
</button>

創(chuàng)建 button.css 和 ripple.css,分別是 Button 的基礎(chǔ)樣式和漣漪效果的樣式。

/* button.css */
.button-root {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px 16px;
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.75;
  min-width: 64px;
  margin: 0;
  border-radius: 4px;
  border: 1px solid rgba(25, 118, 210, 0.5);
  cursor: pointer;
  box-sizing: border-box;
  outline: none;
  appearance: none;
  user-select: none;
  color: #1976d2;
  background-color: transparent;
  transition-property: background-color, color, box-shadow, border-color;
  transition-duration: 0.25s;
}

.button-root:hover {
  background-color: rgba(25, 118, 210, 0.04);
  border: 1px solid #1976d2;
}
/* ripple.css */
@keyframes enterKeyframe {
  0% {
    transform: scale(0);
    opacity: 0.1;
  }

  100% {
    transform: scale(1);
    opacity: 0.3;
  }
}

@keyframes exitKeyframe {
  0% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
}

@keyframes pulsateKeyframe {
  0% {
    transform: scale(0.9);
  }

  50% {
    transform: scale(0.8);
  }

  100% {
    transform: scale(0.9);
  }
}

.ripple-root {
  display: block;
  position: absolute;
  overflow: hidden;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  background-color: transparent;
  z-index: 0;
  border-radius: inherit;
}

.ripple-root > .ripple-child {
  position: absolute;
  display: block;
  opacity: 0;
}

.ripple-root > .ripple-child.enter {
  opacity: 0.3;
  transform: scale(1);
  animation: enterKeyframe 550ms ease-in-out;
}

.ripple-root > .ripple-child > .ripple-child-child {
  opacity: 1;
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background-color: currentColor;
}

.ripple-root > .ripple-child.exit > .ripple-child-child {
  opacity: 0;
  animation: exitKeyframe 550ms ease-in-out;
}

.ripple-root > .ripple-child.pulsate > .ripple-child-child {
  position: absolute;
  left: 0;
  top: 0;
  animation: pulsateKeyframe 2500ms ease-in-out 200ms infinite;
}

開始寫 JavaScript。創(chuàng)建一個 ripple.apis.js 文件,編寫 startRipple 函數(shù)。該函數(shù)首先要獲取 Button 的位置信息和寬高。

// ripple.apis.js
export function startRipple(event) {
  const { currentTarget: container } = event
  const { left, top, width, height } = container.getBoundingClientRect()
}

接著計算開始擴散的位置。

// ripple.apis.js
export function startRipple(event) {
  // ...
  // 效果開始的坐標(biāo)(相對于 Button)
  let rippleX, rippleY
  // 鼠標(biāo)當(dāng)前的坐標(biāo)
  let clientX = 0, clientY = 0
 
 /**
  * 漣漪效果是否從節(jié)點的中心擴散,否則從鼠標(biāo)點擊的位置開始擴散
  * 使用 Tab 鍵移動焦點的時候,從節(jié)點的中心擴散
  */
  let center = false
  let isFocusVisible = false
 
  if (container.matches(':focus-visible')) {
    center = isFocusVisible = true
  } else {
    clientX = event.clientX
    clientY = event.clientY
  }
 
  rippleX = center ? width / 2 : clientX - left
  rippleY = center ? height / 2 : clientY - top
}

通過勾股定理,構(gòu)造一個能正好包圍當(dāng)前元素的圓。

// ripple.apis.js
export function startRipple(event) {
  // ...
  // 從鼠標(biāo)點擊的中心位置,構(gòu)造一個能正好包圍當(dāng)前元素的圓
  const sizeX = Math.max(width - rippleX, rippleX) * 2
  const sizeY = Math.max(height - rippleY, rippleY) * 2
  const diagonal = Math.sqrt(sizeX ** 2 + sizeY ** 2)
}

再創(chuàng)建一個 createRippleChild 函數(shù),用來創(chuàng)建漣漪效果的 DOM,并且使用一個全局變量來保存已經(jīng)創(chuàng)建的 DOM。

// ripple.apis.js
const rippleChildren = []

/**
 * 創(chuàng)建以下結(jié)構(gòu)并返回:
 * <span class="ripple-child enter">
 *   <span class="ripple-child-child"></span>
 * </span>
 */
function createRippleChild(rect) {
  const rippleChild = document.createElement('span')
  rippleChild.classList.add('ripple-child', 'enter')
  const rippleChildChild = document.createElement('span')
  rippleChildChild.classList.add('ripple-child-child')
  rippleChild.appendChild(rippleChildChild)

  const { height, left, top, width } = rect
  rippleChild.style.height = height
  rippleChild.style.width = width
  rippleChild.style.top = top
  rippleChild.style.left = left

  rippleChildren.push(rippleChild)

  return rippleChild
}

回到 startRipple 函數(shù),使用剛才創(chuàng)建的 createRippleChild 函數(shù)。

// ripple.apis.js
export function startRipple(event) {
  // ...
  const rippleChild = createRippleChild({
    width: `${diagonal}px`,
    height: `${diagonal}px`,
    left: `${-diagonal / 2 + rippleX}px`,
    top: `${-diagonal / 2 + rippleY}px`,
  })
  if (isFocusVisible) {
    rippleChild.classList.add('pulsate')
  }
  const rippleRoot = container.querySelector(':scope > .ripple-root')
  rippleRoot.appendChild(rippleChild)
}

完成了 startRipple 函數(shù)之后,我們再創(chuàng)建一個 stopRipple 函數(shù)。該函數(shù)中,從 rippleChildren 取出最早創(chuàng)建的 DOM,添加一個動畫結(jié)束的監(jiān)聽事件,在動畫結(jié)束的時候,刪除該 DOM。

// ripple.apis.js
export function stopRipple() {
  const rippleChild = rippleChildren.shift()

  if (!rippleChild) return

  rippleChild.addEventListener('animationend', (event) => {
    if (event.animationName === 'exitKeyframe') {
      rippleChild.remove()
    }
  })
  rippleChild.classList.add('exit')
}

此時,我們已經(jīng)完成了大部分的代碼,接下來就是給 Button 綁定事件的時候了。在 index.html 文件中添加以下代碼:

<!-- index.html -->
<style>
  @import 'button.css';
  @import 'ripple.css';
</style>

<script type="module">
  import { startRipple, stopRipple } from 'ripple.apis.js'

  const button = document.querySelector('#ripple-example-button')

  button.addEventListener('mousedown', startRipple)
  button.addEventListener('focus', startRipple)
  button.addEventListener('mouseup', stopRipple)
  button.addEventListener('mouseleave', stopRipple)
  button.addEventListener('blur', stopRipple)
</script>

<button class="button-root" id="ripple-example-button" type="button">
  Button
  <!-- 用來裝漣漪效果的 DOM 的容器 -->
  <span class="ripple-root"></span>
</button>

我們完成了所有的功能!完整的代碼在此倉庫中。

也可以直接在 CodeSandbox 中編輯

總結(jié)

到此這篇關(guān)于使用JavaScript實現(xiàn)按鈕的漣漪效果的文章就介紹到這了,更多相關(guān)js實現(xiàn)按鈕漣漪效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論