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>
The problem is, you used offsetX which defines the distance between the top left edge of your controller element. This means the distance is about 5px, your controller jumps to 5px from left, the distance is bigger now, the controller jumps back and so on.
The offsetX read-only property of the MouseEvent interface provides the offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node.
So therefore you can use the difference between the mouse x-position and the x-position of parent for positioning your controller:
Instead use clientX which gets the mouse position relative to the window.
img2.style.clipPath = `inset(0px 0px 0px ${e.clientX - parent.offsetLeft}px)`; controller.style.left = `${e.clientX - parent.offsetLeft}px`;
Top expression has following meaning:
<mouse x-position> - <distance between left screen edge and parent>