Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hover on button #9

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

vincentfretin
Copy link
Collaborator

@vincentfretin vincentfretin commented Dec 6, 2022

This references #3
Don't merge it just yet. I opened the PR as draft for discussion and if anyone want to test it.
I'm still experimenting, I added the behavior only on button, but it should be added to input of type submit and button as well. Do we need it on other elements?
If we move too quickly out of the button, the active or hover classes aren't deleted. I need to investigate that, we should have the mouseleave event from the whole div normally so this is strange...

I'm testing on this button styled with tailwindcss:

<button
  onClick="AFRAME.scenes[0].exitVR();"
  class="btn-red"
>
  Exit Immersive
</button>

and

.btn-red {
    @apply bg-red-600;
    &:hover,
    &.hover {
      @apply bg-red-700;
    }
  }

@vincentfretin
Copy link
Collaborator Author

Huh it actually doesn't work in VR. ><
It works in the 3d scene on desktop though.

@vincentfretin
Copy link
Collaborator Author

If we move too quickly out of the button, the active or hover classes aren't deleted. I need to investigate that, we should have the mouseleave event from the whole div normally so this is strange...

Actually there is no mouseleave / mouseenter events handling in HTMLMesh, that can explain it.

@vincentfretin
Copy link
Collaborator Author

I made progress, some things I figured out.
Using classList.add('hover') on mousemove, even if the class is already there triggers the MutationObserver and rerender, so be sure to use !element.classList.contains('hover') before adding the class to not trigger rerender on each frame while the cursor is on the UI.
With that fixed, the color change didn't work anymore on hover desktop, rerender after 16ms was not enough for the repaint to be done to change style.backgroundColor. Increasing the timeout to 100ms fixed it, but I'm not happy to increase the timeout like this.
Now in VR, it didn't work because repaint is not done at all I think, so style.backgroundColor never change even if I have my hover class on the element. So here I hard coded the color to change backgroundColor if I have the hover class.
This is really not in a mergeable state, but at least if someone else want to contribute to it, they have some hints how it can work.

@AdaRoseCannon
Copy link
Owner

Thanks for taking a look

@vincentfretin
Copy link
Collaborator Author

vincentfretin commented Dec 20, 2022

I understood something.
I actually have in my tailwindcss for the button the class "transition duration-150 ease-in-out"
This translate to:

transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-transform, -webkit-filter, -webkit-backdrop-filter;
transition-duration: 150ms;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);

and window.getComputedStyle( element ) is getting a read only live style but potentially in the middle of the 150ms animation, so it could render in a background color in between the two states.

@vincentfretin
Copy link
Collaborator Author

I also had some js that focused the first button in the div, changing the button background color. That didn't help to have a non focused background color for the button in the rendered canvas lol.

@vincentfretin
Copy link
Collaborator Author

Instead of hard coding my own style for hover, latest commit implements a way to store button hover style as data attributes so it works in VR.

@vincentfretin
Copy link
Collaborator Author

This is a bit hackish but it works. :D

src/HTMLMesh.js Outdated Show resolved Hide resolved
@vincentfretin
Copy link
Collaborator Author

Tips: I'm using z-index: -1 on my div to hide it. You can't use display:none obviously.
I also implemented a way to wrap the text and scale x3 the PlaneGeometry. I'll probably do another PR for that to show you once I have a better code that is not tied to my project. Here is a screenshot of what I'm doing:
Capture d’écran du 2022-12-20 17-52-02

@AdaRoseCannon
Copy link
Owner

neat!

@vincentfretin
Copy link
Collaborator Author

Instead of the element instanceof HTMLButtonElement condition I wrote in several places that matches only button, we need to modify it to match for input with type button submit reset.
But we may actually want the hover on any element. We could add the hover class on all elements really on first render, get the style with and without hover class, compare the two, if those two are different, set some data attribute element.dataset.simulateHover = 'true' then we handle adding and removing the class on the element on cursor intersection if element.dataset.simulateHover === 'true'

@vincentfretin
Copy link
Collaborator Author

I want to mention an alternative to the above. I looked at the web2vr code, this creates primitive meshes, it doesn't render on a canvas, but there is some code related to hover. It checks all css selectors in all stylesheets for :hover, rewrite the selector to add .hover class. Then for each element that is rendered, it checks if one of the hover selectors matches to add the correct class when cursor intersects the element. I'm not sure if we want to go that way. Related code:
https://github.com/kikoano/web2vr/blob/1098bfbb219675dd4baeb55a645ef8fe9dc463d9/src/web2vr.js#L55-L81
https://github.com/kikoano/web2vr/blob/1098bfbb219675dd4baeb55a645ef8fe9dc463d9/src/elements/element.js#L42-L92
This doesn't fix the issue of element.style not updated in VR for our case here though.

@vincentfretin
Copy link
Collaborator Author

Did you know about aframe-htmlembed-component? I just rediscovered it after several years. I completely forgot this one existed and it was not listed in the component registry.
The component is a bit different from aframe-htmlmesh. The htmlembed component is serializing the html to a string, create a svg with a foreignObject containing the serialized html, convert the svg to an image then draw the image to the canvas. This seems so much work to do a render.
It has more features currently compared to aframe-htmlmesh.
It also doing some fancy stuff adding new css rules with hover class here:
https://github.com/supereggbert/aframe-htmlembed-component/blob/1c1448e0c7aaff66cf984e7efcb15d893fd05b3b/src/htmlcanvas.js#L146-L175

@remmel
Copy link

remmel commented Oct 2, 2023

@vincentfretin thanks for sharing https://github.com/supereggbert/aframe-htmlembed-component did you check how long last a render? It looks great!
I tried in the past some html2canvas plugin (maybe https://www.npmjs.com/package/html2canvas ) but it was too slow; I guess it must last less than 0.16 to render (even if it must be rendered only when needed; but some effect such as sliding must be generated quickly )

@vincentfretin
Copy link
Collaborator Author

No I didn't do any perf test with htmlembed. html2canvas is Promise based, so not really for a VR usage to draw on each frame when interacting with a slider for example. Main usage is to create a screenshot of the page.

@remmel
Copy link

remmel commented Oct 3, 2023

aframe-htmlembed-component is also Promise base, but seems to be fast enough in their demo

@vincentfretin
Copy link
Collaborator Author

Fair, my assumption about Promise may be wrong. Again, I didn't do any tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants