2021-10-17 JS使模板元素进行移动(拖拽模板元素)

无道 2021-10-17 0 条评论 前端相关 阅读130 手机阅读

前言

拖拽模板元素,需要明白:

原理很简单,就是将元素设置为绝对定位,然后监听鼠标按下(mousedown),移动事件(mousemove),改变元素的top、left值就行。


但是具体场景,具体业务需要是不一样的,需要具体来说。

下面以我最近的需求为例,来展示如何编写vue3 hooks

需求

如图,我需要:点集Header部分时,随鼠标移动整体部分。

image-20211017153210687

过程

设置目标元素的top,left,就需要想法计算top,left

1、第一步获取offsetX(Y)

image-20211017153503076

如图说明:

  • 点1、鼠标点击地方
  • 点2、浏览器最左上角
  • 线段3、初始状态下的整个目标元素的初始left
  • 线段4、鼠标点击时的clientX

第一步,计算一个鼠标点击时的,点击点到元素内部的offsetX(也就是线段4-线段3这段距离),懂Js的会说,用js的点击事件e.offsetX不就行吗?

image-20211017153929517

这就是我说的,具体业务得具体分析,我想点击header部分移动整个body,但是现在有个padding,通过e.clientX获取的值不会包含这个padding,导致如果直接使用这个e.clientX的话,点击时会有个瞬移,用户体验不好,且有其他问题。

所以计算offsetX是:offsetX = e.clientX - el.left (解释:鼠标初始点击时的位置 减去 元素本来的left值)

第二步,通过鼠标移动事件的e.clientX计算目前目标元素的left值:

其实很简单:left = e.clientX - 第一步计算的offsetX

上面仅仅计算了left,top同样的道理。

代码

import { ref } from 'vue';
import useMobile from '@/hooks/useMobile';
export interface WindowMoveParams {
  stopInMobile: boolean;
  stopInOverWindow: boolean;
  onMove: (top: number, left: number) => void;
}
const useWindowMove = (aim: HTMLElement, container: HTMLElement, params?: Partial<WindowMoveParams>) => {
  if (!aim || !container) {
    console.warn(`aim or container element is null`);
    return;
  }
  params = {
    stopInMobile: params?.stopInMobile || true,
    stopInOverWindow: params?.stopInOverWindow || false,
    ...params,
  };
  const { isMobile } = useMobile();
  const mobile = isMobile();
  if (params.stopInMobile && mobile.value) {
    console.warn(`stop window move when in mobile device`);
    return;
  }

  //offset is mouse click offset aim el
  // 通过计算而来:首次点击的clientX - container元素默认的left = OffsetX
  const offsetX = ref(0);
  const offsetY = ref(0);
  const containerCpt = getComputedStyle(container);
  let containerTop = parseFloat(containerCpt.top);
  let containerLeft = parseFloat(containerCpt.left);

  let containerHeight = parseInt(containerCpt.height);
  let containerWidth = parseInt(containerCpt.width);
  const mouseMove = (e: MouseEvent) => {
    let latestTop = e.clientY - offsetY.value;
    let latestLeft = e.clientX - offsetX.value;

    if (params?.stopInOverWindow) {
      if (
        latestTop <= 0 ||
        latestLeft <= 0 ||
        latestTop + containerHeight > window.innerHeight ||
        latestLeft + containerWidth > window.innerWidth
      )
        return;
    }
    params?.onMove && params.onMove(latestTop, latestLeft);
    container.style.top = latestTop + 'px';
    container.style.left = latestLeft + 'px';
  };
  const mouseOut = () => {
    //update left, top
    const containerCpt = getComputedStyle(container);
    containerTop = parseFloat(containerCpt.top);
    containerLeft = parseFloat(containerCpt.left);
    aim.style.cursor = 'default';
    document.removeEventListener('mousemove', mouseMove);
  };
  // aim.addEventListener('click', aimElClick);
  aim.addEventListener('mousedown', (e) => {
    console.log('mousedown');
    // console.log(e.clientY, e.clientX);
    offsetY.value = e.clientY - containerTop;
    offsetX.value = e.clientX - containerLeft;
    aim.style.cursor = 'move';
    document.addEventListener('mousemove', mouseMove);
  });

  // move out event
  // container.addEventListener('mouseout', mouseOut);
  window.addEventListener('mouseup', mouseOut);
  aim.addEventListener('mouseup', mouseOut);
  container.addEventListener('mouseup', mouseOut);
};

export default useWindowMove;

当然,上面代码是经过不少细节修改的,其中用到了一个useMobile,判断是不是手机宽度,删除这段代码就行(line18-23)。

全文完 [
有帮助?打赏
支付宝打赏
微信打赏
]
标签: js 拖拽元素
修改: 10月17日 15:52
下一篇: 没有下一篇啦~
这篇文章还没有评论呢~
点击刷新/生成验证码