Skip to content

Commit e83a69c

Browse files
committed
add playOpeningAnimation
1 parent 20c71e9 commit e83a69c

File tree

2 files changed

+89
-27
lines changed

2 files changed

+89
-27
lines changed

src/components/Book.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@
5050
}
5151
5252
function onPointerDown(event: PointerEvent) {
53+
if (!bookScene?.openingAnimationPlayed) {
54+
bookScene?.playOpeningAnimation();
55+
return;
56+
}
57+
5358
gsap.killTweensOf(progressTween);
5459
isDragging = true;
5560
startX = event.clientX;

src/components/BookScene.ts

Lines changed: 84 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ export class BookScene {
4242
private maxPixelRatio: number = /iPhone|iPad|iPod/i.test(navigator.userAgent) ? 3 : 2;
4343
private normalCameraZ: number = 6;
4444
private closedCameraZ: number = 4;
45-
private initialCameraOffset = new THREE.Vector3(-1, -6, -2);
45+
46+
private initialCameraOffset = new THREE.Vector3(5, -5, -4);
47+
private initialCameraUp = new THREE.Vector3(-2, 3, 3).normalize();
48+
49+
public openingAnimationStatus: 'none' | 'playing' | 'played' = 'none';
4650

4751
constructor (container: HTMLDivElement) {
4852
this.container = container;
@@ -52,7 +56,6 @@ export class BookScene {
5256
this.scene.add(this.book);
5357

5458
this.camera = new THREE.PerspectiveCamera(45, container.clientWidth / container.clientHeight, 0.1, 1000);
55-
this.camera.lookAt(0, 0, 0);
5659

5760
this.renderer = new THREE.WebGLRenderer({ antialias: !this.isMobile, alpha: true, logarithmicDepthBuffer: false });
5861
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, this.maxPixelRatio));
@@ -71,6 +74,15 @@ export class BookScene {
7174
window.addEventListener('resize', () => this.handleResize());
7275
this.handleResize();
7376

77+
this.camera.position.add(this.initialCameraOffset);
78+
this.camera.up.copy(this.initialCameraUp);
79+
this.camera.lookAt(0, 0, 0);
80+
81+
// helper
82+
// const axesHelper = new THREE.AxesHelper(5);
83+
// this.scene.add(axesHelper);
84+
85+
7486
this.renderer.domElement.addEventListener('click', this._onIconClick.bind(this), false);
7587
this.videoOverlayManager = new VideoOverlayManager(
7688
() => this.pause(),
@@ -131,7 +143,6 @@ export class BookScene {
131143

132144
for (let i = 0;i < config.numPages;i++) {
133145
const page = this._createPage(i, textureLoader);
134-
page.position.z = (config.numPages - i) * config.pageDepth;
135146

136147
this.book.add(page);
137148
this.pages.push(page);
@@ -145,39 +156,68 @@ export class BookScene {
145156
this.renderer.render(this.scene, this.camera);
146157
}
147158

159+
public playOpeningAnimation() {
160+
if (this.openingAnimationStatus !== 'none') return;
161+
162+
this.openingAnimationStatus = 'playing';
163+
164+
const targetCameraY = this.getCameraTargetY();
165+
const targetPosition = {
166+
x: config.pageWidth / 2,
167+
y: targetCameraY,
168+
z: this.isMobile ? this.closedCameraZ : this.normalCameraZ,
169+
};
170+
const targetLookAt = new THREE.Vector3(config.pageWidth / 2, targetCameraY, 0);
171+
const targetUp = new THREE.Vector3(0, 1, 0);
172+
173+
const currentLookAt = new THREE.Vector3(0, 0, 0);
174+
const currentUp = this.camera.up.clone();
175+
176+
const tl = gsap.timeline({
177+
onComplete: () => {
178+
this.openingAnimationStatus = 'played';
179+
},
180+
onUpdate: () => {
181+
this.camera.up.copy(currentUp);
182+
this.camera.lookAt(currentLookAt);
183+
},
184+
defaults: {
185+
duration: 2.5,
186+
ease: 'power3.inOut',
187+
},
188+
});
189+
190+
const startTime = 0;
191+
192+
tl.to(this.camera.position, { ...targetPosition }, startTime);
193+
tl.to(currentLookAt, { ...targetLookAt }, startTime);
194+
tl.to(currentUp, { ...targetUp }, startTime);
195+
}
196+
197+
get openingAnimationPlayed() {
198+
return this.openingAnimationStatus === 'played';
199+
}
200+
148201
public update(progress: number) {
149202
if (!this.pages.length) return;
203+
204+
if (!this.openingAnimationPlayed) {
205+
return;
206+
}
207+
150208
this.updateBgColor(progress);
151209

152210
const perSegment = this.perSegment;
153211
const pageRotations: number[] = [];
154212
const pProgress = progress / perSegment;
155213

156-
157214
if (progress < perSegment) {
158215
this.camera.position.x = THREE.MathUtils.lerp(config.pageWidth / 2, 0, pProgress);
159216

160217
if (this.isMobile) {
161218
this.camera.position.z = THREE.MathUtils.lerp(this.closedCameraZ, this.normalCameraZ, pProgress);
162219
}
163220
}
164-
// if (progress < perSegment) {
165-
// const endPosition = {
166-
// x: 0,
167-
// y: this.isMobile ? this.camera.position.y : 0,
168-
// z: this.normalCameraZ
169-
// };
170-
// const startPosition = {
171-
// x: endPosition.x + this.initialCameraOffset.x,
172-
// y: endPosition.y + this.initialCameraOffset.y,
173-
// z: endPosition.z + this.initialCameraOffset.z
174-
// };
175-
176-
// this.camera.position.x = THREE.MathUtils.lerp(startPosition.x, startPosition.x, pProgress);
177-
// this.camera.position.y = THREE.MathUtils.lerp(startPosition.y, startPosition.y, pProgress);
178-
// this.camera.position.z = THREE.MathUtils.lerp(startPosition.z, startPosition.z, pProgress);
179-
// }
180-
// this.camera.lookAt(0, 0, 0);
181221

182222
this.currentPage = Math.round(pProgress);
183223

@@ -187,6 +227,7 @@ export class BookScene {
187227
const segmentStartProgress = i * perSegment;
188228
const flipProgress = Math.max(0, Math.min(1, pProgress - (segmentStartProgress) / perSegment));
189229
const flipRotation = -flipProgress * Math.PI;
230+
190231
pageRotations.push(flipRotation);
191232

192233
page.rotation.y = flipRotation;
@@ -230,6 +271,19 @@ export class BookScene {
230271

231272
}
232273

274+
private getCameraTargetY(): number {
275+
const width = this.container.clientWidth;
276+
const height = this.container.clientHeight;
277+
278+
const widthPercentage = 0.95;
279+
const visibleWidth = config.pageWidth * 2 / widthPercentage;
280+
const visibleHeight = visibleWidth / this.camera.aspect;
281+
282+
return height < width
283+
? 0
284+
: -0.25 * (visibleHeight - config.pageHeight);
285+
}
286+
233287

234288
private updateBgColor(progress: number) {
235289
const now = performance.now();
@@ -251,6 +305,7 @@ export class BookScene {
251305
this.renderer.setClearColor(color);
252306
document.documentElement.style.setProperty('--bgColor', color.getStyle());
253307
}
308+
254309
public handleResize() {
255310
const width = this.container.clientWidth;
256311
const height = this.container.clientHeight;
@@ -276,8 +331,10 @@ export class BookScene {
276331
this.camera.position.z = this.normalCameraZ;
277332
}
278333

279-
this.camera.position.y = height < width ? 0 : -0.25 * (visibleHeight - config.pageHeight);
334+
if (this.openingAnimationPlayed) {
335+
this.camera.position.y = this.getCameraTargetY();
280336

337+
}
281338
this.camera.updateProjectionMatrix();
282339
this._updateIconGroupPosition();
283340
}
@@ -337,8 +394,8 @@ export class BookScene {
337394
const pageMesh = new THREE.Mesh(geometry, [
338395
new THREE.MeshStandardMaterial({ map: frontTexture }),
339396
new THREE.MeshStandardMaterial({ map: backTexture }),
340-
new THREE.MeshStandardMaterial({ map: frontTexture }),
341397
new THREE.MeshStandardMaterial({ map: backTexture }),
398+
new THREE.MeshStandardMaterial({ map: frontTexture }),
342399
new THREE.MeshStandardMaterial({ ...fMaterialConfig, map: frontTexture }),
343400
new THREE.MeshStandardMaterial({ ...bMaterialConfig, map: backTexture })
344401
]);
@@ -351,6 +408,9 @@ export class BookScene {
351408
this.pages[i - 1]?.add(pair.back);
352409
});
353410
this.decorationPairs[i] = pairs;
411+
412+
pivot.position.z = (config.numPages - i) * config.pageDepth;
413+
354414
return pivot;
355415
}
356416

@@ -508,9 +568,6 @@ export class BookScene {
508568
}
509569
}
510570

511-
512-
513-
514571
private updateIcons() {
515572
if (!this.videoIcon || !this.audioIcon) return;
516573

@@ -544,7 +601,7 @@ export class BookScene {
544601

545602
if (intersects.length > 0) {
546603
const clickedObject = intersects[0].object;
547-
604+
548605
if (clickedObject === this.videoIcon) {
549606
this.videoOverlayManager.show(assets.videos[this.currentPage.toString()] || '');
550607
} else if (clickedObject === this.audioIcon) {

0 commit comments

Comments
 (0)