前言
在前端开发中,常常需要动态控制组件的长宽。其中拖拽控制是一个控制元素长宽很好的方式。本文将很好的介绍,拖拽控制元素大小的方式。
一、拉伸样式
元素拉伸的有八个方向,上、右上、右、右下、下、左下、左、左上,如下图所示
因此我们需要,在需要拉伸的元素中添加八个 拉伸控件。
我们先定义拉伸控件的样式
/*四个交的大小 四条边的宽*/
: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
- 创建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);
}
}
}
- 创建directive文件夹,在其index.js中
import dragSize from "./dragSize";
const install = function(Vue) {
Vue.directive('dragSize', dragSize)
}
export default install
- 在msin.js绑定
import directive from './directive'
Vue.use(directive)
Vue3
- 创建dragSize.js文件
export default
(el, binding) => {
... 内容同上
}
- 创建directive文件夹,在其index.js中
import dragSize from "@/directive/dragSize";
const install = function(app) {
app.directive('dragSize', dragSize)
}
export default install
- 在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指令。希望对大家有帮助