子元素导致父onmousmove事件监听器出现问题



我创建了一个非常简单的例子来说明我的问题。

小提琴链接

在这里,我创建了一个名为parent的div包含2张图片(我采取div在例子中简单,但在我的项目中,这些是图像)控制器我通过将第二张图像定位为absolute

,将图像放在彼此的顶部。.我想使用clip-path剪辑第二张图片属性,然后点击并拖动控制器div .

但是控制器div导致父鼠标移动的问题当光标到达控制器div时,mouseout父节点上触发事件Div导致动画故障。

添加pointer-events: none属性到控制器div修复了这个小故障,但它也带走了元素的各种鼠标交互,我想要点击和拖动效果。

我想创建一个类似于这个网站的效果。

问题似乎是控制器的定位有时(并非总是)'干扰'读取父节点上的偏移x。偏移量减小(在给定的小提琴中为0或10左右)。因此,当控制器向后移动然后再次向上移动时,你会得到闪烁。

我现在不能完全解释这一点,特别是因为控制器是一个绝对定位的元素。

然而,一种解决方案是将控制器移出父控制器。

如果忽略控制器内的任何鼠标移动,则可以将控制器留在父控制器中(因此,当鼠标移动在控制器内时,我们不会获得0到10的偏移量读数-忽略它们,我们将获得事件气泡通过父控制器,然后可以读取偏移量)。_

<head>
<style>
* {
margin: 0;
padding: 0;
}
#parent {
width: 100%;
position: relative;
}
#img1, #img2 {
display: block;
width: 100%;
height: 200px;
pointer-events: none;
}
#img1 {
background: red;
}
#img2 {
background: green;
position: absolute;
top: 0;
left: 0;
}
#controller {
position: absolute;
top: 0;
left: 10px;
width: 10px;
height: 100%;
height: 200px;
background: black;
z-index: 1;
cursor: ew-resize;
/* pointer-events: none; */
}
</style>
</head>
<body>
<div id="parent">
<div id="img1"></div>
<div id="img2"></div>
<div id="controller"></div>
</div>
<h4>
Click and Drag the controller to clip the front image
</h4>
<!-- img1, img2 are images in my case so i named them as imgs -->
<script>
const parent = document.getElementById('parent'),
img2 = document.getElementById('img2'),
controller = document.getElementById('controller');

let pressed = false;
console.log(pressed)
parent.addEventListener('mousemove', (e) => {
if(!pressed) return;
if (e.target != parent) return;
img2.style.clipPath = `inset(0px 0px 0px ${e.offsetX}px)`;
controller.style.left = `${e.offsetX}px`;
});
// for testing purpose
/* parent.addEventListener('mouseout', (e) => {
console.log('mouse out is called');
}); */
controller.addEventListener('mousedown', (e) => {
pressed = true;
});
controller.addEventListener('mouseup', (e) => {
pressed = false;
});
</script>
</body>

const parent = document.getElementById('parent'),
img2 = document.getElementById('img2'),
controller = document.getElementById('controller');
let pressed = false;
parent.addEventListener('mousemove', (e) => {
if (pressed) {
img2.style.clipPath = `inset(0px 0px 0px ${e.clientX - parent.offsetLeft}px)`;
controller.style.left = `${e.clientX - parent.offsetLeft}px`;
}
});
controller.addEventListener('mousedown', (e) => {
pressed = true;
});
controller.addEventListener('mouseup', (e) => {
pressed = false;
});
#parent {
width: 100%;
position: relative;
}
#img1,
#img2 {
display: block;
width: 100%;
height: 200px;
pointer-events: none;
}
#img1 {
background: red;
}
#img2 {
background: green;
position: absolute;
top: 0;
left: 0;
}
#controller {
position: absolute;
top: 0;
left: 10px;
width: 10px;
height: 100%;
background: black;
z-index: 1;
cursor: ew-resize;
/* pointer-events: none; */
}
<div id="parent">
<div id="img1"></div>
<div id="img2"></div>
<div id="controller"></div>
</div>
<h4>
Click and Drag the controller to clip the front image
</h4>

问题是,您使用了offsetX,它定义了controller元素的左上角之间的距离。这意味着距离大约是5px,你的控制器从左边跳到5px,现在距离变大了,控制器向后跳,以此类推。

MouseEvent接口的offsetX只读属性提供鼠标指针的X坐标在该事件之间的偏移量和目标节点的填充边.

因此,你可以使用鼠标的x位置和父节点的x位置之差来定位你的控制器:

使用clientX来获取鼠标相对于窗口的位置。

img2.style.clipPath = `inset(0px 0px 0px ${e.clientX - parent.offsetLeft}px)`;
controller.style.left = `${e.clientX - parent.offsetLeft}px`;

顶部表达式有以下含义:

<mouse x-position> - <distance between left screen edge and parent>

最新更新