要完成以上效果首先咱們的思路是經過點擊外層觸發點擊事件,收集點擊次數而後建立與點擊次數相同的水波紋動畫組件。而後每當水波紋組件動畫執行完以後去銷燬當前這個水波紋組件。javascript
第一步咱們先去製做這個水波分動畫組件css
// wave.vue
<template>
<transition name="wave" @after-enter="end">
<span v-show="animating" class="ripple-wave"></span>
</transition>
</template>
<script>
export default {
data () {
return {
animating: true
}
},
props: {
waveClasses: {
type: String,
default: null
},
waveStyles: {
type: String,
default: null
}
},
methods: {
// 動畫結束
end () {
this.animating = null;
this.$emit('animating-end')
}
}
}
</script>
<style lang="scss" scoped>
.ripple-wave {
position: absolute;
z-index: 1;
pointer-events: none;
background: currentColor;
border-radius: 50%;
opacity: 0;
transform: scale(2) translateZ(0);
width: 100%;
height: 100%;
~ *:not(.ripple-wave) {
position: relative;
z-index: 2;
}
}
.wave-enter-active {
transition: 0.8s cubic-bezier(0.25, 0.8, 0.25, 1);
transition-property: opacity, transform;
will-change: opacity, transform;
}
.wave-enter {
opacity: 0.26;
transform: scale(0.26) translateZ(0);
}
</style>
複製代碼
以上主要是使用了transition
組件去包裹一個標籤,而後每當transition
組件動畫執行完以後去調用end
方法向外部發送動畫結束事件。 而後咱們只須要在外部組件去監聽到這個事件而後對其進行銷燬,就能確保當前組件不會殘留在 DOM 節點中了。vue
接下來咱們來看看外部組件我怎麼實現對該動畫組件進行增長和刪除的。java
咱們先來建立外部組件的一個骨架。git
<template>
<div
class="ripple"
@touchstart.passive="touchstart"
@touchmove.passive="touchmove"
@mousedown.passive="mousedown"
>
<slot></slot>
<Wave></Wave>
</div>
</template>
<script>
import Wave from './wave';
export default {
data () {
return {
}
},
methods: {
// 觸摸開始
touchstart (event) {
},
// 觸摸移動
touchmove (event) {
},
// 鼠標點擊
mousedown () {
}
},
components: {
Wave
}
}
</script>
<style lang="scss" scoped>
.ripple {
width: 100%;
height: 100%;
position: relative;
z-index: 10;
overflow: hidden;
-webkit-mask-image: radial-gradient(circle, #fff 100%, #000 100%);
box-sizing: border-box;
}
</style>
複製代碼
以上建立了一個外殼用於包裹水波紋動畫組件,而後在上面定義了手指觸摸事件和鼠標點擊事件,咱們先來製做鼠標點擊事件。github
因爲咱們製做此組件須要用到requestAnimationFrame
,可是對於IE或其餘瀏覽器可能不支持requestAnimationFrame
因此咱們這裏引入是一個polyfill
庫 raf。web
import raf from 'raf';
export default {
data () {
return {
/* * 事件類型 * * @type {String} */
eventType: null,
/* * 漣漪數組 * * @type {Array} */
ripples: []
}
},
methods: {
// 鼠標點擊
mousedown (event) {
return this.startRipple(event);
},
// 漣漪開始
startRipple ($event) {
console.log($event)
raf(() => {
if (!this.eventType || this.eventType === $event.type) {
//當前元素位置
let size = this.getSize();
// 漣漪位置
let position = null;
// 獲取點擊的位置
position = this.getHitPosition($event, size);
// 事件類型
this.eventType = $event.type;
this.ripples.push({
// 波浪 style
waveStyles: this.applyStyles(position, size),
uuid: this.uuid()
});
}
});
},
getSize () {
const { offsetWidth, offsetHeight } = this.$el;
return Math.round(Math.max(offsetWidth, offsetHeight));
},
// 獲取點擊的位置
getHitPosition ($event, elementSize) {
// 元素的大小及其相對於視口的位置。
const rect = this.$el.getBoundingClientRect();
let top = $event.pageY;
let left = $event.pageX;
return {
top: top - rect.top - elementSize / 2 - document.documentElement.scrollTop + 'px',
left: left - rect.left - elementSize / 2 - document.documentElement.scrollLeft + 'px'
}
},
// 樣式
applyStyles (position, size) {
size += 'px';
return {
...position,
width: size,
height: size
}
},
uuid () {
return Math.random().toString(36).slice(4);
}
}
}
複製代碼
以上代碼咱們引入了raf
庫,而後定義一個函數startRipple
經過鼠標點擊觸發,此函數用於建立水波紋組件數量。數組
eventType
是不是當前點擊事件來肯定是否建立水波紋組件,防止其餘事件誤觸發,致使建立了水波紋組件。getSize
,用於獲取到當前點擊此組件時的寬度啊和高度,而後返回了最大值,此值正是水波紋組件的最大寬度和高度。getHitPosition
咱們獲取到當前鼠標點擊的位置,用於水波紋組件在哪一個位置開始現實。此函數內部經過當前點擊位置pageY
和pageX
減去元素到視口的top
和left
再減去當前元素的最大寬度或最大高度的一半來獲取到當前點擊的位置top
和left
的值。ripples
數組用於收集點擊的次數。數組中包含了水波紋組件的style
和key
,其中uuid
是一個隨機數。咱們還須要去循環建立水波紋組件。瀏覽器
<template>
<div
:class="['ripple']"
@touchstart.passive="touchstart"
@touchmove.passive="touchmove"
@mousedown.passive="mousedown"
>
<slot></slot>
<Wave
v-for="ripple in ripples"
:key="ripple.uuid"
:style="ripple.waveStyles"
></Wave>
</div>
</template>
複製代碼
這裏咱們除了建立還差一個函數,就是動畫執行完以後須要銷燬當前組件,由於若是不銷燬該組件,它會一直殘留在DOM中。在外層組件中監聽水波紋組件動畫結束時發送的事件animating-end
,執行銷燬組件函數clearWave
。app
<template>
<div
:class="['ripple']"
@touchstart.passive="touchstart"
@touchmove.passive="touchmove"
@mousedown.passive="mousedown"
>
<slot></slot>
<Wave
v-for="ripple in ripples"
:key="ripple.uuid"
:style="ripple.waveStyles"
@animating-end="clearWave(ripple.uuid)"
></Wave>
</div>
</template>
export default {
methods: {
// 清除波紋效果
clearWave (uuid) {
if (uuid) {
this.ripples = this.ripples.filter((ripple) => {
return ripple.uuid !== uuid
});
}
else {
this.ripples = [];
}
}
}
}
複製代碼
最後咱們繼續完成手指觸摸事件,其實很簡單咱們只須要判斷只有當手指觸摸才觸發便可。
export default {
methods: {
// 觸摸開始
touchstart (event) {
return this.touchStartCheck(event);
},
// 觸摸移動
touchmove (event) {
return this.touchMoveCheck(event);
},
// 檢查觸摸開始
touchStartCheck ($event) {
this.touchTimeout = window.setTimeout(() => {
this.startRipple($event);
});
}
}
}
複製代碼
到這來就完成了一個簡易的水波紋動畫組件了。