说些题外话

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