vue directives自定义指令的使用

有的情况下,需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。

自定义指令使用情景:

1.按钮级别权限的控制。

2.按钮的波纹动态效果。

3.一键copy的功能。

4.输入框自动聚焦。

5.下拉菜单,点击下拉菜单以外的地方时隐藏下拉菜单。

6.时间转换,比如朋友圈发布动态后的相对时间,比如刚刚、两分钟前等等。

7.输入框的最小值和最大值限制。

一:自定义指令有全局注册指令和局部注册指令两种方式:

全局注册指令:

Vue.directive('focus',{

bind:function(){},

inserted:function(){},

update:function(){},

componentUpdated:function(){},

unbind:function(){}

});

局部注册指令:在.vue文件中使用directives属性:

directives:{

focus:{

bind:function(){},

inserted:function(){},

update:function(){},

componentUpdated:function(){},

unbind:function(){}

}

}

注册指令成功后,直接在dom元素上使用v-focus。

二:注册指令的使用。

下面以局部注册指令来举例子。

自定义指令有4个钩子函数,v-check-num="{key:'myNum',maxval:1000,minval:100}"

钩子函数需要用到的参数解析:

1.el:指令所绑定的元素。

2.binding:绑定对象。属性包含

name(指令名),不包括v-,此例子中为check-num。

value(计算后的指令所绑定的值),此例子中为{key:'myNum',maxval:1000,minval:100}。

oldValue(指令所绑定的前一个值,仅在update和componentUpdated中可用)。

express(绑定的值的字符串形式),此例子中为'{key:'myNum',maxval:1000,minval:100}'。

arg(传递给指令的参数),v-check-num:a中的arg为a。

modifiers(修饰符对象),v-my-directive.foo.bar中的modifiers为{foo:true,bar:true}。

3.vnode:编译生成的虚拟节点。属性有context为虚拟节点的上下文。

注意:el可读可写,其他参数只读。如果需要在钩子函数之间共享数据,可通过dataset来实现。

4.oldVnode:上一个虚拟节点。

directiveCom.vue:



 

在App.vue里面引用上面的指令组件:






效果图:

vue directives自定义指令的使用_第1张图片

页面渲染时,触发了bind和inserted函数。

点击按钮,效果图:

vue directives自定义指令的使用_第2张图片

此时,改变了directiveCom组件里的dom元素的css样式,触发了update和componentUpdated函数。 

将directiveCom中的v-show改为v-if:



 

点击按钮,效果如下:

vue directives自定义指令的使用_第3张图片

 v-if是dom组件的的销毁和创建,指令与元素解绑,此时触发了unbind函数。

三:进阶:使用自定义指令来设置输入框的最小值和最大值规则。规则:如果输入的值<最小值,那么默认为最小值;如果输入的值>最大值,那么默认为最大值;如果输入的是非数字,则清空输入框,默认为空。

directiveCom组件:



 

四:用自定义指令来实现几种需求场景:

git链接:https://github.com/xiaoli0510/vue-directive

directive.vue:



 

先上效果图:

vue directives自定义指令的使用_第4张图片

 checkNum、validBtn、focus、clickoutside是局部注册指令。

waves、copy、time是全局注册指定。

time.js:

var Time = {
    //获取当前时间戳
    getUnix: function () {
        var date = new Date();
        return date.getTime();
    },
    //获取今天0点0分0秒的时间戳
    getTodayUnix: function () {
        var date = new Date();
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);
        return date.getTime();
    },
    //获取今年1月1日0点0分0秒的时间戳
    getYearUnix: function () {
        var date = new Date();
        date.setMonth(0);
        date.setDate(1);
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);
        return date.getTime();
    },
    //获取标准年月日
    getLastDate: function (time) {
        var date = new Date(time);
        var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
        var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
        return date.getFullYear() + '-' + month + '-' + day;
    },
    //转换时间
    getFormatTime: function (timestamp) {
        var now = this.getUnix(); //当前时间戳
        var today = this.getTodayUnix(); //今天0点时间戳
        //var year = this.getYearUnix(); //今年0点时间戳
        var timer = (now - timestamp) / 1000; //转换为妙级别时间戳
        var tip = '';

        if (timer <= 0) {
            tip = '刚刚';
        } else if (Math.floor(timer / 60) <= 0) {
            tip = '刚刚';
        } else if (timer < 3600) {
            tip = Math.floor(timer / 60) + '分钟前';
        } else if (timer >= 3600 && (timestamp - today >= 0)) {
            tip = Math.floor(timer / 3600) + '小时前';
        } else if (timer / 86400 <= 31) {
            tip = Math.ceil(timer / 86400) + '天前';
        } else {
            tip = this.getLastDate(timestamp);
        }
        return tip;
    }
};

export default {
    bind: function (el, binding) {
        el.innerHTML = Time.getFormatTime(binding.value);
        el._timeout_ = setInterval(function () {
            el.innerHTML = Time.getFormatTime(binding.value);
        }, 1000);
    },
    unbind:function(el){
        clearInterval(el._timeout_);
        delete el._timeout_;
    }
}

copy.js:


let listenAction

export default {
  inserted(el, binding) {
    const params = binding.value || {}
    const stickyTop = params.stickyTop || 0
    const zIndex = params.zIndex || 1000
    const elStyle = el.style

    elStyle.position = '-webkit-sticky'
    elStyle.position = 'sticky'
    // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
    // if (~elStyle.position.indexOf('sticky')) {
    //     elStyle.top = `${stickyTop}px`;
    //     elStyle.zIndex = zIndex;
    //     return
    // }
    const elHeight = el.getBoundingClientRect().height
    const elWidth = el.getBoundingClientRect().width
    elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`

    const parentElm = el.parentNode || document.documentElement
    const placeholder = document.createElement('div')
    placeholder.style.display = 'none'
    placeholder.style.width = `${elWidth}px`
    placeholder.style.height = `${elHeight}px`
    parentElm.insertBefore(placeholder, el)

    let active = false

    const getScroll = (target, top) => {
      const prop = top ? 'pageYOffset' : 'pageXOffset'
      const method = top ? 'scrollTop' : 'scrollLeft'
      let ret = target[prop]
      if (typeof ret !== 'number') {
        ret = window.document.documentElement[method]
      }
      return ret
    }

    const sticky = () => {
      if (active) {
        return
      }
      if (!elStyle.height) {
        elStyle.height = `${el.offsetHeight}px`
      }

      elStyle.position = 'fixed'
      elStyle.width = `${elWidth}px`
      placeholder.style.display = 'inline-block'
      active = true
    }

    const reset = () => {
      if (!active) {
        return
      }

      elStyle.position = ''
      placeholder.style.display = 'none'
      active = false
    }

    const check = () => {
      const scrollTop = getScroll(window, true)
      const offsetTop = el.getBoundingClientRect().top
      if (offsetTop < stickyTop) {
        sticky()
      } else {
        if (scrollTop < elHeight + stickyTop) {
          reset()
        }
      }
    }
    listenAction = () => {
      check()
    }

    window.addEventListener('scroll', listenAction)
  },

  unbind() {
    window.removeEventListener('scroll', listenAction)
  }
}

waves.js:

import './waves.css'

const context = '@@wavesContext'

function handleClick(el, binding) {
  function handle(e) {
    const customOpts = Object.assign({}, binding.value)
    const opts = Object.assign({
      ele: el, // 波纹作用元素
      type: 'hit', // hit 点击位置扩散 center中心点扩展
      color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
    },
    customOpts
    )
    const target = opts.ele
    if (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'
      }
      switch (opts.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 ||
              document.body.scrollTop) + 'px'
          ripple.style.left =
            (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft ||
              document.body.scrollLeft) + 'px'
      }
      ripple.style.backgroundColor = opts.color
      ripple.className = 'waves-ripple z-active'
      return false
    }
  }

  if (!el[context]) {
    el[context] = {
      removeHandle: handle
    }
  } else {
    el[context].removeHandle = handle
  }

  return handle
}

export default {
  bind(el, binding) {
    el.addEventListener('click', handleClick(el, binding), false)
  },
  update(el, binding) {
    el.removeEventListener('click', el[context].removeHandle, false)
    el.addEventListener('click', handleClick(el, binding), false)
  },
  unbind(el) {
    el.removeEventListener('click', el[context].removeHandle, false)
    el[context] = null
    delete el[context]
  }
}

waves.css:

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

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

在main.js中引入js文件,再进行全局注册:

main.js:

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

import waves from './directive/waves/waves.js'
Vue.directive('waves', waves)

import copy from './directive/copy.js'
Vue.directive('copy', copy)

import time from './directive/time.js'
Vue.directive('time', time)




new Vue({
  render: h => h(App),
}).$mount('#app')

你可能感兴趣的:(vue,vue.js)