新聞中心
最近在使用 vue2 做一個新的 material ui 庫,波紋點(diǎn)擊效果在 material design 中被多次使用到,于是決定把它封裝成一個公共的組件,使用時直接調(diào)用就好啦。

開發(fā)之前的思考
常見的波紋點(diǎn)擊效果的實(shí)現(xiàn)方式是監(jiān)聽元素的 mousedown 事件,在元素內(nèi)部創(chuàng)建一個 波紋元素 ,并調(diào)整元素的 transform: scale(0); 到 transform: scale(1);, 通過計算點(diǎn)擊的位置來設(shè)置 波紋元素 的大小和位置,以達(dá)到波紋擴(kuò)散的效果。
我將組件分為兩個部分, circleRipple.vue 和 TouchRipple.vue 各自實(shí)現(xiàn)不同的功能
- circleRipple.vue 波紋擴(kuò)散組件,完成波紋擴(kuò)散的效果
- TouchRipple.vue 監(jiān)聽 mouse 和 touch 相關(guān)事件,控制 circleRipple 的顯示,位置。
circleRipple.vue
circleRipple 需要完成波紋擴(kuò)展的效果,而且可以從外部控制它的大小和位置, 所以利用 vue 的 transition 動畫完成效果, 提供mergeStyle 、 color 、opacity 參數(shù)來從外部控制它的樣式。實(shí)現(xiàn)代碼如下。
vue2 對于動畫方面做了比較大的修改,除了把指令換成組件外,它還可以完成更復(fù)雜的動畫效果,具體可以看這里 vue2 transition
TouchRipple.vue
TouchRipple 需要控制 circleRipple 的顯示。完成以下內(nèi)容:
- 監(jiān)聽 mouse 和 touch 相關(guān)事件, 控制 circleRipple 的顯示。
- 通過點(diǎn)擊事件 event 對象, 計算出 circleRipple 的大小和位置
- 如果頻繁點(diǎn)擊可能出現(xiàn)多個 circleRipple
首先,基本模板 + 數(shù)據(jù)模型
開始和結(jié)束波紋效果
增加一個波紋元素只需要在 ripple 增加一個 object 即可,不同的是當(dāng)需要從點(diǎn)擊處擴(kuò)展時,需要計算一下波紋元素的大小和位置。
- {
- // isRippleTouchGenerated 是否是touch 事件開始的
- start (event, isRippleTouchGenerated) {
- // 過濾 touchstart 和 mousedown 同時存在的情況
- if (this.ignoreNextMouseDown && !isRippleTouchGenerated) {
- this.ignoreNextMouseDown = false
- return
- }
- // 添加一個 波紋元素組件
- this.ripples.push({
- key: this.nextKey++,
- color: this.color,
- opacity: this.opacity,
- style: this.centerRipple ? {} : this.getRippleStyle(event) // 不是從中心擴(kuò)展的需要計算波紋元素的位置
- })
- this.ignoreNextMouseDown = isRippleTouchGenerated
- },
- end () {
- if (this.ripples.length === 0) return
- this.ripples.splice(0, 1) // 刪除一個波紋元素
- this.stopListeningForScrollAbort() // 結(jié)束 touch 滾動的處理
- }
- }
因?yàn)?vue2 基于 Virtual DOM 的, 所以如果沒有 key 在增加一個元素又同時刪除一個元素的時候,dom tree并沒有發(fā)生變化,是不會產(chǎn)生動畫效果的。
監(jiān)聽 mousedown 和 touchstart
mousedown 和 touchstart 處理上會有所不同,但都是用來啟動波紋效果的, touch涉及到多點(diǎn)點(diǎn)擊的問題,我們一般取***個即可。
- {
- handleMouseDown (event) {
- // 只監(jiān)聽鼠標(biāo)左鍵的點(diǎn)擊
- if (event.button === 0) {
- this.start(event, false)
- }
- },
- handleTouchStart (event) {
- event.stopPropagation() // 防止多個波紋點(diǎn)擊組件嵌套
- if (event.touches) {
- this.startListeningForScrollAbort(event) // 啟動 touchmove 觸發(fā)滾動處理
- this.startTime = Date.now()
- }
- this.start(event.touches[0], true)
- }
- }
touchmove控制
當(dāng)發(fā)生touchMove事件是需要判斷是否,移動的距離和時間,然后結(jié)束小波紋點(diǎn)擊小姑
- {
- // touchmove 結(jié)束波紋控制
- stopListeningForScrollAbort () {
- if (!this.handleMove) this.handleMove = this.handleTouchMove.bind(this)
- document.body.removeEventListener('touchmove', this.handleMove, false)
- },
- startListeningForScrollAbort (event) {
- this.firstTouchY = event.touches[0].clientY
- this.firstTouchX = event.touches[0].clientX
- document.body.addEventListener('touchmove', this.handleMove, false)
- },
- handleTouchMove (event) {
- const timeSinceStart = Math.abs(Date.now() - this.startTime)
- if (timeSinceStart > 300) {
- this.stopListeningForScrollAbort()
- return
- }
- const deltaY = Math.abs(event.touches[0].clientY - this.firstTouchY)
- const deltaX = Math.abs(event.touches[0].clientX - this.firstTouchX)
- // 滑動范圍在 > 6px 結(jié)束波紋點(diǎn)擊效果
- if (deltaY > 6 || deltaX > 6) this.end()
- }
- }
計算波紋的位置和大小
需要從點(diǎn)擊處擴(kuò)散的波紋效果,需要計算波紋元素的大小和位置
- {
- getRippleStyle (event) {
- let holder = this.$refs.holder
- // 這個方法返回一個矩形對象,包含四個屬性:left、top、right和bottom。分別表示元素各邊與頁面上邊和左邊的距離。
- let rect = holder.getBoundingClientRect()
- // 獲取點(diǎn)擊點(diǎn)的位置
- let x = event.offsetX
- let y
- if (x !== undefined) {
- y = event.offsetY
- } else {
- x = event.clientX - rect.left
- y = event.clientY - rect.top
- }
- // 獲取***邊長
- let max
- if (rect.width === rect.height) {
- max = rect.width * 1.412
- } else {
- max = Math.sqrt(
- (rect.width * rect.width) + (rect.height * rect.height)
- )
- }
- const dim = (max * 2) + 'px'
- return {
- width: dim,
- height: dim,
- // 通過margin控制波紋中心點(diǎn)和點(diǎn)擊點(diǎn)一致
- 'margin-left': -max + x + 'px',
- 'margin-top': -max + y + 'px'
- }
- }
- }
使用
由于 touchRipple 內(nèi)部都是 position:absolute 布局,使用時,需要在外部加上 position:relative
***
到這點(diǎn)擊波紋組件就開發(fā)完了, 這些代碼借鑒了 keen-ui 和 material-ui 的實(shí)現(xiàn)方式。
文章題目:如何使用vue開發(fā)波紋點(diǎn)擊特效組件
文章URL:http://www.dlmjj.cn/article/djdpscg.html


咨詢
建站咨詢
