vatro / svelthree

Create three.js content using Svelte components.
https://svelthree.dev
MIT License
485 stars 15 forks source link

Figure out hot to handle `shadow_root` and `shadow_dom_target` in non-svelthree 'container' components #72

Closed vatro closed 2 years ago

vatro commented 2 years ago

Given:

<!-- Car.svelte (non-svelthree 'container' component with a svelthree 'wrapper' component child having slots populated by non-svelthree containers) -->

<Empty name="car_wrapper" >
  <Body name="car_body" />
  <Wheels name="car_wheels" />
</Empty>
<!-- Body.svelte (non-svelthree 'container' component with a svelthree child) -->

<Mesh name="car_body" />
<!-- Wheels.svelte (non-svelthree 'container' component with svelthree children) -->

<Mesh name="wheel_1"/>
<Mesh name="wheel_2"/>
<Mesh name="wheel_3"/>
<Mesh name="wheel_4"/>
<!-- App.svelte -->

<Canvas>
  <Scene>
    <Car />
  </Scene>
</Canvas>

Problem: https://github.com/vatro/svelthree/blob/07120b002240dd5a62f0db7a2501664dafb64f40/src/components/Mesh.svelte#L342-L347

vatro commented 2 years ago

Turns out, the "problem" has something to do with Svelte. If a non-svelthree 'container' component has a child component with slots and bind:this is set on any of it's slots, the child component will receive context before anything else, even before the Scene which is actually it's higher parent -> means: the Scene component will have no shadow_dom_target which get's generated reactively when the Scene receives the shadow_root (store) / $shadow_root.element shared via context by Canvas.

I've filed a Svelte issue concerning this, but I guess I'll have to find some workaround, like... simply avoiding slots / referencing slots inside non-svelthree 'containers' / 'wrappers'? 🤔 ... no.

vatro commented 2 years ago

ok, this is a good DUCKTAPE-FIX candidate:

vatro commented 2 years ago

Unfortunately this is still not 100% fixed...

After fiddling a bit, I think the real fix would be to share parents_shadow_dom_target in a store via context and let the children reactively generate their shadow_dom_target when parents_shadow_dom_target is available.

Removing await tick() from Scene component's logic made it look good for the most of the test-scenes, even scene-in-scene, but the shadow DOM structure of scene-in-mesh was wrong, means this is not the fix.

vatro commented 2 years ago

Ok, will deploy the new fix in a minute. I ended up using context only (not stores) close to the logic for getting parent components / scenes. Checked with test-scenes, looking good.