mistic100 / Photo-Sphere-Viewer

A JavaScript library to display 360° sphere panoramas.
https://photo-sphere-viewer.js.org
MIT License
1.87k stars 675 forks source link

virtual-tour-plugin new arrows not working in web-components #1412

Closed sho-ax closed 1 week ago

sho-ax commented 3 weeks ago

Describe the bug

I'm using the Photo Sphere Viewer with the Virtual Tour Plugin in lit web-components. Since the new arrows in the Virtual Tour Plugin are implemented, they are in my case not clickable anymore while the hover effects still work. I discovered that the "mouseup" eventlistener in core/index.module.js in line 4782 is used for the click on an arrow to move to another pano. My problem is: since the eventlistener is added to window, the returned target of the event is the web-component, in which the Photo Sphere Viewer is placed.

In my case it solved the issue, when I changed the current code in core/index.module.js in the class EventsHandler in function init():

...
4781 window.addEventListener("mousemove", this, { passive: false });
4782 window.addEventListener("mouseup", this);
4783 this.viewer.container.addEventListener("touchstart", this, { passive: false });
...

to the following code:

...
4781 window.addEventListener("mousemove", this, { passive: false });
4782 this.viewer.container.addEventListener("mouseup", this);
4783 this.viewer.container.addEventListener("touchstart", this, { passive: false });
...

Online demo URL

No response

Photo Sphere Viewer version

5.9.0

Plugins loaded

virtual-tour-plugin; gallery-plugin;

OS & browser

MacOS: Firefox

Additional context

No response

mistic100 commented 3 weeks ago

binding the mouseup to the container is not a suitable solution because it leads to unvoluntary movement when the user moves the cursor away from the viewer before releasing the button.

Please provide a minimal reproduction, I don't know what lit is.

github-actions[bot] commented 2 weeks ago

This issue has been automatically marked as stale because not enough information was provided. It will be closed if no further activity occurs.

sho-ax commented 2 weeks ago

Here is a Demo with the PSV in a WebComponent in form of a LitElement: https://codesandbox.io/p/sandbox/virtual-tour-with-lit-hx3c2j

And a more pecise description of the problem:

WebComponents

WebComponents are a technology to build customElements, custom encapsulated HTML Elements with internal encapsulated html and js code. These customElements can be used as default html Tags/Elements in the HTML DOM tree. This concept has many advantages in bigger, complex WebApps but often causes problems with classical JS libraries. LitElements are just an easier to use framework around WebComponents and have nothing directliy to do with this Problem.

Detailed description of this specific Problem

CustomElements also encapsulate events, which means that if we catch an event outside a customElement that is dispatched inside, the event target is the outer customElement and not the original element, in this case the arrow button svg, which is defined inside the customElement. Instead, there is a method event.composedPath() that returns a list with all Elements of the whole path of the event trough the dom tree. Hence, it also includes the clicked arrow div Element. You can see this with the mouseup event in the handleEvent method of the PSV EventHandler, when you have clicked on an link arrow in the virtual tour.

Possible solution

So, a possible solution could be to infer the clicked arrow element from the composedPath instead of the event target Element. The composedPath should be available in all modern browsers.

mistic100 commented 1 week ago

When using closed shadow dom and the event listener is attached to window, the composedPath stops at the web component element, the rest of the hierarchy is "lost".

If I attach the listener to viewer.container.getRootNode() it exhibits the same buggy behaviour as described in my first message (when using a Web Component, else it works as before).

I can listen to "mouseout" on the container to stop movements, but that only works on devices with a mouse, I also need to listen for touchmoves outside the viewer.

If I listen on both window and the container, the handler is called twice (obviously) without any way to know which one is which (when not using web components, if using web components I can filter out the one where the viewer container is not in the composed path).

What a mess...

sho-ax commented 1 week ago

Lit uses open shadow dom as default, so in my case this would work fine for me either way. I don't know if there would be a more elegant solution for using closed shadow dom but if you would like it to work in as many cases as posible, i think your consideration of using both a listener on window and the container would work anyway. You can check the composed path regardless of whether using web components or not. In my quick test the evt.composedPath() worked also without a web component arround the photo-sphere-viewer and deliveres the container when the mouse button is lifted inside the container but not, when it was lifted outside.

mistic100 commented 1 week ago

I used composedPath everywhere I could and also added a check to log an error if a closed shadow DOM is used.

sho-ax commented 1 week ago

thank you very much for your quick reaction!

github-actions[bot] commented 1 week ago

This feature/bug fix has been released in version 5.10.0.