常見自定義指令
一、回應縮放指令
- 使用
<div v-resize="resize"></div>
- 代碼
/**
* 回應縮放指令
* @direction 使用該指令可以回應元素寬高改變時執行的方法,
* @resize {function} 傳入對應resize方法
* v-resize="resize"
**/
export default {
bind(el, binding) {
let width = '', height = '';
function isResize() {
const style = document.defaultView.getComputedStyle(el);
if (width !== style.width || height !== style.height) {
binding.value(); // 執行傳入的方法
}
width = style.width;
height = style.height;
}
el.__timer__ = setInterval(isResize, 300); // 周期性監聽元素是否改變
},
unbind(el) {
clearInterval(el.__timer__);
}
}
二、元素點擊范圍擴展指令
- 使用
<div v-expandClick="20,30,40,50"></div>
- 代碼
/**
* 元素點擊范圍擴展指令
* @direction 使用該指令可以隱式的擴展元素的點擊范圍,由于借用偽元素實作,故不會影響元素在頁面上的排列布局,法,
* @top {string} 上擴展的范圍,單位 px,默認向外擴展 10px
* @right {string} 右擴展的范圍,單位 px,默認向外擴展 10px
* @bottom {string} 下擴展的范圍,單位 px,默認向外擴展 10px
* @left {string} 左擴展的范圍,單位 px,默認向外擴展 10px
* v-expandClick="top, right, bottom, left" (10, 10, 10, 10)
**/
export default {
bind(el, binding) {
const s = document.styleSheets[document.styleSheets.length - 1]
const DEFAULT = -10 // 默認向外擴展10px
const ruleStr = `content:"";position:absolute;top:-${top || DEFAULT}px;bottom:-${bottom || DEFAULT}px;right:-${right || DEFAULT}px;left:-${left || DEFAULT}px;`
const [top, right, bottom, left] = binding.expression && binding.expression.split(',') || []
const classNameList = el.className.split(' ')
el.className = classNameList.includes('expand_click_range') ? classNameList.join(' ') : [...classNameList, 'expand_click_range'].join(' ')
el.style.position = el.style.position || 'relative'
if (s.insertRule) {
s.insertRule('.expand_click_range::before' + '{' + ruleStr + '}', s.cssRules.length)
} else { /* IE */
s.addRule('.expand_click_range::before', ruleStr, -1)
}
}
}
三、文本內容復制指令
- 使用
<div v-copy> 單擊復制 </div>
<div v-copy.dblclick> 雙擊復制 </div>
<div v-copy.icon> icon復制 </div>
- 代碼
/**
* 文本內容復制指令
* @direction 使用該指令可以復制元素的文本內容(指令支持單擊復制 v-copy、雙擊復制 v-copy.dblclick、點擊icon復制 v-copy.icon三種模式),不傳引數時,默認使用單擊復制,
* @dblclick {string} dblclick 雙擊復制文本內容
* @icon {string} icon 單擊icon復制文本內容
* v-copy 單擊復制
* v-copy.dblclick 雙擊復制
* v-copy.icon icon復制
**/
export default {
bind(el, binding) {
// 雙擊觸發復制
if (binding.modifiers.dblclick) {
el.addEventListener('dblclick', () => handleClick(el.innerText))
el.style.cursor = 'copy'
} else if (binding.modifiers.icon) { // 點擊icon觸發復制
if (el.hasIcon) return
const iconElement = document.createElement('i')
iconElement.setAttribute('class', 'el-icon-document-copy')
iconElement.setAttribute('style', 'margin-left:5px')
el.appendChild(iconElement)
el.hasIcon = true
iconElement.addEventListener('click', () => handleClick(el.innerText))
iconElement.style.cursor = 'copy'
} else { // 單擊觸發復制
el.addEventListener('click', () => handleClick(el.innerText))
el.style.cursor = 'copy'
}
}
}
四、元素說明指令
- 使用
<div v-tooltip:content='tootipParams'> 提示 </div>
- 代碼
/**
* 元素說明指令
* @direction 為元素添加說明,同 element-ui 的el-tooltip
* @content {string} 傳給指令的引數
* @tooltipParams {Object} element-ui 支持的 tooltip屬性
* v-tooltip:content='tooltipParams'
**/
import Vue from 'vue'
function structureIcon(content, attrs) {
// 拼接系結屬性
let attrStr = ''
for (const key in attrs) {
attrStr += `${key}=${attrs[key]} `
}
const a = `<el-tooltip content=${content} ${attrStr}><i style="margin:0 10px"></i></el-tooltip>`
// 創建構造器
const tooltip = Vue.extend({
template: a
})
// 創建一個 tooltip 實體并回傳 dom 節點
const component = new tooltip().$mount()
return component.$el
}
export default {
bind(el, binding) {
if (el.hasIcon) return
const iconElement = structureIcon(binding.arg, binding.value)
el.appendChild(iconElement)
el.hasIcon = true
}
}
五、文字超出省略指令
- 使用
<div v-ellipsis:100> 需要省略的文字是阿薩的副本阿薩的副本阿薩的副本阿薩的副本</div>
- 代碼
/**
* 文字超出省略指令
* @direction 使用該指令當文字內容超出寬度,
* @width {number} 元素寬度
* v-ellipsis:width
**/
export default {
bind(el, binding) {
el.style.width = binding.arg || 100 + 'px'
el.style.whiteSpace = 'nowrap'
el.style.overflow = 'hidden';
el.style.textOverflow = 'ellipsis';
}
}
六、回到頂部指令
- 使用
<div v-backtop:app="400"> 回到頂部 </div>
- 代碼
/**
* 回到頂部指令
* @direction 使用該指令可以讓頁面或指定元素回到頂部,
* @id {string} 給需要回到頂部的元素添加的id
* @offset {number} 偏移距離為 height 時顯示指令系結的元素
* v-backtop:id="offset"
**/
export default {
bind(el, binding, vnode) {
// 回應點擊后滾動到元素頂部
el.addEventListener('click', () => {
const target = binding.arg ? document.getElementById(binding.arg) : window
target.scrollTo({
top: 0,
behavior: 'smooth'
})
})
},
update(el, binding, vnode) {
// 滾動到達引數值才出現系結指令的元素
const target = binding.arg ? document.getElementById(binding.arg) : window
if (binding.value) {
target.addEventListener('scroll', (e) => {
if (e.srcElement.scrollTop > binding.value) {
el.style.visibility = 'unset'
} else {
el.style.visibility = 'hidden'
}
})
}
// 判斷初始化狀態
if (target.scrollTop < binding.value) {
el.style.visibility = 'hidden'
}
},
unbind(el, binding) {
const target = binding.arg ? document.getElementById(binding.arg) : window
target.removeEventListener('scroll')
el.removeEventListener('click')
}
}
七、拖拽指令
- 使用
<div v-drag> 支持拖拽的元素 </div>
- 代碼
/**
* 拖拽指令
* @direction 使用該指令可以對元素進行拖拽,
* v-drag=“[width, height]”
**/
export default {
inserted(el, binding) {
let { value } = binding;
if (!value || value.length === 0) value = https://www.cnblogs.com/Mr-lil/archive/2023/06/07/[0, 0]
el.style.cursor ='move'
el.onmousedown = function(e) {
const disx = e.pageX - el.offsetLeft
const disy = e.pageY - el.offsetTop
document.onmousemove = function(e) {
let x = e.pageX - disx
let y = e.pageY - disy
const maxX = document.body.clientWidth - parseInt(window.getComputedStyle(el).width) - value[0]
const maxY = document.body.clientHeight - parseInt(window.getComputedStyle(el).height) - value[1]
if (x < 0) {
x = 0
} else if (x > maxX) {
x = maxX
}
if (y < 0) {
y = 0
} else if (y > maxY) {
y = maxY
}
el.style.left = x + 'px'
el.style.top = y + 'px'
}
document.onmouseup = function() {
document.onmousemove = document.onmouseup = null
}
}
}
}
八、長按指令
- 使用
<div v-longpress="longPress"></div>
- 代碼
/**
* 長按指令
* @direction 實作長按,用戶需要按下并按住按鈕幾秒鐘,觸發相應的事件
* @longPress {function} 長按的回呼
* v-longpress="longPress"
**/
export default {
bind: function(el, binding, vNode) {
if (typeof binding.value !== 'function') {
throw 'callback must be a function'
}
// 定義變數
let pressTimer = null
// 創建計時器( 2秒后執行函式 )
const start = (e) => {
if (e.type === 'click' && e.button !== 0) {
return
}
if (pressTimer === null) {
pressTimer = setTimeout(() => {
handler()
}, 2000)
}
}
// 取消計時器
const 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 = https://www.cnblogs.com/Mr-lil/archive/2023/06/07/value
},
// 指令與元素解綁的時候,移除事件系結
unbind(el) {
el.removeEventListener('click', el.handler)
}
}
九、防抖指令
- 使用
<el-button v-debounce="debounceClick"> 防抖 </el-button>
- 代碼
/**
* 防抖指令
* @direction 防止按鈕在短時間內被多次點擊,使用防抖函式限制規定時間內只能點擊一次,
* @debounceClick {function} 按鈕的點擊事件
* v-debounce="debounceClick"
**/
export default {
inserted(el, binding) {
let timer
el.addEventListener('keyup', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
binding.value()
}, 1000)
})
}
}
十、水印指令
- 使用
<div v-waterMarker="{text:'著作權所有',textColor:'rgba(180, 180, 180, 0.4)', font:'16px Microsoft JhengHei'}"></div>
- 代碼
/**
* 水印指令
* @direction 給整個頁面添加背景水印
* @data {Object} 水印文案,顏色,字體
* v-waterMarker="{text:'著作權所有',textColor:'rgba(180, 180, 180, 0.4)', font:'16px Microsoft JhengHei'}"
**/
function addWaterMarker(str, parentNode, font, textColor) {
// 水印文字,父元素,字體,文字顏色
const can = document.createElement('canvas')
parentNode.appendChild(can)
can.width = 200
can.height = 150
can.style.display = 'none'
const 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 {
bind(el, binding) {
addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor)
}
}
十一、懶加載指令select
- 使用
<el-select v-loadselect="loadmore"></el-select>
- 代碼
/**
* el-select懶加載指令
* @direction 下拉框資料較多,需要下拉的時候懶加載資料
* @load {function} 懶加載呼叫方法
* v-loadselect="load"
**/
export default {
inserted(el, binding) {
const selectWarpDom = el.querySelector(
'.el-select-dropdown .el-select-dropdown__wrap'
)
selectWarpDom.addEventListener('scroll', function() {
if (this.scrollHeight - this.scrollTop <= this.clientHeight) {
binding.value()
}
})
}
}
十二、徽標指令
- 使用
<div v-badge.dot.info="10"></div>
>
- 代碼
/**
* 徽標指令
* @direction 使用該指令在元素右上角顯示徽標,
* @shape {string} 形狀 [normal(正常徽標), dot(僅展示一個點)]
* @type {string} 徽標顏色型別 [success, error, info, warning]
* @num {number} 懶加載呼叫方法
* v-badge.shape.type="num"
**/
import Vue from 'vue'
const SUCCESS = '#72c140'
const ERROR = '#ed5b56'
const WARNING = '#f0af41'
const INFO = '#4091f7'
const HEIGHT = 20
let flag = false
export default {
update(el, binding, vnode) {
const { modifiers, value } = binding
const modifiersKey = Object.keys(modifiers)
const isDot = modifiersKey.includes('dot')
let backgroundColor = ''
if (modifiersKey.includes('success')) {
backgroundColor = SUCCESS
} else if (modifiersKey.includes('warning')) {
backgroundColor = WARNING
} else if (modifiersKey.includes('info')) {
backgroundColor = INFO
} else {
backgroundColor = ERROR
}
const targetTemplate = isDot
? `<div style="position:absolute;top:-5px;right:-5px;height:10px;width:10px;border-radius:50%;background:${backgroundColor}"></div>`
: `<div style="background:${backgroundColor};position:absolute;top:-${HEIGHT / 2}px;right:-${HEIGHT / 2}px;height:${HEIGHT}px;min-width:${HEIGHT}px;border-radius:${HEIGHT / 2}px;text-align:center;line-height:${HEIGHT}px;color:#fff;padding:0 5px;">${value}</div>`
el.style.position = el.style.position || 'relative'
const badge = Vue.extend({
template: targetTemplate
})
const component = new badge().$mount().$el
if (flag) {
el.removeChild(el.lastChild)
}
el.appendChild(component)
flag = true
}
}
十三、空狀態指令
- 使用
<div v-empty="emptyValue" style="height:500px;width:500px"> 原本內容 </div>
- 代碼
/**
* 空狀態指令
* @direction 使用該指令可以顯示預設的空狀態, 可以傳入默認圖片(可選,默認無圖片)、默認文字內容(可選,默認為暫無資料)、以及標示是否顯示空狀態(必選),
* @emptyValue {Object} 懶加載呼叫方法
* @content: '暫無串列', 可選
* @img: require('../../assets/images/xxx.png'), 可選
* @visible: 'true', 是否顯示. 必傳
* v-empty="emptyValue "
**/
import Vue from 'vue';
export default {
update(el, binding, vnode) {
el.style.position = el.style.position || 'relative'
const { offsetHeight, offsetWidth } = el
const { visible, content, img } = binding.value
const image = img ? `<img src="https://www.cnblogs.com/Mr-lil/archive/2023/06/07/${img}" height="30%" >` : ''
const defaultStyle = 'position:absolute;top:0;left:0;z-index:9999;background:#fff;display:flex;justify-content: center;align-items: center;'
const empty = Vue.extend({
template: `<div style="height:${offsetHeight}px;width:${offsetWidth}px;${defaultStyle}">
<div style="text-align:center">
<div>${image}</div>
<div>${content || '暫無資料'}</div>
</div>
</div>`
})
const component = new empty().$mount().$el
if (visible) {
el.appendChild(component)
} else {
el.removeChild(el.lastChild)
}
}
}
十四、輸入框聚焦指令
- 使用
<div v-focus></div>
- 代碼
/**
* 輸入框聚焦指令
* @direction 頁面加載時,輸入框自動聚焦,
* v-focus
**/
export default {
inserted(el) {
// 聚焦元素
el.focus()
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/554607.html
標籤:其他
上一篇:Vue3從入門到精通(一)
下一篇:返回列表
