前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2021-10-17 JS使模板元素进行移动(拖拽模板元素)

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

作者头像
无道
发布2021-10-19 10:59:08
2.4K0
发布2021-10-19 10:59:08
举报
文章被收录于专栏:无道编程无道编程

前言

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

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


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

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

需求

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

image-20211017153210687
image-20211017153210687

过程

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

1、第一步获取offsetX(Y)

image-20211017153503076
image-20211017153503076

如图说明:

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

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

image-20211017153929517
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)。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-10-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 需求
  • 过程
  • 代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档