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