0

I have created a very simple example of my problem.

Fiddle Link

In the fiddle, I have created a div named parent containing 2 imgs (i take divs in the example for simplicity but in my project, these are images) and a controller div. I place the images on the top of each other by positioning 2nd image as absolute.

I want to clip the 2nd image using clip-path property whenever, I click and then drag the controller" over the parent div.

But the controller div is causing issue with parent mousemove event whenever cursor goes on controller div, mouseout event is fired on parent div causing glitch in animation.

Adding pointer-events: none property to controller div fix the glitch but it also takes away every kind of mouse interaction from the element and I want click and drag effect.

I want to create similar effect used in this website.

0

2 Answers 2

1

The problem seems to be that the positioning of the controller sometimes (not always) 'interferes' with the reading of offsetX on the parent. And the offset goes down (to 0 or up to about 10 in the given fiddle). Hence you get the flickering as the controller moves back and then up along again.

I cannot at the moment totally explain this, particularly since the controller is an absolutely positioned element.

However, one solution is to move the controller out of the parent.

UPDATE It is though possible to leave the controller in the parent if one ignores any mousemove within the controller (so we don't get readings of 0 to 10 for the offset when the mousemove is within the controller - ignore them and we'll get the event bubbling through to the parent and can then take a reading of offset). _

<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>

Sign up to request clarification or add additional context in comments.

7 Comments

Appreciate your efforts, event bubbling trick works but the animation is not really smooth
I think, I have to put controller outside of parent element but then controller position will not be relative to parent anymore.
Not really smooth because it's not a 'real' animation - it's discrete. Not sure what you'd do to make it smooth given it's a clip (rather than, say, a translation). would it be suitable for your final requirement to convert from a clip to a translation perhaps?
I put the controller outside the parent in my initial answer - I'm not sure what you mean by 'position not relative to parent anymore' - I still used offsetX to decide where to put the controller, and still monitored the event on the parent. I don't see it would alter the non smoothness though.
I don't understand the meaning of "convert from a clip to a translation" but I want to show the behind image whenever controller moves in the parent, approach doesn't matter. If you know another method rather than clipping the front image then kindly share it.
|
0

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> 

6 Comments

I want mouse position relative to the parent element not window object because controller position is absolute to parent element and onmousemove event is attached to the parent not window object
The same problem happens if you use clientX and anyway it should be OK using offsetX as the question requires. If you don't move the controller offsetX gives the correct offset all the time and the 'animation' works without flickering. However, if you move the controller element the offset goes down (sometimes to 0, sometimes a bit more) occasionally. I cannot explain this (currently!) - especially since the controller is actually at z-index 1 so should not be affecting the offset on the parent.
Found a workaround - have put snippet in an answer.
@AHaworth I don't think offsetX can be used since it gets the distance to target's top left corner (target of the evenet is the controller in that case), and therefore it flickers. The distance between the parent's and the mouse coordinates can be used.
Actually, the problem is that (occasionally) the offset is the offset of the mousemove within the controller element, not within the parent, hence sometimes you get an offsetX reading of 0 to 10 (the width of the controller, which is what I observed). I'll update my answer to put the controller back into the parent but to ignore the movement within the controller.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.