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

说些题外话

基于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指令,在咱们的组件库中也有这样的指令,所以更欠缺的版本能够去看咱们的源码。心愿对大家有作用。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理