vue封装之六个非常实用的Vue自定义指令
一 v-copy二 v-longpress三 v-debounce四 v-permission五 v-waterMarker六 v-loadMore。vue封装之六个非常实用的Vue自定义指令
vue封装之六个非常实用的Vue自定义指令
一 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'">权限按钮1</button>
<button v-permission="'2'">权限按钮2</button>
<button v-permission="'3'">权限按钮3</button>
<!-- 不显示 -->
<button v-permission="'10'">权限按钮2</button>
</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'">权限按钮1</button>
<button v-permission="'2'">权限按钮2</button>
<button v-permission="'3'">权限按钮3</button>
<!-- 不显示 -->
<button v-permission="'10'">权限按钮2</button>
<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'">权限按钮1</button>
<button v-permission="'2'">权限按钮2</button>
<button v-permission="'3'">权限按钮3</button>
<!-- 不显示 -->
<button v-permission="'10'">权限按钮2</button>
<div style="height: 300px;width: 300px;" v-waterMarker="{text:'版权所有',textColor:'rgba(147,49,255,0.4)'}"></div>
<!-- <div style="height: 500px;width: 500px;background-color: red;position: absolute;right: 10px;bottom: 10px">-->
<!-- <div v-draggable style="height: 100px;width: 100px;background-color: #9331ff;position: relative">-->
<!-- </div>-->
<!-- </div>-->
<!-- 演示代码 -->
<!-- 一般与filter一起用 -->
<!-- 参考: https://blog.csdn.net/Subuprogrammer/article/details/126572380 -->
<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
更多推荐
所有评论(0)