vue结合rxjs实现一个简易拖拽指令,使用transform完成移动,同时支持仅在X或Y移动,移动中事件,结束时事件
1.核心代码
import { fromEvent } from 'rxjs';
import { map, switchMap, takeUntil, finalize, } from 'rxjs/operators';
export const drag = {
bind: function (el, binding) {
const mouseDown = fromEvent(el, 'mousedown', { capture: false });
const mouseMove = fromEvent(document, 'mousemove');
const mouseUp = fromEvent(document, 'mouseup');
function getPos(el) {
const style = getComputedStyle(el)
const regExp3d = /matrix3d\(([-\d.]+,\s*){12}([-\d.]+),\s*([-\d.]+),\s*([-\d.]+)/i;
const regExp = /matrix\(([-\d.]+,\s*){4}([-\d.]+),\s*([-\d.]+)/i;
const result = style.transform.match(regExp) || style.transform.match(regExp3d);
console.log(result)
if (result) {
const [, , x, y, z] = result;
return {
x: parseFloat(x),
y: parseFloat(y),
z: parseFloat(z || 0)
}
}
return {
x: 0,
y: 0,
z: 0
}
}
const { onMouseEnd, onMouseMove } = binding.value || {};
const { onlyX = false, onlyY = false } = binding.modifiers;
mouseDown.pipe(
map((downEvent) => {
downEvent.stopPropagation();
downEvent.preventDefault();
return {
pos: getPos(el), /// 获取当前的pos, transform会计算为matrix3d或者matrix
downEvent,
};
}),
switchMap((initialState) => {
const initialPos = initialState.pos;
const { clientX, clientY } = initialState.downEvent
return mouseMove.pipe(
map((moveEvent) => {
const distX = moveEvent.clientX - clientX + initialPos.x;
const distY = moveEvent.clientY - clientY + initialPos.y;
return {
x: onlyY ? initialPos.x : distX,
y: onlyX ? initialPos.y : distY,
z: initialPos.z
}
}),
takeUntil(mouseUp),
finalize(() => onMouseEnd && onMouseEnd())
)
}),
)
.subscribe((pos) => {
el.style.transform = `translate3d(${pos.x}px , ${pos.y}px, ${pos.z}px)`
onMouseMove && onMouseMove(pos)
})
}
}
只需要关注mouseEvent的clientX和clientY,在switchMap前记录mouseDown的clientX和clientY, 然后mouseMove的clientX和clientY减去mouseDown的对应就是鼠标移动的距离,然后加上元素初始位置的xyz就是元素最终的位置。
2.使用
<div v-drag.onlyY="{onMouseMove:onMouseMove}" style="background: red; height: 100px ; width: 100px; "></div>