vue.js自定义指令实现点击waves波纹特效

前言

vue.js的自定义指令的最大优势就是复用,而waves波纹特效可以说是最适合做自定义指令的特效,因为它可以应用在任何按钮或者页面每一个角落里。

本文参考了vue-element-admin的waves特效。

学习自定义指令

可以看我写的《通俗学习Vue.js自定义指令》

然后咱们先做一个不借助自定义指令的DEMO。

按钮waves特效DEMO

准备按钮

准备一个button,用div模拟一个:

我是按钮

waveMe方法的第一部分代码

上面绑定了一个click事件,现在准备waveMe方法,第一部分代码如下,这部分做的事情是:

  • 让按钮relative,这样给按钮插入子元素之后,子元素可以将按钮作为定位祖先。子元素就是产生波纹特效的元素。
  • 给按钮append这个子元素,子元素的class是waves-ripple
  • 子元素的宽高取按钮的最长的一边(例子里是宽最长),这样子元素放大的时候才能充满这个按钮。
      const target = e.target
      target.style.position = 'relative'
      target.style.overflow = 'hidden'
      const rect = target.getBoundingClientRect()
      let ripple = target.querySelector('.waves-ripple')
      if (!ripple) {
        ripple = document.createElement('span')
        ripple.className = 'waves-ripple'
        ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
        target.appendChild(ripple)
      } else {
        ripple.className = 'waves-ripple'
      }

准备CSS

waveMe方法先看上面第一部分,然后看看waves-ripple类是怎么回事:

  • .waves-ripple要绝对定位、圆形、设一个Alpha透明度很透明的黑色背景、先缩小到消失、opacity则完全不透明。opacity是在Alpha透明度的基础上再做透明度计算,也就是乘法。

  • .waves-ripple.z-active是说,一旦加上.z-active,希望实现:用1.2秒时间opacity从1到0,用0.6秒时间从0到放大2倍,两个特效是同时开始的。视觉效果就是:不到0.6秒的时间里,波纹就已经充满了按钮,因为0.6秒的时候已经2倍大了,充满按钮之后还有不到一秒的时间会看到波纹慢慢变淡直到消失。

其实波纹特效本身是没有什么规范的,差不多就可以。

.waves-ripple {
    position: absolute;
    border-radius: 100%;
    background-color: rgba(0, 0, 0, 0.15);
    pointer-events: none;
    user-select: none;
    transform: scale(0);
    opacity: 1;
}

.waves-ripple.z-active {
    opacity: 0;
    transform: scale(2);
    transition: opacity 1.2s ease-out, transform 0.6s ease-out;
}

waveMe方法的第二部分代码

水波的动画上面已经准备好了,现在确定水波的坐标。

这里考虑两种不同的需求,一种是一定要从按钮中心点开始水波扩散,另一种是从真正的点击位置进行鼠标扩散。

      const type = 'center'
      switch (type) {
        case 'center':
          ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px'
          ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px'
          break
        default:
          ripple.style.top =
            e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop + 'px'
          ripple.style.left =
            e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft + 'px'
      ripple.className = 'waves-ripple z-active'
center情况

我解释一下ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px'

这里面rect.height就是按钮高度,除以2就是半高,ripple.offsetHeight是水波元素的高度,除以2也是元素的半高。怎么理解?我说一种通俗的理解方法:

假如水波元素没有高度,那么,水波元素的top就真的是按钮的半高就可以了。
假如水波元素有10像素高度,那么,top如果还是按钮半高,那么水波元素的中心点就不再是按钮的中心点,而是靠下了,靠下了5像素。所以,水波元素高度如果是N,被挤下去的距离就是N/2,那么top就要向上修正N/2,也就是减去N/2。这么理解就可以了。

其他情况,也就是从点击位置发散水波

以这句代码为例解释一下:

          ripple.style.top =
            e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop + 'px'

e.pageY:鼠标相对于文档坐标(注意,不是相对于浏览器窗口)的纵坐标

rect.top:按钮左上角相对于浏览器窗口的纵坐标

ripple.offsetHeight / 2:水波元素的半高

document.documentElement.scrollTop:滚动条的纵坐标,其实意思就是“页面当前可视部分的上沿,距离页面顶部的距离”

这一连串相减,怎么理解:

先考虑“鼠标相对于文档坐标”减去“滚动条的纵坐标”,等于鼠标相对于浏览器视口的纵坐标,再减去“按钮左上角相对于浏览器窗口的纵坐标”,等于按钮左上角到鼠标的纵距离,再减水波元素的半高,道理跟上面的center场景一样,因为水波有高度,是为了抵消高度带来的偏差,最后等于水波元素的top值。

改写成局部自定义指令

我是按钮
  data() {
    return {
      wavePoint: 'hit' // 或 'center'
    };
  },
  directives: {
    wave: {
      bind(el, binding) {
        el.addEventListener('click', function(e) {
          // 贴 waveMe 的代码段
          // 注意一处改动 const type = binding.arg
        })
      }
    }
  }

改写成全局自定义指令

Vue.directive('wave', {
  bind(el, binding) {
          // 同上,贴 waveMe 的代码段
  }
})

改写成Vue.js的插件

Vue.js的插件编写模式有多种,这里介绍比较流行的一种,做法是在src/directive/wave/中创建三个文件,分别是index.js、wave.css、wave.js:

  1. 创建src/directive/wave/index.js

这是入口文件,负责引入wave.js,并且定义install函数。if (window.Vue)这一句是为了兼容Vue的UMD版本,UMD版本说穿了就是用

你可能感兴趣的:(vue.js自定义指令实现点击waves波纹特效)