@@ -42,7 +42,11 @@ export class BookScene {
4242 private maxPixelRatio : number = / i P h o n e | i P a d | i P o d / 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