Browse Source

fix: 796 unmount the canvas component instant mount children again even if canvas is not mounted (#799)

* fix: avoid mounting again custom renderer components on TresCanvas unmounted

* chore: fix lint
Alvaro Saburido 11 months ago
parent
commit
9a20b52b2c

+ 3 - 2
playground/src/pages/basic/index.vue

@@ -5,7 +5,7 @@ import { TresCanvas, useRenderLoop } from '@tresjs/core'
 import { OrbitControls } from '@tresjs/cientos'
 
 const state = reactive({
-  clearColor: '#201919',
+  clearColor: '#ffffff',
   shadows: true,
   alpha: false,
 
@@ -24,7 +24,7 @@ onLoop(({ elapsed }) => {
   sphereRef.value.position.y += Math.sin(elapsed) * 0.01
 
   // Update events without needing the mouse to move
-  canvasRef.value?.context?.eventManager.forceUpdate()
+  canvasRef.value?.context?.eventManager?.forceUpdate()
 })
 
 function onPointerEnter(ev) {
@@ -50,6 +50,7 @@ const toonTealMaterial = new MeshToonMaterial({
     type="checkbox"
   />
   <TresCanvas
+    v-if="sphereExists"
     ref="canvasRef"
     v-bind="state"
     @render="onRender"

+ 21 - 0
playground/src/pages/issues/796/TheBox.vue

@@ -0,0 +1,21 @@
+<!-- eslint-disable no-console -->
+<script setup lang="ts">
+import { onMounted, onUnmounted } from 'vue'
+
+console.log('BOX--INIT:', Date.now())
+
+onMounted(() => {
+  console.log('BOX--MOUNTED', Date.now())
+})
+
+onUnmounted(() => {
+  console.log('BOX--UNMOUNTED', Date.now())
+})
+</script>
+
+<template>
+  <TresMesh>
+    <TresBoxGeometry />
+    <TresMeshNormalMaterial />
+  </TresMesh>
+</template>

+ 8 - 0
playground/src/pages/issues/796/TheExperience.vue

@@ -0,0 +1,8 @@
+<script setup lang="ts">
+import TheBox from './TheBox.vue'
+</script>
+
+<template>
+  <TresPerspectiveCamera :position="[5, 5, 5]" :look-at="[0, 0, 0]" />
+  <TheBox />
+</template>

+ 22 - 0
playground/src/pages/issues/796/index.vue

@@ -0,0 +1,22 @@
+<script setup lang="ts">
+import { TresCanvas } from '@tresjs/core'
+import TheExperience from './TheExperience.vue'
+
+const gl = {
+  clearColor: '#82DBC5',
+  shadows: true,
+  alpha: false,
+}
+
+const showCanvas = ref(true)
+</script>
+
+<template>
+  <input
+    v-model="showCanvas"
+    type="checkbox"
+  />
+  <TresCanvas v-if="showCanvas" v-bind="gl">
+    <TheExperience />
+  </TresCanvas>
+</template>

+ 6 - 0
playground/src/router/routes/issues.ts

@@ -19,4 +19,10 @@ export const issuesRoutes = [
     name: '#749: attach-detach',
     component: () => import('../../pages/issues/749/index.vue'),
   },
+  {
+    path: '/issues/796',
+    name: '#796: unmounted',
+    component: () => import('../../pages/issues/796/index.vue'),
+  },
+
 ]

+ 16 - 9
src/components/TresCanvas.vue

@@ -110,7 +110,7 @@ const scene = shallowRef<TresScene | Scene>(new Scene())
 const instance = getCurrentInstance()?.appContext.app
 extend(THREE)
 
-const createInternalComponent = (context: TresContext) =>
+const createInternalComponent = (context: TresContext, empty = false) =>
   defineComponent({
     setup() {
       const ctx = getCurrentInstance()?.appContext
@@ -121,12 +121,12 @@ const createInternalComponent = (context: TresContext) =>
       if (typeof window !== 'undefined') {
         registerTresDevtools(ctx?.app, context)
       }
-      return () => h(Fragment, null, slots?.default ? slots.default() : [])
+      return () => h(Fragment, null, !empty ? slots.default() : [])
     },
   })
 
-const mountCustomRenderer = (context: TresContext) => {
-  const InternalComponent = createInternalComponent(context)
+const mountCustomRenderer = (context: TresContext, empty = false) => {
+  const InternalComponent = createInternalComponent(context, empty)
   const { render } = createRenderer(nodeOps(context))
   render(h(InternalComponent), scene.value as unknown as TresObject)
 }
@@ -141,7 +141,6 @@ const dispose = (context: TresContext, force = false) => {
   (scene.value as TresScene).__tres = {
     root: context,
   }
-  mountCustomRenderer(context)
 }
 
 const disableRender = computed(() => props.disableRender)
@@ -150,6 +149,16 @@ const context = shallowRef<TresContext | null>(null)
 
 defineExpose({ context, dispose: () => dispose(context.value as TresContext, true) })
 
+const handleHMR = (context: TresContext) => {
+  dispose(context)
+  mountCustomRenderer(context)
+}
+
+const unmountCanvas = () => {
+  dispose(context.value as TresContext)
+  mountCustomRenderer(context.value as TresContext, true)
+}
+
 onMounted(() => {
   const existingCanvas = canvas as Ref<HTMLCanvasElement>
 
@@ -209,12 +218,10 @@ onMounted(() => {
   }
 
   // HMR support
-  if (import.meta.hot && context.value) { import.meta.hot.on('vite:afterUpdate', () => dispose(context.value as TresContext)) }
+  if (import.meta.hot && context.value) { import.meta.hot.on('vite:afterUpdate', () => handleHMR(context.value as TresContext)) }
 })
 
-onUnmounted(() => {
-  dispose(context.value as TresContext)
-})
+onUnmounted(unmountCanvas)
 </script>
 
 <template>