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