2021-10-17 JS使模板元素进行移动(拖拽模板元素)
无道
2021-10-17
0 条评论
前端相关
阅读1293
手机阅读
前言
拖拽模板元素,需要明白:
原理很简单,就是将元素设置为绝对定位,然后监听鼠标按下(mousedown),移动事件(mousemove),改变元素的top、left值就行。
但是具体场景,具体业务需要是不一样的,需要具体来说。
下面以我最近的需求为例,来展示如何编写vue3 hooks
需求
如图,我需要:点集Header部分时,随鼠标移动整体部分。
过程
设置目标元素的top,left,就需要想法计算top,left
1、第一步获取offsetX(Y)
如图说明:
- 点1、鼠标点击地方
- 点2、浏览器最左上角
- 线段3、初始状态下的整个目标元素的初始left
- 线段4、鼠标点击时的clientX
第一步,计算一个鼠标点击时的,点击点到元素内部的offsetX(也就是线段4-线段3这段距离),懂Js的会说,用js的点击事件e.offsetX不就行吗?
这就是我说的,具体业务得具体分析,我想点击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)。
全文完 [
支付宝打赏
微信打赏
]
有帮助?打赏


非特殊说明,本博所有文章均为博主原创。如若转载,请注明出处:https://www.misiyu.cn/article/189.html
这篇文章还没有评论呢~
It's me
昵称:无道
坐标:成都
性别:
一个人,一介学生社会人,一个儿子~ 愿你我都被世界温暖以待
最新评论
点击排行
随机标签