const dragSize = {
install(Vue) {
Vue.directive("dragSize", {
// Vue2 inserted
beforeMount(el, binding) {
const state = {
element: el,
allowedDirections: {
top: false,
bottom: false,
left: false,
right: false,
topLeft: false,
topRight: false,
bottomLeft: false,
bottomRight: false,
},
dragWidth: 3, // 拖拽条的 宽度或者高度
class: "dragSize", // 拖拽条的class
touch: { lastTime: 0, interval: 300, startX: 0, startY: 0 },
moveX: 0,
moveY: 0,
mousemove_: null,
mouseup_: null,
};
// onStart 开始前
// onMove 拖住时
// onEnd 拖拽结束时
// onCustomSize 自定义调整大小
const { onStart, onMove, onEnd, onCustomSize, all, ...directions } = binding.value || {};
const isNumber = (value) => Object.prototype.toString.call(value) === "[object Number]" && !isNaN(value);
["minWidth", "maxWidth", "minHeight", "maxHeight"].forEach((property) => (isNumber(binding.value[property]) ? (el.style[property] = `${binding.value[property]}px`) : (el.style[property] = binding.value[property])));
state.allowedDirections = ["top", "bottom", "left", "right", "topLeft", "topRight", "bottomLeft", "bottomRight"].reduce((acc, key) => {
acc[key] = !!all || !!directions[key];
return acc;
}, {});
state.class = directions?.class ? state.class + " " + directions?.class : state?.class;
state.dragWidth = isNumber(directions?.dragWidth) ? directions?.dragWidth : 3;
el.style.position = "relative";
let isOnMove = false;
state.mousemove_ = (e) => {
if (!state.touch.init) return;
state.moveX = e.pageX - state.touch.startX;
state.moveY = e.pageY - state.touch.startY;
if (onMove && typeof onMove === "function" && !isOnMove) {
onMove(el, e);
isOnMove = true;
}
if (["right", "left", "topRight", "bottomRight", "topLeft", "bottomLeft"].includes(state.touch.type)) {
const newWidth = state.touch.width + (state.touch.type.includes("left") ? -state.moveX : state.moveX);
if (onCustomSize && typeof onCustomSize === "function") {
onCustomSize(el, e, { width: newWidth });
} else {
el.style.width = `${newWidth}px`;
}
}
if (["bottom", "top", "topRight", "bottomRight", "topLeft", "bottomLeft"].includes(state.touch.type)) {
const newHeight = state.touch.height + (state.touch.type.includes("top") ? -state.moveY : state.moveY);
if (onCustomSize && typeof onCustomSize === "function") {
onCustomSize(el, e, { height: newHeight });
} else {
el.style.height = `${newHeight}px`;
}
}
};
state.mouseup_ = (e) => {
document.body.style.cursor = "default";
if (!state.touch.init) return;
if (onEnd && typeof onEnd === "function") {
onEnd(el, e);
}
isOnMove = false;
state.touch.init = false;
document.removeEventListener("mousemove", state.mousemove_);
document.removeEventListener("mouseup", state.mouseup_);
};
const addHandle = (cursor, type, styles = {}, enabled) => {
if (!enabled) return;
const dom = document.createElement("DIV");
Object.assign(dom.style, {
position: "absolute",
zIndex: "10",
cursor: cursor,
userSelect: "none",
touchAction: "none",
});
if (state.class) {
const classNames = state.class.split(" ").filter((className) => className.trim());
dom.classList.add(...classNames);
}
Object.assign(dom.style, styles);
dom.addEventListener("mousedown", (e) => {
const now = +new Date();
if (now - state.touch.lastTime < state.touch.interval) {
return (state.touch.init = false);
}
state.touch.init = true;
state.touch.startX = e.pageX;
state.touch.startY = e.pageY;
const computedStyle = window.getComputedStyle(el);
if (computedStyle.position === "relative" || computedStyle.position === "absolute") {
if (computedStyle.left === "auto" || computedStyle.left === "") {
el.style.left = `${el.offsetLeft}px`;
}
if (computedStyle.top === "auto" || computedStyle.top === "") {
el.style.top = `${el.offsetTop}px`;
}
}
document.body.style.cursor = cursor;
const rect = el.getBoundingClientRect();
Object.assign(state.touch, {
width: rect.width,
height: rect.height,
left: parseFloat(el.style.left) || 0,
top: parseFloat(el.style.top) || 0,
type: type,
});
document.addEventListener("mousemove", state.mousemove_, { passive: false });
document.addEventListener("mouseup", state.mouseup_);
});
el.appendChild(dom);
};
if (onStart && typeof onStart === "function") {
onStart(el);
}
[
["ns-resize", "top", { top: "0", left: "0", right: "0", height: state.dragWidth + "px" }, state.allowedDirections.top],
["ns-resize", "bottom", { bottom: "0", left: "0", right: "0", height: state.dragWidth + "px" }, state.allowedDirections.bottom],
["ew-resize", "left", { top: "0", bottom: "0", left: "0", width: state.dragWidth + "px" }, state.allowedDirections.left],
["ew-resize", "right", { top: "0", bottom: "0", right: "0", width: state.dragWidth + "px" }, state.allowedDirections.right],
["nwse-resize", "topLeft", { top: "0", left: "0", width: "10px", height: "10px" }, state.allowedDirections.topLeft],
["nesw-resize", "topRight", { top: "0", right: "0", width: "10px", height: "10px" }, state.allowedDirections.topRight],
["nesw-resize", "bottomLeft", { bottom: "0", left: "0", width: "10px", height: "10px" }, state.allowedDirections.bottomLeft],
["nwse-resize", "bottomRight", { bottom: "0", right: "0", width: "10px", height: "10px" }, state.allowedDirections.bottomRight],
].forEach((item) => addHandle(...item));
// 保存状态
el.__dragState__ = state;
},
// Vue2 unbind
unmounted(el) {
const state = el.__dragState__;
if (state) {
document.removeEventListener("mousemove", state.mousemove_);
document.removeEventListener("mouseup", state.mouseup_);
}
delete el.__dragState__;
},
});
},
};
export default dragSize;
请登录后查看回复内容