vue自定义简易拖拽指令

770 阅读1分钟

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>