feat(nds): intro animation
This commit is contained in:
@@ -1,6 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { useLoop, useTresContext } from "@tresjs/core";
|
||||
import * as THREE from "three";
|
||||
import gsap from "gsap";
|
||||
|
||||
const INTRO_ANIMATION = {
|
||||
MODEL_SPIN_DURATION: 3,
|
||||
LID_OPEN_DURATION: 2,
|
||||
LID_OPEN_OVERLAP: 1.5,
|
||||
|
||||
CAMERA_START_POSITION: new THREE.Vector3(0, 15, 2.6),
|
||||
CAMERA_START_ROTATION: new THREE.Euler(THREE.MathUtils.degToRad(-90), 0, 0),
|
||||
CAMERA_END_POSITION: new THREE.Vector3(0, 14, 10),
|
||||
CAMERA_END_ROTATION: new THREE.Euler(THREE.MathUtils.degToRad(-55), 0, 0),
|
||||
CAMERA_DURATION: 3,
|
||||
|
||||
LID_CLOSED_ROTATION: THREE.MathUtils.degToRad(120),
|
||||
LID_OPENED_ROTATION: THREE.MathUtils.degToRad(-30),
|
||||
};
|
||||
|
||||
const props = defineProps<{
|
||||
topScreenCanvas: HTMLCanvasElement | null;
|
||||
@@ -8,6 +24,7 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const { assets } = useAssets();
|
||||
const app = useAppStore();
|
||||
|
||||
const model = assets.nintendoDs.scene.clone(true);
|
||||
|
||||
@@ -18,6 +35,7 @@ let bottomScreenTexture: THREE.CanvasTexture | null = null;
|
||||
// screens
|
||||
const TOP_SCREEN = "Object_9";
|
||||
const BOTTOM_SCREEN = "Object_28";
|
||||
const LID = "Object_8";
|
||||
|
||||
// buttons
|
||||
const CROSS_BUTTON = "Object_21";
|
||||
@@ -41,11 +59,20 @@ const { camera, renderer } = useTresContext();
|
||||
|
||||
model.scale.set(100, 100, 100);
|
||||
|
||||
const app = useAppStore();
|
||||
watch(
|
||||
() => camera.activeCamera.value,
|
||||
(cam) => {
|
||||
if (cam) app.setCamera(cam);
|
||||
if (cam) {
|
||||
app.setCamera(cam);
|
||||
|
||||
if (app.booted) {
|
||||
cam.position.copy(INTRO_ANIMATION.CAMERA_END_POSITION);
|
||||
cam.rotation.copy(INTRO_ANIMATION.CAMERA_END_ROTATION);
|
||||
} else {
|
||||
cam.position.copy(INTRO_ANIMATION.CAMERA_START_POSITION);
|
||||
cam.rotation.copy(INTRO_ANIMATION.CAMERA_START_ROTATION);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
@@ -57,6 +84,80 @@ model.traverse((child) => {
|
||||
}
|
||||
});
|
||||
|
||||
const lidMesh = requireMesh(LID);
|
||||
const topScreenMesh = requireMesh(TOP_SCREEN);
|
||||
|
||||
if (app.booted) {
|
||||
lidMesh.rotation.x = INTRO_ANIMATION.LID_OPENED_ROTATION;
|
||||
topScreenMesh.rotation.x = INTRO_ANIMATION.LID_OPENED_ROTATION;
|
||||
} else {
|
||||
lidMesh.rotation.x = INTRO_ANIMATION.LID_CLOSED_ROTATION;
|
||||
topScreenMesh.rotation.x = INTRO_ANIMATION.LID_CLOSED_ROTATION;
|
||||
}
|
||||
|
||||
const hasAnimated = ref(app.booted);
|
||||
|
||||
const animateIntro = () => {
|
||||
if (hasAnimated.value) return;
|
||||
hasAnimated.value = true;
|
||||
|
||||
const introTimeline = gsap.timeline();
|
||||
|
||||
introTimeline.to(
|
||||
camera.activeCamera.value.position,
|
||||
{
|
||||
x: INTRO_ANIMATION.CAMERA_END_POSITION.x,
|
||||
y: INTRO_ANIMATION.CAMERA_END_POSITION.y,
|
||||
z: INTRO_ANIMATION.CAMERA_END_POSITION.z,
|
||||
duration: INTRO_ANIMATION.CAMERA_DURATION,
|
||||
ease: "power2.inOut",
|
||||
},
|
||||
0,
|
||||
);
|
||||
|
||||
introTimeline.to(
|
||||
camera.activeCamera.value.rotation,
|
||||
{
|
||||
x: INTRO_ANIMATION.CAMERA_END_ROTATION.x,
|
||||
y: INTRO_ANIMATION.CAMERA_END_ROTATION.y,
|
||||
z: INTRO_ANIMATION.CAMERA_END_ROTATION.z,
|
||||
duration: INTRO_ANIMATION.CAMERA_DURATION,
|
||||
ease: "power2.inOut",
|
||||
},
|
||||
0,
|
||||
);
|
||||
|
||||
introTimeline.to(
|
||||
model.rotation,
|
||||
{
|
||||
y: Math.PI * 2,
|
||||
duration: INTRO_ANIMATION.MODEL_SPIN_DURATION,
|
||||
ease: "power2.inOut",
|
||||
},
|
||||
0,
|
||||
);
|
||||
|
||||
introTimeline.to(
|
||||
lidMesh.rotation,
|
||||
{
|
||||
x: INTRO_ANIMATION.LID_OPENED_ROTATION,
|
||||
duration: INTRO_ANIMATION.LID_OPEN_DURATION,
|
||||
ease: "power2.out",
|
||||
},
|
||||
INTRO_ANIMATION.MODEL_SPIN_DURATION - INTRO_ANIMATION.LID_OPEN_OVERLAP,
|
||||
);
|
||||
|
||||
introTimeline.to(
|
||||
topScreenMesh.rotation,
|
||||
{
|
||||
x: INTRO_ANIMATION.LID_OPENED_ROTATION,
|
||||
duration: INTRO_ANIMATION.LID_OPEN_DURATION,
|
||||
ease: "power2.out",
|
||||
},
|
||||
"<",
|
||||
);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [props.topScreenCanvas, props.bottomScreenCanvas],
|
||||
() => {
|
||||
@@ -189,6 +290,11 @@ const pressButton = (button: string) => {
|
||||
};
|
||||
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
if (!hasAnimated.value) {
|
||||
animateIntro();
|
||||
return;
|
||||
}
|
||||
|
||||
const domElement = renderer.instance.domElement;
|
||||
const rect = domElement.getBoundingClientRect();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user