213 lines
5.4 KiB
TypeScript
213 lines
5.4 KiB
TypeScript
import gsap from "gsap";
|
|
import * as THREE from "three";
|
|
|
|
const ANIMATION = {
|
|
FADE_DURATION: 1,
|
|
FADE_CAMERA_OVERLAP: 0.9,
|
|
NAVIGATE_DELAY: 3150,
|
|
|
|
// 3D zoom
|
|
NDS_CAMERA_POSITION: new THREE.Vector3(0, 14, 10),
|
|
NDS_CAMERA_ROTATION: new THREE.Euler(THREE.MathUtils.degToRad(-55), 0, 0),
|
|
GALLERY_CAMERA_POSITION: new THREE.Vector3(0, 4.5, -3),
|
|
GALLERY_CAMERA_ROTATION: new THREE.Euler(THREE.MathUtils.degToRad(-62), 0, 0),
|
|
CAMERA_DURATION_INTRO: 2.115,
|
|
CAMERA_DURATION_OUTRO: 2.037,
|
|
CAMERA_ROTATION_OVERLAP: 0.1,
|
|
|
|
// 2D zoom
|
|
ZOOM_SCALE: 6,
|
|
ZOOM_DURATION_INTRO: 2.115,
|
|
ZOOM_DURATION_OUTRO: 2.037,
|
|
ZOOM_EASE: "power2.inOut",
|
|
};
|
|
|
|
export const useGalleryStore = defineStore("gallery", {
|
|
state: () => ({
|
|
intro: {
|
|
fadeOpacity: 0,
|
|
},
|
|
|
|
outro: {
|
|
fadeOpacity: 1,
|
|
},
|
|
|
|
zoom: {
|
|
scale: 1,
|
|
},
|
|
|
|
isIntro: true,
|
|
isOutro: false,
|
|
shouldAnimateOutro: false,
|
|
}),
|
|
|
|
actions: {
|
|
cleanup() {
|
|
gsap.killTweensOf(this.zoom);
|
|
gsap.killTweensOf(this.intro);
|
|
gsap.killTweensOf(this.outro);
|
|
},
|
|
|
|
animateIntro() {
|
|
this.isIntro = true;
|
|
this.isOutro = false;
|
|
|
|
const app = useAppStore();
|
|
app.disallowHints();
|
|
|
|
// Intro: Fade starts first (at 0), camera/zoom starts after with overlap
|
|
const zoomDelay = ANIMATION.FADE_DURATION - ANIMATION.FADE_CAMERA_OVERLAP;
|
|
|
|
gsap.fromTo(
|
|
this.intro,
|
|
{ fadeOpacity: 0 },
|
|
{
|
|
fadeOpacity: 1,
|
|
duration: ANIMATION.FADE_DURATION,
|
|
delay: 0,
|
|
ease: "none",
|
|
},
|
|
);
|
|
|
|
if (app.camera) {
|
|
gsap.fromTo(
|
|
app.camera.position,
|
|
{
|
|
x: ANIMATION.NDS_CAMERA_POSITION.x,
|
|
y: ANIMATION.NDS_CAMERA_POSITION.y,
|
|
z: ANIMATION.NDS_CAMERA_POSITION.z,
|
|
},
|
|
{
|
|
x: ANIMATION.GALLERY_CAMERA_POSITION.x,
|
|
y: ANIMATION.GALLERY_CAMERA_POSITION.y,
|
|
z: ANIMATION.GALLERY_CAMERA_POSITION.z,
|
|
duration: ANIMATION.CAMERA_DURATION_INTRO,
|
|
delay: zoomDelay,
|
|
ease: ANIMATION.ZOOM_EASE,
|
|
},
|
|
);
|
|
|
|
gsap.fromTo(
|
|
app.camera.rotation,
|
|
{
|
|
x: ANIMATION.NDS_CAMERA_ROTATION.x,
|
|
y: ANIMATION.NDS_CAMERA_ROTATION.y,
|
|
z: ANIMATION.NDS_CAMERA_ROTATION.z,
|
|
},
|
|
{
|
|
x: ANIMATION.GALLERY_CAMERA_ROTATION.x,
|
|
y: ANIMATION.GALLERY_CAMERA_ROTATION.y,
|
|
z: ANIMATION.GALLERY_CAMERA_ROTATION.z,
|
|
duration:
|
|
ANIMATION.CAMERA_DURATION_INTRO *
|
|
(1 - ANIMATION.CAMERA_ROTATION_OVERLAP),
|
|
delay: zoomDelay,
|
|
ease: ANIMATION.ZOOM_EASE,
|
|
},
|
|
);
|
|
} else {
|
|
gsap.fromTo(
|
|
this.zoom,
|
|
{ scale: 1 },
|
|
{
|
|
scale: ANIMATION.ZOOM_SCALE,
|
|
duration: ANIMATION.ZOOM_DURATION_INTRO,
|
|
delay: zoomDelay,
|
|
ease: ANIMATION.ZOOM_EASE,
|
|
},
|
|
);
|
|
}
|
|
|
|
setTimeout(() => {
|
|
this.shouldAnimateOutro = true;
|
|
navigateTo("/gallery");
|
|
}, ANIMATION.NAVIGATE_DELAY);
|
|
|
|
setTimeout(() => {
|
|
const { assets } = useAssets();
|
|
assets.audio.whooshSmall.play(0.6);
|
|
}, 100);
|
|
},
|
|
|
|
animateOutro() {
|
|
this.isIntro = false;
|
|
this.isOutro = true;
|
|
|
|
const app = useAppStore();
|
|
|
|
// Outro: Camera/zoom starts first (at 0), fade starts after with overlap
|
|
const fadeDelay =
|
|
ANIMATION.CAMERA_DURATION_OUTRO - ANIMATION.FADE_CAMERA_OVERLAP;
|
|
|
|
if (app.camera) {
|
|
gsap.fromTo(
|
|
app.camera.position,
|
|
{
|
|
x: ANIMATION.GALLERY_CAMERA_POSITION.x,
|
|
y: ANIMATION.GALLERY_CAMERA_POSITION.y,
|
|
z: ANIMATION.GALLERY_CAMERA_POSITION.z,
|
|
},
|
|
{
|
|
x: ANIMATION.NDS_CAMERA_POSITION.x,
|
|
y: ANIMATION.NDS_CAMERA_POSITION.y,
|
|
z: ANIMATION.NDS_CAMERA_POSITION.z,
|
|
duration: ANIMATION.CAMERA_DURATION_OUTRO,
|
|
delay: 0,
|
|
ease: ANIMATION.ZOOM_EASE,
|
|
},
|
|
);
|
|
|
|
gsap.fromTo(
|
|
app.camera.rotation,
|
|
{
|
|
x: ANIMATION.GALLERY_CAMERA_ROTATION.x,
|
|
y: ANIMATION.GALLERY_CAMERA_ROTATION.y,
|
|
z: ANIMATION.GALLERY_CAMERA_ROTATION.z,
|
|
},
|
|
{
|
|
x: ANIMATION.NDS_CAMERA_ROTATION.x,
|
|
y: ANIMATION.NDS_CAMERA_ROTATION.y,
|
|
z: ANIMATION.NDS_CAMERA_ROTATION.z,
|
|
duration:
|
|
ANIMATION.CAMERA_DURATION_OUTRO *
|
|
(1 - ANIMATION.CAMERA_ROTATION_OVERLAP),
|
|
delay: 0,
|
|
ease: ANIMATION.ZOOM_EASE,
|
|
},
|
|
);
|
|
} else {
|
|
gsap.fromTo(
|
|
this.zoom,
|
|
{ scale: ANIMATION.ZOOM_SCALE },
|
|
{
|
|
scale: 1,
|
|
duration: ANIMATION.ZOOM_DURATION_OUTRO,
|
|
delay: 0,
|
|
ease: ANIMATION.ZOOM_EASE,
|
|
},
|
|
);
|
|
}
|
|
|
|
gsap.fromTo(
|
|
this.outro,
|
|
{ fadeOpacity: 1 },
|
|
{
|
|
fadeOpacity: 0,
|
|
duration: ANIMATION.FADE_DURATION,
|
|
delay: fadeDelay,
|
|
ease: "none",
|
|
},
|
|
);
|
|
|
|
setTimeout(() => {
|
|
this.isOutro = false;
|
|
app.navigateTo("home");
|
|
app.allowHints();
|
|
}, ANIMATION.NAVIGATE_DELAY);
|
|
|
|
const { assets } = useAssets();
|
|
assets.audio.whooshSmallReverse.play(0.6);
|
|
},
|
|
},
|
|
});
|