Vue 自定义 拉伸指令


前言

在前端开发中,常常需要动态控制组件的长宽。其中拖拽控制是一个控制元素长宽很好的方式。本文将很好的介绍,拖拽控制元素大小的方式。


一、拉伸样式

元素拉伸的有八个方向,上、右上、右、右下、下、左下、左、左上,如下图所示
拉伸方向
因此我们需要,在需要拉伸的元素中添加八个 拉伸控件。
我们先定义拉伸控件的样式

/*四个交的大小 四条边的宽*/
:root {
  --cornersize: 10px;
  --bordersize: 5px;
}

/*拉伸控件的样式*/
.resize-handle {
  position: absolute;
  background-color: #00000000;
}

/*分别 定义各拉伸控件的位置*/
.top-left {
  width: var(--cornersize);
  height: var(--cornersize);
  top: calc(-1 * var(--cornersize)/2);
  left: calc(-1 * var(--cornersize)/2);
  cursor: nw-resize;
}

.top-right {
  width: var(--cornersize);
  height: var(--cornersize);
  top: calc(-1 * var(--cornersize)/2);
  right: calc(-1 * var(--cornersize)/2);
  cursor: ne-resize;
}

.bottom-right {
  width: var(--cornersize);
  height: var(--cornersize);
  bottom: calc(-1 * var(--cornersize)/2);
  right: calc(-1 * var(--cornersize)/2);
  cursor: se-resize;
}

.bottom-left {
  width: var(--cornersize);
  height: var(--cornersize);
  bottom: calc(-1 * var(--cornersize)/2);
  left: calc(-1 * var(--cornersize)/2);
  cursor: sw-resize;
}

.top {
 
  top: -1px;
  left: calc(var(--cornersize)/2);
  height: var(--bordersize);
  width: calc(100% - var(--cornersize));
  cursor: ns-resize;
}

.right {
  top: calc(var(--cornersize)/2);
  right: -1px;
  width: var(--bordersize);
  height: calc(100% - var(--cornersize));
  cursor: ew-resize;
}

.bottom {
  bottom: -1px;
  left: calc(var(--cornersize)/2);
  height: var(--bordersize);
  width: calc(100% - var(--cornersize));
  cursor: ns-resize;
}

.left {
  top: calc(var(--cornersize)/2);
  left: -1px;
  width: var(--bordersize);
  height: calc(100% - var(--cornersize));
  cursor: ew-resize;
}

根据调整 --cornersize与–bordersize来调整感应范围。添加背景颜色看见其感应区。

二、算法

在元素拉伸过程中,我们需要记录当前 元素的 width与height、鼠标点击是的 x与y、拉伸过程中鼠标的x与y。在鼠标拖拽移动时,按照拖拽控件的方向计算 拖拽差值,根据差值重新,计算拖拽元素的width和height

// 为每个拉伸控件添加按下鼠标事件
    resizeHandles.forEach((handle) => {
      handle.addEventListener("mousedown", startResize);
    });

function startResize(event) {
      event.preventDefault();
      // 获取 拉伸控件的方向
      const direction = event.target.className.split(" ")[1];
      const startX = event.clientX;
      const startY = event.clientY;
      const startWidth = el.offsetWidth;
      const startHeight = el.offsetHeight;
      const startLeft = el.offsetLeft;
      const startTop = el.offsetTop;

      document.addEventListener("mousemove", resize);
      document.addEventListener("mouseup", stopResize);
      function resize(event) {
        const dx = event.clientX - startX;
        const dy = event.clientY - startY;
        let width = startWidth,
          height = startHeight,
          left = startLeft,
          top = startTop;
        if (direction.includes("left")) {
          width = startWidth - dx + "px";
          left = startLeft + dx + "px";
        }
        if (direction.includes("right")) {
          width = startWidth + dx + "px";
        }
        if (direction.includes("top")) {
          height = startHeight - dy + "px";
          top = startTop + dy + "px";
        }
        if (direction.includes("bottom")) {
          height = startHeight + dy + "px";
        }
        el.style.width = width;
        el.style.height = height;
        el.style.left = left;
        el.style.top = top;
      }
      function stopResize() {
        document.removeEventListener("mousemove", resize);
        document.removeEventListener("mouseup", stopResize);
      }
    }

三、封装为 Vue指令

在使用之前,各拖拽控件的样式是必要的,将其挂载到全局样式中。在封装的过程中,我添加了,缩小的参数。为空则每次默认最小50%

Vue2

  1. 创建dragSize.js文件
export default {
  bind(el, binding) {
// 缩放功能
    // 八个方位 八个盒子
    let classes = ["top", "left", "bottom", "right", "top-left", "top-right", "bottom-left", "bottom-right"];
    let resizeHandles = [];
    classes.forEach((className) => {
      let newNode = document.createElement("div");
      newNode.classList.add("resize-handle", className);
      resizeHandles.push(newNode);
      el.appendChild(newNode);
    });

    resizeHandles.forEach((handle) => {
      handle.addEventListener("mousedown", startResize);
    });

    function startResize(event) {
      event.preventDefault();
      // 获取 拉伸控件的方向
      const direction = event.target.className.split(" ")[1];
      const startX = event.clientX;
      const startY = event.clientY;
      const startWidth = el.offsetWidth;
      const startHeight = el.offsetHeight;
      const startLeft = el.offsetLeft;
      const startTop = el.offsetTop;
      let minWidth;
      let minHeight;
      if (!binding.value) {
      //  不存在参数,默认缩放最小长宽为原先比列50%
        minWidth = startWidth / 2;
        minHeight = startHeight / 2;
      } else {
        minWidth = (binding.value.width > startWidth) ?  startWidth / 2 : binding.value.width;
        minHeight = (binding.value.height > startHeight) ? startHeight / 2 : binding.value.height;
      }
      document.addEventListener("mousemove", resize);
      document.addEventListener("mouseup", stopResize);
      function resize(event) {
        const dx = event.clientX - startX;
        const dy = event.clientY - startY;
        let width = startWidth,
          height = startHeight,
          left = startLeft,
          top = startTop;
        if (direction.includes("left")) {
          width = startWidth - dx + "px";
          left = startLeft + dx + "px";
        }
        if (direction.includes("right")) {
          width = startWidth + dx + "px";
        }
        if (direction.includes("top")) {
          height = startHeight - dy + "px";
          top = startTop + dy + "px";
        }
        if (direction.includes("bottom")) {
          height = startHeight + dy + "px";
        }
        if (parseInt(width) < minWidth || parseInt(height) < minHeight) return;
        el.style.width = width;
        el.style.height = height;
        el.style.left = left;
        el.style.top = top;
      }
      function stopResize() {
        document.removeEventListener("mousemove", resize);
        document.removeEventListener("mouseup", stopResize);
      }
    }
}
  1. 创建directive文件夹,在其index.js中
import dragSize from "./dragSize";
const install = function(Vue) {
  Vue.directive('dragSize', dragSize)
 }
export default install
  1. 在msin.js绑定
import directive from './directive'
Vue.use(directive)

Vue3

  1. 创建dragSize.js文件
export default
  (el, binding) => {
  	... 内容同上
 }
  1. 创建directive文件夹,在其index.js中
import dragSize from "@/directive/dragSize";

const install = function(app) {
    app.directive('dragSize', dragSize)
}

export default install
  1. 在main.js中绑定
import directive from "@/directive";

const app = createApp(App)
directive(app)// 注册全局指令
app.mount('#app')

使用

<div v-drag-size></div> 或者 <div v-drag-size="{width: 500, height: 300}"></div>

总结

本文介绍了,如何给元素添加拖拽放大/缩小效果,并制作为 Vue指令。希望对大家有帮助在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值