vue實現(xiàn)水波漣漪效果的點擊反饋指令
水波效果
當用戶點擊時,會以點擊中心為圓心產(chǎn)生一個水波擴散的漣漪效果,適用各個場景,美觀又不浮夸,關(guān)鍵是可以給用戶帶來很直觀的反饋。

來看實現(xiàn)
首先這里基于Vue3自定義指令進行封裝,Vue3的自定義指令跟Vue2相比變動不是很大,。我們的目標是完成一個水波指令的基本原型,這里循序漸進展開。
定制一個水波紋默認樣式
水波紋實際上就是通過用戶點擊的位置生成一個小圓圈,并且尺寸逐漸擴大到整個被點擊元素的一個過程,所以這里先制定一個水波基本的樣式,并設(shè)置好過度動畫,過度動畫應(yīng)該是一個先慢后快的一個過程,這里使用貝塞爾曲線定制
.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;
}
計算水波紋的位置和直徑
如果確定了水波的直徑、創(chuàng)建時的(x,y)、過度動畫結(jié)束時的(x,y),我們就可以通過transition去渲染水波動畫了,創(chuàng)建時的(x,y)就是用戶點擊的位置,但是水波的直徑和過度動畫結(jié)束時的(x,y)怎么計算呢?我們的元素都是矩形,不論用戶從元素的任意坐標進行點擊,以矩形斜邊作為直徑的圓都可以完美的覆蓋整個元素,斜邊的計算我們利用小學數(shù)學知識求兩邊平方和進行開方得到,下面是過度動畫結(jié)束時的水波推演圖。
第一個箭頭: 期望得到的水波
第二個箭頭: 元素(0,0)點創(chuàng)建的水波
第三個箭頭: 元素(0,0)點創(chuàng)建的水波, 不帶圓角效果

我們可以發(fā)現(xiàn)通過元素(0,0)點創(chuàng)建的水波進行一定偏移就可以得到我們想要的水波,由此我們可以推斷出
動畫結(jié)束時的水波的尺寸 = 圓的斜邊
創(chuàng)建時的(x,y) = 用戶點擊的位置
過度動畫結(jié)束時的(x,y) = 元素(0,0)點創(chuàng)建的水波進行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 }
}
鼠標按下時創(chuàng)建水波
然后我們需要在鼠標按下時創(chuàng)建水波,監(jiān)聽鼠標按下的事件,這里以pc端為例子,剛創(chuàng)建水波時使用transform縮小到0.3,這是作者嘗試過相對合適的創(chuàng)建大小, 然后修改transform觸發(fā)過度水波擴散動畫,這里還加入了透明度的過度,可以使水波漣漪更有質(zhì)感。
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`
// 記錄水波的創(chuàng)建時間
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)
}
}

鼠標抬起時銷毀水波
當鼠標抬起時,只需要找到這個生成的水波節(jié)點修改透明度,再等到透明度修改動畫結(jié)束之后將水波紋節(jié)點移除即可
function removeRipple() {
const container = this
const ripples = container.querySelectorAll('.my-ripple')
if (!ripples.length) {
return
}
const lastRipple = ripples[ripples.length - 1]
// 通過水波的創(chuàng)建時間計算出擴散動畫還需要執(zhí)行多久,確保每一個水波都完整的執(zhí)行了擴散動畫
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去擴展你的指令,比如可以提供修改顏色,禁用狀態(tài)等等選項,這里就不詳細展開了。我們來看一下成果。

寫在最后
到此為止我們就實現(xiàn)了一個簡單的ripple指令,在我們的組件庫中也有這樣的指令,所以更完善的版本可以去看我們的源碼。 先要感謝一下掘金社區(qū),已經(jīng)有一部分小伙伴開始pr一些代碼到我們的倉庫中來,我們也很高興能和社區(qū)的小伙伴們?nèi)ヒ黄鹱鲞@樣一件事情,另外我們的組件庫團隊一直在募集愛好者來參與貢獻,有興趣的小伙伴歡迎加入討論,加入方式就是直接去倉庫提issue留郵箱,我們會第一時間處理,有沒有興趣都希望為我們點點star,關(guān)注一下我們,社區(qū)小伙伴的支持和興趣是我們最大的動力。
以上就是vue實現(xiàn)水波漣漪效果的點擊反饋指令的詳細內(nèi)容,更多關(guān)于vue 點擊反饋指令的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue基于mint-ui的城市選擇3級聯(lián)動的示例
本篇文章主要介紹了vue基于mint-ui的城市選擇3級聯(lián)動的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
vue3緩存頁面keep-alive及路由統(tǒng)一處理詳解
當我們不想每次跳轉(zhuǎn)路由都會重新加載頁面時(重新加載頁面很耗時),就可以考慮使用keep-alive緩存頁面了,這篇文章主要給大家介紹了關(guān)于vue3緩存頁面keep-alive及路由統(tǒng)一處理的相關(guān)資料,需要的朋友可以參考下2021-10-10
vue項目本地開發(fā)完成后部署到服務(wù)器后報404錯誤解決方案
很多時候我們發(fā)現(xiàn)辛辛苦苦寫的VueJs應(yīng)用經(jīng)過打包后在自己本地搭建的服務(wù)器上測試沒有什么問題,但真正放在服務(wù)器上后會發(fā)現(xiàn)或多或少的問題,這篇文章主要給大家介紹了關(guān)于vue項目本地開發(fā)完成后部署到服務(wù)器后報404錯誤的解決方案,需要的朋友可以參考下2024-01-01
解決vue中數(shù)據(jù)更新視圖不更新問題this.$set()方法
這篇文章主要介紹了解決vue中數(shù)據(jù)更新視圖不更新問題this.$set()方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
實用的 vue tags 創(chuàng)建緩存導航的過程實現(xiàn)
這篇文章主要介紹了實用的 vue tags 創(chuàng)建緩存導航的過程實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12

