7

Vue 常用自定义指令

 3 years ago
source link: http://www.fly63.com/article/detial/10506
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1、元素点击范围扩展指令 v-expandClick

使用该指令可以隐式的扩展元素的点击范围,由于借用伪元素实现,故不会影响元素在页面上的排列布局。

可传入的参数为:上右下左扩展的范围,单位 px,默认向外扩展 10px。指令的代码如下:

export default function (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)
    }
}

参数 Attributes:

参数说明默认值类型可选top, right, bottom, left上右下左扩展宽度(逗号分割),
单位px10,10,10,10String可填

然后你可以在模板中任何元素上使用新的 v-expandClick property,如下:

<div v-expandClick="20,30,40,50" @click="glabClickoutside"> 点击范围扩大</div>

2、文本内容复制指令 v-copy

使用该指令可以复制元素的文本内容(指令支持单击复制 v-copy、双击复制 v-copy.dblclick、点击icon复制 v-copy.icon 三种模式),不传参数时,默认使用单击复制。

指令的代码如下:

export default {
  bind (el, binding) {
    // 双击触发复制
    if (binding.modifiers.dblclick) {
      el.addEventListener('dblclick', () => handleClick(el.innerText))
      el.style.cursor = 'copy'
    }
    // 点击icon触发复制
    else if (binding.modifiers.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'
    }
  }
}

function handleClick (text) {
  // 创建元素
  if (!document.getElementById('copyTarget')) {
    const copyTarget = document.createElement('input')
    copyTarget.setAttribute('style', 'position:fixed;top:0;left:0;opacity:0;z-index:-1000;')
    copyTarget.setAttribute('id', 'copyTarget')
    document.body.appendChild(copyTarget)
  }

  // 复制内容
  const input = document.getElementById('copyTarget')
  input.value = text
  input.select()
  document.execCommand('copy')
  // alert('复制成功')
}

参数 Attributes:

参数说明默认值类型可选dblclick双击复制文本内容/String可选icon单击icon复制文本内容/String可选

然后你可以在模板中任何元素上使用新的 v-copy property,如下:

<div v-copy> 单击复制 </div>
<div v-copy.dblclick> 双击复制 </div>
<div v-copy.icon> icon复制 </div>

3、元素全屏指令 v-screenfull

全屏指令,点击元素进行全屏/退出全屏的操作。支持元素后面是否插入 element-ui 的全屏图标 el-icon-full-screen。

指令的代码如下:

import screenfull from 'screenfull'

export default {
  bind (el, binding) {
    if (binding.modifiers.icon) {
      if (el.hasIcon) return
      // 创建全屏图标
      const iconElement = document.createElement('i')
      iconElement.setAttribute('class', 'el-icon-full-screen')
      iconElement.setAttribute('style', 'margin-left:5px')
      el.appendChild(iconElement)
      el.hasIcon = true
  }
    el.style.cursor = el.style.cursor || 'pointer'
    // 监听点击全屏事件
    el.addEventListener('click', () => handleClick())
  }
}

function handleClick () {
  if (!screenfull.isEnabled) {
    alert('浏览器不支持全屏')
    return
  }
  screenfull.toggle()
}

参数 Attributes:

参数说明默认值类型可选icon是否添加 icon/String可选

然后你可以在模板中任何元素上使用新的 v-screenfull property,如下:

<div v-screenfull.icon> 全屏 </div>

4、元素说明指令 v-tooltip

为元素添加说明,如同 element-ui 的 el-tooltip(问号 icon 在鼠标覆盖后,展示说明文字)
60e84969d00a3.jpg
指令的代码如下:
import vue from 'vue'
export default function (el, binding) {
    if (el.hasIcon) return
    const iconElement = structureIcon(binding.arg, binding.value)
    el.appendChild(iconElement)
    el.hasIcon = true
}

function structureIcon (content, attrs) {
    // 拼接绑定属性
    let attrStr = ''
    for (let key in attrs) {
        attrStr += `${key}=${attrs[key]} `
    }
    const a = `<el-tooltip content=${content} ${attrStr}><i></i></el-tooltip>`
    // 创建构造器
    const tooltip = Vue.extend({
        template: a
    })
    // 创建一个 tooltip 实例并返回 dom 节点
    const component = new tooltip().$mount()
    return component.$el
}

参数 Attributes:

<div v-tooltip:content='tootipParams'> 提示 </div>
<div v-tooltip:提示内容为XXX1> 提示1</div>
<div v-tooltip:提示内容为XXX='tootipParams'> 提示2 </div>

为指令传入 element-ui 支持的参数:

data() {
    return {
        tootipParams: {
            placement: 'top',
            effect: 'light',
        }
    }
}

5、文字超出省略指令 v-ellipsis

使用该指令当文字内容超出宽度(默认 100 px)时自动变为省略形式。等同于使用 css
width: 100px;
whiteSpace: nowrap
overflow: hidden;
textOverflow: ellipsis;

使用指令效果:
60e849702e9c1.jpg
指令的代码如下:

export default function (el, binding) {
    el.style.width = binding.arg || 100 + 'px'
    el.style.whiteSpace = 'nowrap'
    el.style.overflow = 'hidden';
    el.style.textOverflow = 'ellipsis';
}

参数 Attributes:

参数说明默认值类型可选width元素宽度100Number必填

然后你可以在模板中任何元素上使用新的 v-ellipsis property,如下:

<div v-ellipsis:100> 需要省略的文字是阿萨的副本阿萨的副本阿萨的副本阿萨的副本</div>

6、回到顶部指令 v-backtop

使用该指令可以让页面或指定元素回到顶部。

可选指定元素,如果不指定则全局页面回到顶部。可选在元素偏移多少 px 后显示 backtop 元素,例如在滚动 400px 后显示回到顶部按钮。

60e84975bccf3.jpg
指令的代码如下:

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) {
    const target = binding.arg ? document.getElementById(binding.arg) : window
    target.removeEventListener('scroll')
    el.removeEventListener('click')
  }
}

参数 Attributes:

参数说明默认值类型可选id给需要回到顶部的元素添加的id/String可选offset偏移距离为 height 时显示指令绑定的元素/Number可选

然后你可以在模板中任何元素上使用新的 v-backtop property,如下表示在 id 为 app 的元素滚动 400px 后显示绑定指令的元素:

<div v-backtop:app="400"> 回到顶部 </div>

也可以这样使用,表示为一直显示绑定指令的元素,并且是全局页面回到顶部:

<div v-backtop> 回到顶部 </div>

7、空状态指令 v-empty

使用该指令可以显示缺省的空状态。可以传入默认图片(可选,默认无图片)、默认文字内容(可选,默认为暂无数据)、以及标示是否显示空状态(必选)。
60e8497d74218.jpg
指令的代码如下:
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="${img}" height="30%" width="30%"></img>` : ''
    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)
    }
  },
}

参数 Attributes:

参数说明默认值类型可选emptyValue包含文字内容 content、图片 img、是否显示 visible,仅 visible 必传/Object必须

然后你可以在模板中任

然后你可以在模板中任何元素上使用新的 v-empty property,如下传入对象 emptyValue:

<div style="height:500px;width:500px" v-empty="emptyValue"> 原本内容

需要传入一个参数对象,例如显示文字为:暂无列表,图片路径为 ../../assets/images/blue_big.png,控制标示 visible:

emptyValue = {
  content: '暂无列表',
  img: require('../../assets/images/blue_big.png'),
  visible: true,
},

8、徽标指令 v-badge

使用该指令在元素右上角显示徽标。

支持配置徽标的背景颜色、徽标形状;支持传入徽标上显示的数字。
60e84983c81d8.jpg
指令的代码如下:

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)
    let 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 
        ? `<divhljs-subst">${backgroundColor}"></div>` 
        : `<divhljs-subst">${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
  }
}

参数 Attributes:

参数说明默认值类型可选normal、dot徽标形状normal为正常徽标;dot 仅为一个点normalString可选success、error、info、warning徽标颜色errorString可选number徽标上显示的数字/Number可选

然后你可以在模板中任何元素上使用新的 v-badge property,如下:

<div v-badge.dot.info="badgeCount" style="height:50px;width:50px;background:#999"> </div>

9、拖拽指令 v-drag

使用该指令可以对元素进行拖拽。

指令的代码如下:

export default {
  let _el = el
  document.onselectstart = function() {
    return false  //禁止选择网页上的文字
  }
  
  _el.onmousedown = e => {
    let disX = e.clientX - _el.offsetLeft //鼠标按下,计算当前元素距离可视区的距离
    let disY = e.clientY - _el.offsetTop
    document.onmousemove = function(e){     
      let l = e.clientX - disX
      let t = e.clientY - disY;
      _el.style.left = l + "px"
      _el.style.top = t + "px"
    }
    document.onmouseup = e => {
      document.onmousemove = document.onmouseup = null
    }
    return false
  }
}

然后你可以在模板中任何元素上使用新的 v-drag property,如下:

<div v-drag> 支持拖拽的元素 </div>

10、响应缩放指令 v-resize

使用该指令可以响应元素宽高改变时执行的方法。

指令的代码如下:

export default {
  bind(el, binding) {
    let width = '', height = '';
    function isReize() {
      const style = document.defaultView.getComputedStyle(el);
      if (width !== style.width || height !== style.height) {
        binding.value();  // 执行传入的方法
      }
      width = style.width;
      height = style.height;
     }
     el.__timer__ = setInterval(isReize, 300); // 周期性监听元素是否改变
  },
  unbind(el) {
    clearInterval(el.__timer__);
  }
}

参数 Attributes:

参数说明默认值类型可选resize()传入元素改变 size 后执行的方法/Function必选

然后你可以在模板中任何元素上使用新的 v-resize property,如下:

// 传入 resize() 方法
<div v-resize="resize"></div>

11、字符串整形指令 v-format

使用该指令可以修改字符串,如使用 v-format.toFixed 保留两位小数、 v-format.price 将内容变成金额(每三位逗号分隔),可以同时使用,如 v-format.toFixed.price。

例如将数字 243112.331 变成 243112.33,或 243,112.33。

指令的代码如下:

export default {
  update (el, binding, vnode) {
    const { value, modifiers } = binding
    if (!value) return
    let formatValue = value
    if (modifiers.toFixed) {
      formatValue = value.toFixed(2)
    }
    console.log(formatValue)
    if (modifiers.price) {
      formatValue = formatNumber(formatValue)
    }
    el.innerText = formatValue
  },
}



function formatNumber (num) {
  num += '';
  let strs = num.split('.');
  let x1 = strs[0];
  let x2 = strs.length > 1 ? '.' + strs[1] : '';
  var rgx = /(\d+)(\d{3})/;
  while (rgx.test(x1)) {
    x1 = x1.replace(rgx, '$1' + ',' + '$2');
  }
  return x1 + x2
}

参数 Attributes:

参数说明默认值类型可选toFixed保留两位小数/String可选price整形成金额(三位逗号分隔)/String可选

然后你可以在模板中任何元素上使用新的 v-format property,如下:

<div v-format.toFixed.price="123333"> 123 </div>

如何使用这些指令?

为了便于管理指令,我们将每个指令都存在于单独的 js 文件中。在项目的 src 下建一个 directives 目录,目录下新建 index.js 文件用于引入并注册指令。

├── src
|  ├── directive
|  |  ├── index.js
|  |  ├── backtop.js
|  |  ├── badge.js
|  |  ├── copy.js
|  |  ├── ellipsis.js
|  |  ├── empty.js
|  |  ├── expandClick.js
|  |  ├── screenfull.js
|  |  └── tooltips.js
|  ├── main.js

举个例子:

directives 目录下新建 ellipsis.js 文件:

export default function (el, binding) {
    el.style.width = binding.arg || 100 + 'px'
    el.style.whiteSpace = 'nowrap'
    el.style.overflow = 'hidden';
    el.style.textOverflow = 'ellipsis';
}

directives 的 index.js 文件中引入 ellipsis 指令并注册:

import Vue from 'vue'
import ellipsis from './ellipsis' // 引入指令
// import other directives

const directives = {
  ellipsis
  // other directives
}

Object.keys(directives).forEach(name => Vue.directive(name, directives[name]))

最后在 mian.js 中引入 index.js 文件:

import '@/directives/index'

这样就可以正常使用这些指令了:

import '@/directives/index'

作者:Huup_We
链接:https://juejin.cn/post/6963840401899782175
来源:掘金

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: http://www.fly63.com/article/detial/10506


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK