N
Nuxt2y ago
Furnaxe

Nuxt3 and Three.js bug

Hello, I have a little problem or I can't find the answer anywhere on the internet (stackoverflow, youtube, etc). I try to make a planet appear in three.js but when I change page with NuxtLink and then I come back to my page where there is my planet, it disappears and does not show any error, I noticed that if I readjust my window in size it reappears. Anyone have an idea where this is coming from? I give you the code below.
<template>
<div ref="container" class="w-full sm:h-[30rem] lg:block hidden relative overflow-hidden">
<canvas id="canvas" ref="canvas"></canvas>
</div>
</template>

<script setup lang="ts">
import { onMounted, ref, onBeforeUnmount } from 'vue';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

const container: { value: HTMLElement | null } = ref(null);
const canvas: { value: HTMLCanvasElement | null } = ref(null);

onMounted(() => {
if (container.value && canvas.value) {
setupWebGL(container.value, canvas.value);
}
});

function setupWebGL(container: HTMLElement, canvas: HTMLCanvasElement) {
const scene = new THREE.Scene();

const light = new THREE.AmbientLight(0xffffff, 2.8)
scene.add(light)

const camera = new THREE.PerspectiveCamera(
20,
container.clientWidth / container.clientHeight,
0.1,
200
);
camera.position.set(-4, 3, 6);

const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
preserveDrawingBuffer: true,
alpha: true,
});
renderer.setSize(container.clientWidth, container.clientHeight);

const controls = new OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
controls.enablePan = false;
controls.enableZoom = false;
controls.maxPolarAngle = Math.PI / 2;
controls.minPolarAngle = Math.PI / 2;

const loader = new GLTFLoader();
loader.load(
'/models/planet.gltf',
(gltf) => {
gltf.scene.traverse((child) => {
if (child instanceof THREE.Mesh) {
child.material = new THREE.MeshStandardMaterial({
color: child.material.color,
map: child.material.map,
});
}
});

const earthMesh = gltf.scene;
earthMesh.scale.set(1.25, 1.25, 1.25);
scene.add(earthMesh);
animate();
},
undefined,
(error) => {
console.error('An error occurred while loading the model:', error);
}
);

function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}

function onWindowResize() {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
}

window.addEventListener('resize', onWindowResize);

onBeforeUnmount(() => {
window.removeEventListener('resize', onWindowResize);
});
}
</script>

<style scoped>
.earth-container {
width: 100%;
height: 500px;
overflow: hidden;
position: relative;
}
</style>
<template>
<div ref="container" class="w-full sm:h-[30rem] lg:block hidden relative overflow-hidden">
<canvas id="canvas" ref="canvas"></canvas>
</div>
</template>

<script setup lang="ts">
import { onMounted, ref, onBeforeUnmount } from 'vue';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

const container: { value: HTMLElement | null } = ref(null);
const canvas: { value: HTMLCanvasElement | null } = ref(null);

onMounted(() => {
if (container.value && canvas.value) {
setupWebGL(container.value, canvas.value);
}
});

function setupWebGL(container: HTMLElement, canvas: HTMLCanvasElement) {
const scene = new THREE.Scene();

const light = new THREE.AmbientLight(0xffffff, 2.8)
scene.add(light)

const camera = new THREE.PerspectiveCamera(
20,
container.clientWidth / container.clientHeight,
0.1,
200
);
camera.position.set(-4, 3, 6);

const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
preserveDrawingBuffer: true,
alpha: true,
});
renderer.setSize(container.clientWidth, container.clientHeight);

const controls = new OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
controls.enablePan = false;
controls.enableZoom = false;
controls.maxPolarAngle = Math.PI / 2;
controls.minPolarAngle = Math.PI / 2;

const loader = new GLTFLoader();
loader.load(
'/models/planet.gltf',
(gltf) => {
gltf.scene.traverse((child) => {
if (child instanceof THREE.Mesh) {
child.material = new THREE.MeshStandardMaterial({
color: child.material.color,
map: child.material.map,
});
}
});

const earthMesh = gltf.scene;
earthMesh.scale.set(1.25, 1.25, 1.25);
scene.add(earthMesh);
animate();
},
undefined,
(error) => {
console.error('An error occurred while loading the model:', error);
}
);

function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}

function onWindowResize() {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
}

window.addEventListener('resize', onWindowResize);

onBeforeUnmount(() => {
window.removeEventListener('resize', onWindowResize);
});
}
</script>

<style scoped>
.earth-container {
width: 100%;
height: 500px;
overflow: hidden;
position: relative;
}
</style>
9 Replies
TheSpik3
TheSpik32y ago
I guess you need to make the function inside onMounted async and add await nextTick() in the beginning of it, since elements are available only after nextTick in script setup 😅
Furnaxe
FurnaxeOP2y ago
I try this :
onMounted(async () => {
await nextTick();
if (container.value && canvas.value) {
setupWebGL(container.value, canvas.value);
}
});
onMounted(async () => {
await nextTick();
if (container.value && canvas.value) {
setupWebGL(container.value, canvas.value);
}
});
And my planet still does not appear :/
TheSpik3
TheSpik32y ago
So I tried your original code and it works, weird nuxt version is 3.4.1 and three version is 0.151.3
Furnaxe
FurnaxeOP2y ago
So when you use NuxtLink and you leave the page and come back to the original one you don't have any problems to see the planet?
TheSpik3
TheSpik32y ago
No (i have different model, but its loading fine) Are you coming back to the page via back button or another NuxtLink?
Furnaxe
FurnaxeOP2y ago
By NuxtLink But if I touch the size of my window or open the inspector the planet element appears
TheSpik3
TheSpik32y ago
Hmm, still works, the model takes some time to load, but its working
Furnaxe
FurnaxeOP2y ago
I don't understand where it comes from knowing that for example when I trigger a 404 and I come back to my model page it loads well
Unknown User
Unknown User2y ago
Message Not Public
Sign In & Join Server To View
Want results from more Discord servers?
Add your server