vue按鈕水波紋效果

要完成以上效果首先咱們的思路是經過點擊外層觸發點擊事件,收集點擊次數而後建立與點擊次數相同的水波紋動畫組件。而後每當水波紋組件動畫執行完以後去銷燬當前這個水波紋組件。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因此咱們這裏引入是一個polyfillrafweb

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咱們獲取到當前鼠標點擊的位置,用於水波紋組件在哪一個位置開始現實。此函數內部經過當前點擊位置pageYpageX減去元素到視口的topleft再減去當前元素的最大寬度或最大高度的一半來獲取到當前點擊的位置topleft的值。
  • 最後咱們經過定義一個ripples數組用於收集點擊的次數。數組中包含了水波紋組件的stylekey,其中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,執行銷燬組件函數clearWaveapp

<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);
                  });
            }
      }
}
複製代碼

到這來就完成了一個簡易的水波紋動畫組件了。

如想查看詳細的組件代碼能夠查看這裏。
關於查看水波紋按鈕效果能夠查看這裏

相關文章
相關標籤/搜索