按钮点击水波纹效果封装(Vue3指令)

指令主体

我是放在utils目录下,建了directive.ts(自定义指令)文件
若有更优方法,欢迎留言讨论交流!

文件中class类(本计划将class单独放入一个文件,但是为了方便其他项目复用时只需要复制一个文件即可,便直接将class Ripple内容放在了directive.ts内):

// 水波纹类
class Ripple {
  el: HTMLElement
  rippleColor: string
  animationListener: any
  mouseDownListener: any

  constructor(config: any) {
    this.el = config.el
    this.rippleColor = config.color || 'rgba(0, 0, 0, .35)'
  }

  // 创建水波纹动画节点并插入
  createRieEl() {
    let el = document.createElement('span')
    el.classList.add('ani_ripple')
    // 添加动画监听
    this.animationListener = this.onAnimationEnd.bind(this)
    el.addEventListener('animationend', this.animationListener)
    this.el.appendChild(el)
    return el
  }

  // 初始化水波纹动画
  init() {
    // 添加点击事件监听
    this.mouseDownListener = this.onMouseDown.bind(this)
    // 增加鼠标按下事件
    this.el.addEventListener('mousedown', this.mouseDownListener)
  }

  // 点击事件
  onMouseDown(e: any) {
    let riEl: any = this.getRieEl()
    if (riEl) {
      // 删除旧节点为了频繁点击动画刷新
      this.el.removeChild(riEl)
    }
    riEl = this.createReEl()
    // 取宽和高较大的值作为水波纹大小
    const max = Math.max(this.el.clientWidth, this.el.clientHeight)
    const style = {
      left: e.offsetX - max / 2 + 'px',
      top: e.offsetY - max / 2 + 'px',
      width: max + 'px',
      height: max + 'px',
      position: 'absolute',
      transform: 'scale(0)',
      pointerEvents: 'none',
      borderRadius: '100%',
      backgroundColor: this.rippleColor,
      animation: 'ripple 1s ease-out'
    }
    Object.assign(riEl.style, style)
  }

  // 查找是否已经有了水波纹动画节点
  getRieEl() {
    if (this.el.children.length === 0) return null
    for (const i in this.el.children) {
      if (this.el.children[i].className?.includes('ani_ripple')) {
        return this.el.children[i]
      }
    }
    return null
  }

  // 清除水波纹动画及其节点
  clearRie() {
    let riEl = this.getRieEl();
    if (riEl) {
      this.el.removeEventListener('mousedown', this.mouseDownListener)
      riEl.removeEventListener('animationend', this.animationListener)
      this.el.removeChild(riEl)
    }
  }

  // 动画结束事件--暂时可以不用,也可以添加判断:结束之前不可重复动画
  onAnimationEnd() {
  }
}

自定义指令:

import type { Directive, DirectiveBinding } from "vue"
interface elType extends HTMLElement {
  _clearRipple: Function
}
// ------指令用法,在页面中导入文件,直接const vRipple = ripple即可
// 按钮水波纹效果指令
export const ripple: Directive = {
  mounted(el: elType, binding: DirectiveBinding) {
    let options = new Ripple({ el })
    options.init()
    // 绑定水波纹清除事件
    el._clearRipple = options.clearRie.bind(options)
  },
  beforeUnmount(el, binding, vnode, prevVnode) {
    el._clearRipple()
  }
}

// 防抖指令v-debounce
// 节流指令v-throttle
// 拖拽指令v-draggable

组件引用部分

html内容v-ripple直接使用即可

<template>
  <!-- 炫酷按钮列表 -->
  <ul class="qc_btnlist flex_items_center" :class="'theme_' + themeName">
    <li
      v-for="item in btnList"
      :class="[
        'qc_btnlist_item',
        'qc_btnitem_' + item.position,
        modelValue === item.type && 'qc_btnitem_highlight'
      ]"
      v-ripple
      @click="btnClick(item)"
    >
      <!-- <slot v-if="item.slotName"> </slot> -->
      <div class="flex_items_center qc_btn_cont">
        <span
          class="icon"
          v-if="item.isIcon === undefined ? true : item.isIcon"
          :style="{ backgroundImage: `url(${item.icon})` }"
        ></span>
        {{ item.text }}
      </div>
    </li>
  </ul>
</template>

ts内容部分,只写了指令导入,按钮的代码就不列出来了

<script lang="ts" setup>
import { ripple } from '@/utils/directive'
// 自定义水波纹指令
const vRipple = ripple
// ......
</script>

css部分,只需要写个动画即可,建议在项目根目录建animation.css文件,自定义的动画均放在里面(包括Transation组件动画)

/* 按钮水波纹动画 */
@keyframes ripple {
  100% {
    -webkit-transform: scale(2);
    transform: scale(2);
    opacity: 0;
  }
}

参考了部分oumae-kumiko的文章,若存在纠纷问题,请留言提示!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值