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 拖拽结束时
const { minWidth, maxWidth, minHeight, maxHeight, onStart, onMove, onEnd, 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`) : null));
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);
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);
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;
请登录后查看回复内容