关于前端:Vue组件设计-实现水波涟漪效果的点击反馈指令

39次阅读

共计 3409 个字符,预计需要花费 9 分钟才能阅读完成。

说些题外话

基于 Vue3 的挪动端组件库 Varlet 邀请有趣味参加的社区敌人们退出其中,有动向退出咱们的能够到仓库的 issue 列表中留下邮箱,咱们会给您发邮件邀请您退出。

仓库地址
文档地址

点击反馈

不晓得小伙伴们有没有留神过这样一个细节,有的利用按钮,链接,可交互的卡片点击起来非常有感觉,而有的却像是点在白纸上了一样,是什么造成了他们使用户有如此显著的感触辨别呢?….

鼠标移入时的小手、鼠标点击时按钮下压弹起的动画、触屏利用点击时的屏幕触动,这些成果都给予用户一种 是我的行为产生了这样的成果 的直觉,这些成果也被统称为点击反馈,尽管看似是利用中的细枝末节,然而只有略微投入一点点心理,带来的用户体验晋升是非常显著的

水波成果

这里作者为小伙伴们举荐一种作者最喜爱的点击反馈成果。当用户点击时,会以点击核心为圆心产生一个水波扩散的涟漪成果,实用各个场景,好看又不虚夸,要害是能够给用户带来很直观的反馈。

来看实现

首先这里基于 Vue3 自定义指令进行封装,Vue3 的自定义指令跟 Vue2 相比变动不是很大,具体阐明请看 Vue3 自定义指令。咱们的指标是实现一个水波指令的根本原型,这里循序渐进开展。

定制一个水波纹默认款式

水波纹实际上就是通过 用户点击的地位 生成一个小圆圈,并且尺寸逐步扩充到整个被点击元素的一个过程,所以这里先制订一个水波根本的款式,并设置好适度动画,适度动画应该是一个先慢后快的一个过程,这里应用贝塞尔曲线定制,不分明如何调试动画曲线的能够看这一篇文章

.my-ripple {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 100;
  border-radius: 50%;
  background-color: currentColor;
  opacity: 0;
  transition: transform 0.2s cubic-bezier(0.68, 0.01, 0.62, 0.6), opacity 0.08s linear;
  will-change: transform, opacity;
  pointer-events: none;
}

计算水波纹的地位和直径

如果确定了 水波的直径 创立时的 (x,y) 适度动画完结时的 (x,y),咱们就能够通过transition 去渲染水波动画了,创立时的 (x,y) 就是用户点击的地位,然而 水波的直径 适度动画完结时的 (x,y) 怎么计算呢?咱们的元素都是矩形,不管用户从元素的任意坐标进行点击,以矩形斜边作为直径的圆都能够完满的笼罩整个元素,斜边的计算咱们利用 小学数学知识 求两边平方和进行开方失去,上面是 适度动画完结时的水波推演图

第一个箭头 : 冀望失去的水波

第二个箭头 : 元素 (0,0) 点创立的水波

第三个箭头 : 元素 (0,0) 点创立的水波, 不带圆角成果

咱们能够发现通过元素 (0,0) 点创立的水波进行肯定偏移就能够失去咱们想要的水波, 由此咱们能够推断出

动画完结时的水波的尺寸 = 圆的斜边

创立时的 (x,y) = 用户点击的地位

适度动画完结时的 (x,y) = 元素 (0,0) 点创立的水波进行 x 和 y 的偏移失去

function computeRippleStyles(element, event) {const { top, left} = element.getBoundingClientRect()
  const {clientWidth, clientHeight} = element
  
  const radius = Math.sqrt(clientWidth ** 2 + clientHeight ** 2) / 2
  const size = radius * 2
  
  const localX = event.clientX - left
  const localY = event.clientY - top

  const centerX = (clientWidth - radius * 2) / 2
  const centerY = (clientHeight - radius * 2) / 2

  const x = localX - radius
  const y = localY - radius

  return {x, y, centerX, centerY, size}
}

鼠标按下时创立水波

而后咱们须要在鼠标按下时创立水波,监听鼠标按下的事件,这里以 pc 端为例子,刚创立水波时应用 transform 放大到0.3,这是作者尝试过绝对适合的创立大小, 而后批改 transform 触发适度水波扩散动画,这里还退出了透明度的适度,能够使水波涟漪更有质感。

function createRipple(event) {
  const container = this
  const {x, y, centerX, centerY, size} = computeRippleStyles(container, event)
  const ripple = document.createElement('div')
  ripple.classList.add('my-ripple')
  ripple.style.opacity = `0`
  ripple.style.transform = `translate(${x}px, ${y}px) scale3d(.3, .3, .3)`
  ripple.style.width = `${size}px`
  ripple.style.height = `${size}px`
  // 记录水波的创立工夫
  ripple.dataset.createdAt = String(performance.now())

  const {position} = window.getComputedStyle(container)
  container.style.overflow = 'hidden'
  position === 'static' && (this.style.position = 'relative')

  container.appendChild(ripple)

  window.setTimeout(() => {ripple.style.transform = `translate(${centerX}px, ${centerY}px) scale3d(1, 1, 1)`
    ripple.style.opacity = `.25`
  })
}

const VRipple = {mounted(el) {el.addEventListener('mousedown', createRipple)
  }
}

鼠标抬起时销毁水波

当鼠标抬起时,只须要找到这个生成的水波节点批改透明度,再等到透明度批改动画完结之后将水波纹节点移除即可

function removeRipple() {
  const container = this
  const ripples = container.querySelectorAll('.my-ripple')
  if (!ripples.length) {return}

  const lastRipple = ripples[ripples.length - 1]
  // 通过水波的创立工夫计算出扩散动画还须要执行多久,确保每一个水波都残缺的执行了扩散动画
  const delay = 300 - performance.now() + Number(lastRipple.dataset.createdAt)

  setTimeout(() => {
    lastRipple.style.opacity = `0`
    
    setTimeout(() => lastRipple.parentNode?.removeChild(lastRipple), 300)
  }, delay)
}

const VRipple = {mounted(el) {el.addEventListener('mousedown', createRipple)
    document.addEventListener('mouseup', removeRipple)
  },
  unmounted(el) {el.removeEventListener('mousedown', createRipple)
    document.removeEventListener('mouseup', removeRipple)
  }
}

通过指令 binding 去扩大你的水波选项

你还能够通过 binding 去扩大你的指令,比方能够提供批改色彩,禁用状态等等选项,这里就不具体开展了。咱们来看一下成绩。

写在最初

到此为止咱们就实现了一个简略的 ripple 指令,在咱们的组件库中也有这样的指令,所以更欠缺的版本能够去看咱们的源码。心愿对大家有作用。

正文完
 0