vue封装之六个非常实用的Vue自定义指令

vue封装之六个非常实用的Vue自定义指令

    • 一 v-copy
    • 二 v-longpress
    • 三 v-debounce
    • 四 v-permission
    • 五 v-waterMarker
    • 六 v-loadMore
    • 七 源码集合:

一 v-copy

实现一键复制文本内容,用于鼠标右键粘贴。
思路:
1 动态创建 textarea 标签,并设置 readOnly 属性及移出可视区域
2 将要复制的值赋给 textarea 标签的 value 属性,并插入到 body
3 选中值 textarea 并复制
4 将 body 中插入的 textarea 移除
5 在第一次调用时绑定事件,在解绑时移除事件

创建一个textarea,让其不显示。点击动作的时候,先把值复制到textarea上,通过document.execCommand('Copy')执行了系统的复制功能

const copy = {
    install: function(Vue) {
        Vue.directive('copy',{
            bind(el, { value }) {
                el.$value = value
                el.handler = () => {
                    if (!el.$value) {
                        // 值为空的时候,给出提示。可根据项目UI仔细设计
                        console.log('无复制内容')
                        return
                    }
                    // 第一步:动态创建 textarea 标签
                    const textarea = document.createElement('textarea')
                    // 第一步:将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
                    textarea.readOnly = 'readonly'
                    textarea.style.position = 'absolute'
                    textarea.style.left = '-9999px'
                    // 第二步:将要 copy 的值赋给 textarea 标签的 value 属性
                    textarea.value = el.$value
                    // 第二步:将 textarea 插入到 body 中
                    document.body.appendChild(textarea)
                    // 第三步:选中值并复制
                    textarea.select()
                    const result = document.execCommand('Copy')
                    if (result) {
                        console.log('复制成功') // 可根据项目UI仔细设计
                    }
                    // 第四步: 将 body 中插入的 textarea 移除
                    document.body.removeChild(textarea)
                }
                // 绑定点击事件,就是所谓的一键 copy 啦
                el.addEventListener('click', el.handler)
            },
            // 第五步: 当传进来的值更新的时候触发
            componentUpdated(el, { value }) {
                el.$value = value
            },
            // 第五步: 指令与元素解绑的时候,移除事件绑定
            unbind(el) {
                el.removeEventListener('click', el.handler)
            },
        })
    }
}

export default copy

// main.js
import copy from './copy'
Vue.use(copy)

<template>
    <div id="app">
        <main>
            <button v-copy="copyText">复制button>
        main>
    div>
template>

<script>

    export default {
        data() {
            return {
                copyText: '粘贴指令',
            }
        },
    }
script>

二 v-longpress

需求:实现长按,用户需要按下并按住按钮几秒钟,触发相应的事件
思路:
1 创建一个计时器, 2 秒后执行函数
2 当用户按下按钮时触发 mousedown 事件,启动计时器;用户松开按钮时 调用 mouseout 事件。
3 如果 mouseup 事件 2 秒内被触发,就清除计时器,当作一个普通的点击事件
4 如果计时器没有在 2 秒内清除,则判定为一次长按,可以执行关联的函数。
5 在移动端要考虑 touchstart,touchend 事件

const longpress = {
    install: function(Vue) {
        Vue.directive('longpress',{
            bind: function (el, binding, vNode) {
                if (typeof binding.value !== 'function') {
                    throw 'callback must be a function'
                }
                // 定义变量
                let pressTimer = null
                // 创建计时器( 2秒后执行函数 )
                let start = (e) => {
                    if (e.type === 'click' && e.button !== 0) {
                        return
                    }
                    if (pressTimer === null) {
                        pressTimer = setTimeout(() => {
                            handler()
                        }, 2000)
                    }
                }
                // 取消计时器
                let cancel = (e) => {
                    if (pressTimer !== null) {
                        clearTimeout(pressTimer)
                        pressTimer = null
                    }
                }
                // 运行函数
                const handler = (e) => {
                    binding.value(e)
                }
                // 添加事件监听器
                el.addEventListener('mousedown', start)
                el.addEventListener('touchstart', start)
                // 取消计时器
                el.addEventListener('click', cancel)
                el.addEventListener('mouseout', cancel)
                el.addEventListener('touchend', cancel)
                el.addEventListener('touchcancel', cancel)
            },
            // 当传进来的值更新的时候触发
            componentUpdated(el, { value }) {
                el.$value = value
            },
            // 指令与元素解绑的时候,移除事件绑定
            unbind(el) {
                el.removeEventListener('click', el.handler)
            }
        })
    }
}

export default longpress


// main.js 
import longpress from './longpress'
Vue.use(longpress)
<template>
    <div id="app">
        <main>
            <button v-copy="copyText">复制button>
            <button v-longpress="longpress">长按button>
        main>
    div>
template>

<script>

    export default {
        data() {
            return {
                copyText: '粘贴指令',
            }
        },
        methods: {
            longpress () {
                alert('长按指令生效')
            }
        }
    }
script>

三 v-debounce

防止按钮在短时间内被多次点击,使用防抖函数限制规定时间内只能点击一次。

思路:
1 定义一个延迟执行的方法,如果在延迟时间内再调用该方法,则重新计算执行时间。
2 将时间绑定在 click 方法上。

const debounce = {
    install: function(Vue) {
        Vue.directive('debounce',{
            inserted: function (el, binding) {
                // 设置时长
                let s =  1000
                let timer
                el.addEventListener('click', () => {
                    if (timer) {
                        clearTimeout(timer)
                    }
                    timer = setTimeout(() => {
                        binding.value(s)
                    }, s)
                })
            }
        })
    }
}

export default debounce


// main.js
import debounce from './debounce'
Vue.use(debounce)
<template>
    <div id="app">
        <main>
            <button v-copy="copyText">复制button>
            <button v-longpress="longpress">长按button>
            <button v-debounce="debounceClick">测试防抖button>
        main>
    div>
template>

<script>

    export default {
        data() {
            return {
                copyText: '粘贴指令',
            }
        },
        methods: {
            longpress () {
                alert('长按指令生效')
            },
            debounceClick () {
                console.log('防抖指令生效')
            }
        }
    }
script>

四 v-permission

思路:
自定义一个权限数组
判断用户的权限是否在这个数组内,如果是则显示,否则则移除 Dom

const permission = {
    install: function(Vue) {
        Vue.directive('permission',{
            inserted: function (el, binding) {
                let permission = binding.value; // 获取到 v-permission的值
                if (permission) {
                    let hasPermission = checkArray(permission)
                    if (!hasPermission) {
                        // 没有权限 移除Dom元素
                        el.parentNode && el.parentNode.removeChild(el)
                    }
                }
            },
        })
    }
}


function checkArray(key) {
    let arr = ['1', '2', '3', '4']
    let index = arr.indexOf(key)
    if (index > -1) {
        return true // 有权限
    } else {
        return false // 无权限
    }
}

export default permission

// main.js
import permission from './permission'
Vue.use(permission)

<template>
    <div id="app">
        <main>
            <button v-copy="copyText">复制button>
            <button v-longpress="longpress">长按button>
            <button v-debounce="debounceClick">测试防抖button>

            
            <button v-permission="'1'">权限按钮1button>
            <button v-permission="'2'">权限按钮2button>
            <button v-permission="'3'">权限按钮3button>
            
            <button v-permission="'10'">权限按钮2button>
        main>
    div>
template>

<script>

    export default {
        data() {
            return {
                copyText: '粘贴指令',
            }
        },
        methods: {
            longpress () {
                alert('长按指令生效')
            },
            debounceClick () {
                console.log('防抖指令生效')
            }
        }
    }
script>

五 v-waterMarker

给整个页面添加背景水印。固定写法,粘贴就行

const waterMarker = {
    install: function(Vue) {
        Vue.directive('waterMarker',{
            bind: function (el, binding) {
                addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor)
            },
        })
    }
}


function addWaterMarker(str, parentNode, font, textColor) {
    // 水印文字,父元素,字体,文字颜色
    var can = document.createElement('canvas')
    parentNode.appendChild(can)
    can.width = 200
    can.height = 150
    can.style.display = 'none'
    var cans = can.getContext('2d')
    cans.rotate((-20 * Math.PI) / 180)
    cans.font = font || '16px Microsoft JhengHei'
    cans.fillStyle = textColor || 'rgba(180, 180, 180, 0.3)'
    cans.textAlign = 'left'
    cans.textBaseline = 'Middle'
    cans.fillText(str, can.width / 10, can.height / 2)
    parentNode.style.backgroundImage = 'url(' + can.toDataURL('image/png') + ')'
}

export default waterMarker


// main.js
import waterMarker from './waterMarker'
Vue.use(waterMarker)
<template>
    <div id="app">
        <main>
            <button v-copy="copyText">复制button>
            <button v-longpress="longpress">长按button>
            <button v-debounce="debounceClick">测试防抖button>

            
            <button v-permission="'1'">权限按钮1button>
            <button v-permission="'2'">权限按钮2button>
            <button v-permission="'3'">权限按钮3button>
            
            <button v-permission="'10'">权限按钮2button>




            <div style="height: 300px;width: 300px;" v-waterMarker="{text:'版权所有',textColor:'rgba(147,49,255,0.4)'}">div>
        main>
    div>
template>

<script>

    export default {
        data() {
            return {
                copyText: '粘贴指令',
            }
        },
        methods: {
            longpress () {
                alert('长按指令生效')
            },
            debounceClick () {
                console.log('防抖指令生效')
            }
        }
    }
script>

六 v-loadMore

需求:下拉框承载很多数据,需要下拉的时候缓加载数据
思路:
监听scroll滚动事件,当滚动到底部,再发请求。重复操作

const loadMore = {
    install: function(Vue) {
        Vue.directive('loadMore',{
            bind(el, binding) {
                //el.querySelector 返回与指定的选择器组匹配的元素的后代的第一个元素
                let select_dom = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap');
                select_dom.addEventListener('scroll', function () {
                    //监听scroll滚动事件
                    let height = this.scrollHeight - this.scrollTop <= this.clientHeight;
                    if (height) {
                        binding.value() //指令的绑定值
                    }
                })
            }
        })
    }
}


export default loadMore

// main.js
import loadMore from './loadMore'
Vue.use(loadMore)
<template>
    <div id="app">
        <main style="position: relative">
            <button v-copy="copyText">复制button>
            <button v-longpress="longpress">长按button>
            <button v-debounce="debounceClick">测试防抖button>

            
            <button v-permission="'1'">权限按钮1button>
            <button v-permission="'2'">权限按钮2button>
            <button v-permission="'3'">权限按钮3button>
            
            <button v-permission="'10'">权限按钮2button>




            <div style="height: 300px;width: 300px;" v-waterMarker="{text:'版权所有',textColor:'rgba(147,49,255,0.4)'}">div>







            
            
            
            <el-select
                    v-loadMore="loadMoreNameData"
            >
                <el-option
                        v-for="(item, index) in nameList"
                        :key="index"
                        :value="item.name"
                        :label="item.name"
                >
                    {{ item.name }}
                el-option>
            el-select>

        main>
    div>
template>

<script>

    export default {
        data() {
            return {
                copyText: '粘贴指令',
                nameList:[],
                total:'',
                pageIndex:1,
                pageSize:10
            }
        },
        methods: {
            longpress () {
                alert('长按指令生效')
            },
            debounceClick () {
                console.log('防抖指令生效')
            },
            loadMoreNameData(){
                //如果当前的list的长度与后端返回的所有数据即total相等,说明没有数据,不必再进行接口请求
                //该判断只能用于后端接口请求后返回的total是满足该条件的所有数据的数量
                if (this.nameList && this.nameList.length == this.total) {
                    return;
                }
                //如果不想等,说明还有下一页数据,即pageIndex页数++
                this.pageIndex++;
                this.pageSize = 10;
                //调用接口请求
                this.getList(this.pageIndex,this.pageSize);
            },
            getList(){

            }
        }
    }
script>

七 源码集合:

https://download.csdn.net/download/sugerfle/87312167

你可能感兴趣的:(vue封装专栏,vue.js,javascript,ecmascript)