vue3自定义指令之复制指令、拖拽指令、按钮长按指令

1.复制指令

主要用到 document.execCommand('Copy')这个api

app.directive('copy', {
    beforeMount(el, binding) {
    el.targetContent = binding.value;
    el.addEventListener('click', () => {
      if (!el.targetContent) return console.warn('没有需要复制的目标内容');
      // 创建textarea标签
      const textarea = document.createElement('textarea');
      // 设置相关属性
      textarea.readOnly = 'readonly';
      textarea.style.position = 'fixed';
      textarea.style.top = '-99999px';
      // 把目标内容赋值给它的value属性
      textarea.value = el.targetContent;
      // 插入到页面
      document.body.appendChild(textarea);
      // 调用onselect()方法
      textarea.select();

      const success = binding.arg;
      // 把目标内容复制进剪贴板, 该API会返回一个Boolean
      const res = document.execCommand('Copy');
      res && success ? success(el.targetContent) : ''
      // 移除textarea标签
      document.body.removeChild(textarea);
    })
  },
  updated(el, binding) {
    // 实时更新最新的目标内容
    el.targetContent = binding.value;
  },
  unmounted(el) {
    el.removeEventListener('click', () => { })
  }

})

 在组件中使用:v-copy="复制的数据"

<template>
  <div v-copy="copyvalue">点击复制</div>
</template>

<script setup>
import { ref } from 'vue'
const copyvalue = ref('点击复制哈哈哈 ')
</script>

如果复制之后还有其他操作,需要个回调方法做其他事情:v-copy:[回调函数]="要复制的数据"

<template>
  <div v-copy:[success]="copyvalue">点击复制</div>
</template>

<script setup>
import { ref } from 'vue'
const copyvalue = ref('点击复制哈哈哈 ')
const success = (val) => {
  console.log('内容是', val)
}
</script>

2.拖拽指令

让一个盒子限制宽高的父级元素内移动,没有限制的时候是相对于屏幕上的可视区内移动的。

app.directive("dragable",{
  mounted(el, binding) {
    // 设置目标元素基础属性
    el.style.cursor = 'move';
    el.style.position = 'fixed';
    // 获取容器宽高
    const containerId = binding.arg || null;
    let containerWidth = window.innerWidth- getScrollWidth();
    let containerHeight = window.innerHeight;
    // 存在父级容器
    if (containerId) {
      const containerEle = document.getElementById(containerId);
      let { width, height } = containerEle.getBoundingClientRect();
      containerWidth = width;
      containerHeight = height;
      if (!['fixed', 'absolute', 'relative'].includes(getStyle(containerEle, 'position'))) {
        containerEle.style.position = 'relative';
      }
      el.style.position = 'absolute';
    }

    // 鼠标在目标元素上按下        
    el.addEventListener('mousedown', (e) => {
      let { width, height } = el.getBoundingClientRect();
      // 当前目标元素的left与top
      const left = el.offsetLeft;
      const top = el.offsetTop;
      // 保存按下的鼠标的X与Y
      const mouseX = e.clientX;
      const mouseY = e.clientY;
      // 计算边界值
      const leftLimit = left;
      const rightLimit = containerWidth - left - width;
      const topLimit = top;
      const bottomLimit = containerHeight - top - height;

      // 监听鼠标移动
      document.onmousemove = (e) => {
        // 鼠标移动的距离
        let disX = e.clientX - mouseX;
        let disY = e.clientY - mouseY;
        // 左右边界
        if (disX < 0 && disX <= -leftLimit) {
          el.style.left = (left - leftLimit) + 'px';
        } else if (disX > 0 && disX >= rightLimit) {
          el.style.left = (left + rightLimit) + 'px';
        } else {
          el.style.left = (left + disX) + 'px';
        }
        // 上下边界
        if (disY < 0 && disY <= -topLimit) {
          el.style.top = (top - topLimit) + 'px';
        } else if (disY > 0 && disY >= bottomLimit) {
          el.style.top = (top + bottomLimit) + 'px';
        } else {
          el.style.top = (top + disY) + 'px';
        }
        return false;
      }

      // 监听鼠标抬起
      document.onmouseup = () => {
        document.onmousemove = null;
        document.onmouseup = null;
      }



    });

    // 获取元素的相关CSS
    function getStyle(el, attr) {
      return el.currentStyle ? el.currentStyle[attr] : window.getComputedStyle(el, false)[attr];
    }

    // 返回滚动条的宽度, 没有则返回0
    function getScrollWidth() {
      let noScroll, scroll, oDiv = document.createElement("DIV");
      oDiv.style.cssText = "position:absolute; top:-1000px; width:100px; height:100px; overflow:hidden;";
      noScroll = document.body.appendChild(oDiv).clientWidth;
      oDiv.style.overflowY = "scroll";
      scroll = oDiv.clientWidth;
      document.body.removeChild(oDiv);
      let isExsit = document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight);
      return isExsit ? noScroll - scroll : 0
    }
  }

})

 使用:给父级唯一个 id 值,通过 v-draggable:id属性,传递进去。或者 直接v-draggable则是以浏览器为准

  <div id="dragbox" style="width: 500px;height:500px;border:1px solid red;margin: 100px auto">
    <div v-dragable:dragbox style="width:100px;height:100px;background:green;"></div>
  </div>

3.按钮长按指令

点击一个按钮,不松开达到一定的时间才触发 ,移动端,pc端均可使用:

app.directive('longhandle', {

  beforeMount(el, binding) {
    const cb = binding.value;
    el.$duration = binding.arg || 3000; // 获取长按时长, 默认3秒执行长按事件
    if (typeof cb !== 'function') return console.warn('v-longpress指令必须接收一个回调函数');
    let timer = null;
    const add = (e) => {
      // 排除点击与右键情况, event.button: 0-左键  2-右键
      if (e.type === 'click' && e.button !== 0) return;
      e.preventDefault();
      if (timer === null) {
        timer = setTimeout(() => {
          cb();
          timer = null;
        }, el.$duration)
      }
    }
    const cancel = () => {
      if (timer !== null) {
        clearTimeout(timer);
        timer = null;
      }
    }

    // 添加计时器
    el.addEventListener('mousedown', add);
    el.addEventListener('touchstart', add);
    // 取消计时器
    el.addEventListener('click', cancel);
    el.addEventListener('mouseout', cancel);
    el.addEventListener('touchend', cancel)
    el.addEventListener('touchcancel', cancel)
  },
  updated(el, binding) {
    // 可以实时更新时长
    el.$duration = binding.arg;
  },
  unmounted(el) {
    el.removeEventListener('mousedown', () => { });
    el.removeEventListener('touchstart', () => { });
    el.removeEventListener('click', () => { });
    el.removeEventListener('mouseout', () => { });
    el.removeEventListener('touchend', () => { });
    el.removeEventListener('touchcancel', () => { });
  }
})

使用:v-longhandle:[毫秒时间]="回调函数"

<template>
   <button v-longhandle:[2000]="longpress">按钮</button>
</template>

<script setup>
const longpress = () => {
  console.log('时间')
}
</script>

一般项目中自定义指令比较多,都是放在一个文件里面,然后统一在main.js中注册

utils下的directive.js文件:

export const copy = {
  beforeMount(el, binding) {
    el.targetContent = binding.value;
    el.addEventListener('click', () => {
      if (!el.targetContent) return console.warn('没有需要复制的目标内容');
      // 创建textarea标签
      const textarea = document.createElement('textarea');
      // 设置相关属性
      textarea.readOnly = 'readonly';
      textarea.style.position = 'fixed';
      textarea.style.top = '-99999px';
      // 把目标内容赋值给它的value属性
      textarea.value = el.targetContent;
      // 插入到页面
      document.body.appendChild(textarea);
      // 调用onselect()方法
      textarea.select();

      const success = binding.arg;
      // 把目标内容复制进剪贴板, 该API会返回一个Boolean
      const res = document.execCommand('Copy');
      res && success ? success(el.targetContent) : ''
      // 移除textarea标签
      document.body.removeChild(textarea);
    })
  },
  updated(el, binding) {
    // 实时更新最新的目标内容
    el.targetContent = binding.value;
  },
  unmounted(el) {
    el.removeEventListener('click', () => { })
  }
}

export const longhandle = {
  beforeMount(el, binding) {
    const cb = binding.value;
    el.$duration = binding.arg || 3000; // 获取长按时长, 默认3秒执行长按事件
    if (typeof cb !== 'function') return console.warn('v-longpress指令必须接收一个回调函数');
    let timer = null;
    const add = (e) => {
      // 排除点击与右键情况, event.button: 0-左键  2-右键
      if (e.type === 'click' && e.button !== 0) return;
      e.preventDefault();
      if (timer === null) {
        timer = setTimeout(() => {
          cb();
          timer = null;
        }, el.$duration)
      }
    }
    const cancel = () => {
      if (timer !== null) {
        clearTimeout(timer);
        timer = null;
      }
    }

    // 添加计时器
    el.addEventListener('mousedown', add);
    el.addEventListener('touchstart', add);
    // 取消计时器
    el.addEventListener('click', cancel);
    el.addEventListener('mouseout', cancel);
    el.addEventListener('touchend', cancel)
    el.addEventListener('touchcancel', cancel)
  },
  updated(el, binding) {
    // 可以实时更新时长
    el.$duration = binding.arg;
  },
  unmounted(el) {
    el.removeEventListener('mousedown', () => { });
    el.removeEventListener('touchstart', () => { });
    el.removeEventListener('click', () => { });
    el.removeEventListener('mouseout', () => { });
    el.removeEventListener('touchend', () => { });
    el.removeEventListener('touchcancel', () => { });
  }
}

export const dragable = {
  mounted(el, binding) {
    // 设置目标元素基础属性
    el.style.cursor = 'move';
    el.style.position = 'fixed';
    // 获取容器宽高
    const containerId = binding.arg || null;
    let containerWidth = window.innerWidth- getScrollWidth();
    let containerHeight = window.innerHeight;
    // 存在父级容器
    if (containerId) {
      const containerEle = document.getElementById(containerId);
      let { width, height } = containerEle.getBoundingClientRect();
      containerWidth = width;
      containerHeight = height;
      if (!['fixed', 'absolute', 'relative'].includes(getStyle(containerEle, 'position'))) {
        containerEle.style.position = 'relative';
      }
      el.style.position = 'absolute';
    }

    // 鼠标在目标元素上按下        
    el.addEventListener('mousedown', (e) => {
      let { width, height } = el.getBoundingClientRect();
      // 当前目标元素的left与top
      const left = el.offsetLeft;
      const top = el.offsetTop;
      // 保存按下的鼠标的X与Y
      const mouseX = e.clientX;
      const mouseY = e.clientY;
      // 计算边界值
      const leftLimit = left;
      const rightLimit = containerWidth - left - width;
      const topLimit = top;
      const bottomLimit = containerHeight - top - height;

      // 监听鼠标移动
      document.onmousemove = (e) => {
        // 鼠标移动的距离
        let disX = e.clientX - mouseX;
        let disY = e.clientY - mouseY;
        // 左右边界
        if (disX < 0 && disX <= -leftLimit) {
          el.style.left = (left - leftLimit) + 'px';
        } else if (disX > 0 && disX >= rightLimit) {
          el.style.left = (left + rightLimit) + 'px';
        } else {
          el.style.left = (left + disX) + 'px';
        }
        // 上下边界
        if (disY < 0 && disY <= -topLimit) {
          el.style.top = (top - topLimit) + 'px';
        } else if (disY > 0 && disY >= bottomLimit) {
          el.style.top = (top + bottomLimit) + 'px';
        } else {
          el.style.top = (top + disY) + 'px';
        }
        return false;
      }

      // 监听鼠标抬起
      document.onmouseup = () => {
        document.onmousemove = null;
        document.onmouseup = null;
      }



    });

    // 获取元素的相关CSS
    function getStyle(el, attr) {
      return el.currentStyle ? el.currentStyle[attr] : window.getComputedStyle(el, false)[attr];
    }

    // 返回滚动条的宽度, 没有则返回0
    function getScrollWidth() {
      let noScroll, scroll, oDiv = document.createElement("DIV");
      oDiv.style.cssText = "position:absolute; top:-1000px; width:100px; height:100px; overflow:hidden;";
      noScroll = document.body.appendChild(oDiv).clientWidth;
      oDiv.style.overflowY = "scroll";
      scroll = oDiv.clientWidth;
      document.body.removeChild(oDiv);
      let isExsit = document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight);
      return isExsit ? noScroll - scroll : 0
    }
  }
}

 main.js中注册

import { createApp } from 'vue'
import App from './App.vue'
import * as dictive from './utils/directive'

const app = createApp(App)

//注册
let dictValue= Object.values(dictive)
Object.keys(dictive).forEach((item,i)=>{
    app.directive(item,dictValue[i])
})

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Vue 3中的自定义指令可以用于操作DOM元素,包括复制元素。在Vue 3中,我们可以通过调用`app.directive`来创建一个自定义指令。下面是一个例子,展示如何实现复制功能的自定义指令。 ```javascript // 在Vue 3中创建一个自定义指令 app.directive('copy', { mounted(el, binding) { // 绑定指令时执行的逻辑 el.addEventListener('click', () => { // 复制元素的内容到剪贴板 navigator.clipboard.writeText(el.textContent) .then(() => { // 成功复制的处理逻辑 console.log('Copied to clipboard!'); }) .catch(err => { // 复制失败的处理逻辑 console.error('Failed to copy text: ', err); }); }); } }) ``` 在上面的例子中,我们定义了一个名为`copy`的自定义指令,它会在元素被点击时将其内容复制到剪贴板。在`mounted`钩子函数中,我们为元素添加了一个点击事件监听器。当元素被点击时,我们使用`navigator.clipboard.writeText`方法将元素的内容写入剪贴板。如果复制成功,我们会在控制台打印一条成功消息,否则会打印一个错误消息。 要在模板中使用自定义指令,只需要将其绑定在需要添加指令的元素上。在下面的例子中,我们将自定义指令`copy`绑定在一个元素上。 ```html <template> <div> <p v-copy>This text will be copied when clicked.</p> </div> </template> ``` 在这个例子中,当这个`<p>`元素被点击时,它的内容将被复制到剪贴板。这是用Vue 3实现复制功能的简单例子,你可以根据自己的需求进行自定义指令的定义和实现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值