Vue自定义指令 /弹框拖拽 / 函数防抖 /文本高度溢出显示...

455 阅读2分钟
函数防抖
/*

*  使用方法

*  将以下代码复制到一个js文件中,然后在入口文件main.js中import引入即可;

*  给elementUI的dialog上加上 v-dialogDrag 指令就可以实现弹窗的全屏和拉伸了。

*  给dialog设置 :close-on-click-modal="false" , 禁止点击遮罩层关闭弹出层

*  如果是form表单,不要将提交等按钮放置el-form-item,以免在上下拉伸时被隐藏

*/
import Vue from 'vue'
// 函数防抖 2秒内只能点击一次 点完按钮会被禁用  v-noMoreClick
Vue.directive('noMoreClick', {
    inserted (el, binding) {
        el.addEventListener('click', e => {
            el.classList.add('is-disabled')
            el.disabled = true
            setTimeout(() => {
                el.disabled = false
                el.classList.remove('is-disabled')
            }, 2000)
        })
    }
})

弹窗拖拽
// v-dialogDrag: 弹窗拖拽
Vue.directive('dialogDrag', {
    bind (el, binding, vnode, oldVnode) {
        const dialogHeaderEl = el.querySelector('.el-dialog__header')
        const dragDom = el.querySelector('.el-dialog')
        dialogHeaderEl.style.cursor = 'move'

        // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
        const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)

        dialogHeaderEl.onmousedown = (e) => {
            // 鼠标按下,计算当前元素距离可视区的距离
            const disX = e.clientX - dialogHeaderEl.offsetLeft
            const disY = e.clientY - dialogHeaderEl.offsetTop

            // 获取到的值带px 正则匹配替换
            let styL, styT

            // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
            if (sty.left.includes('%')) {
                styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100)
                styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100)
            } else {
                styL = +sty.left.replace(/\px/g, '')
                styT = +sty.top.replace(/\px/g, '')
            };

            document.onmousemove = function (e) {
                // 通过事件委托,计算移动的距离
                const l = e.clientX - disX
                const t = e.clientY - disY

                // 移动当前元素
                dragDom.style.left = `${l + styL}px`
                dragDom.style.top = `${t + styT}px`

                // 将此时的位置传出去
                // binding.value({x:e.pageX,y:e.pageY})
            }

            document.onmouseup = function (e) {
                document.onmousemove = null
                document.onmouseup = null
            }
        }
    }
})

溢出省略指令
// 注册文本高度溢出省略指令
Vue.directive("textOverflow", (el) => {
    Vue.nextTick(() => {
        let text = el.innerHTML;
        // 遍历元素的innerHTML内容,当元素的offsetHeight小于scrollHeight(超出元素的高度溢出换行)时,
        // 设置溢出隐藏(overflow = "hidden")
        // 将末尾的三个文字用...取代,同时跳出for循环
        if (!text || !text.length) return;
        for (let i = 0; i <= text.length; i++) {
            el.innerHTML = text.substring(0, i);
            if (el.offsetHeight < el.scrollHeight) {
                el.style.overflow = "hidden";
                // 截取当前字符串0-倒数第三的区间位置的字符并加上"...",然后赋值给元素的innerHTML
                el.innerHTML = text.substring(0, i - 3) + "...";
                break;
            }
        }
    });
});
文本拷贝指令
// 文本拷贝指令 v-copy='需要拷贝的数据'

<template> <button v-copy="copyText">复制</button> </template>

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仔细设计
                }
                document.body.removeChild(textarea)
            }
            // 绑定点击事件,就是所谓的一键 copy 啦
            el.addEventListener('click', el.handler)
        },
        // 当传进来的值更新的时候触发
        componentUpdated (el, { value }) {
            el.$value = value
        },
        // 指令与元素解绑的时候,移除事件绑定
        unbind (el) {
            el.removeEventListener('click', el.handler)
        }
    });
图片懒加载
const vLazy: Directive<HTMLImageElement, string> = async (el, binding) => {
	// 默认展示图片
	const defaultUrl = await import('../../assets/logo-mini.svg');
	el.src = defaultUrl.default;
	// IntersectionObserver 方法可以监听元素是否在可视区域
	const observer = new IntersectionObserver((enr: any) => {
		if (enr[0].intersectionRatio > 0) {
			setTimeout(() => {
				el.src = binding.value;
			}, 1000);
			observer.unobserve(el);
		}
	});
	observer.observe(el);
};