22gsap . registerPlugin ( ScrollTrigger ) ;
33
44// --------------------------------------------------------------------
5- // 1. THIẾT LẬP CẢNH 3D CƠ BẢN
5+ // 1. THIẾT LẬP CẢNH 3D
66// --------------------------------------------------------------------
77const scene = new THREE . Scene ( ) ;
88const camera = new THREE . PerspectiveCamera ( 75 , window . innerWidth / window . innerHeight , 0.1 , 1000 ) ;
9- // Vẫn giữ camera ở xa để đảm bảo thấy mô hình lúc đầu
109camera . position . set ( 0 , 5 , 30 ) ;
1110
1211const renderer = new THREE . WebGLRenderer ( {
@@ -17,31 +16,30 @@ const renderer = new THREE.WebGLRenderer({
1716renderer . setSize ( window . innerWidth , window . innerHeight ) ;
1817renderer . setPixelRatio ( Math . min ( window . devicePixelRatio , 2 ) ) ;
1918
19+ // ***** KHỞI TẠO ORBIT CONTROLS *****
20+ // Truyền vào camera và canvas để nó biết cần điều khiển cái gì và lắng nghe sự kiện ở đâu
21+ const controls = new THREE . OrbitControls ( camera , renderer . domElement ) ;
22+ controls . enableDamping = true ; // Tạo ra hiệu ứng "quán tính" mượt mà khi xoay
23+ controls . enablePan = false ; // Tắt tính năng kéo (pan) để người dùng không kéo Kirov ra khỏi màn hình
24+ controls . minDistance = 10 ; // Ngăn người dùng zoom quá gần
25+ controls . maxDistance = 150 ; // Ngăn người dùng zoom quá xa
26+
27+ // Ánh sáng và Skybox (giữ nguyên)
2028const ambientLight = new THREE . AmbientLight ( 0xffffff , 0.8 ) ;
2129scene . add ( ambientLight ) ;
2230const directionalLight = new THREE . DirectionalLight ( 0xffffff , 1.5 ) ;
23- directionalLight . position . set ( 10 , 10 , 5 ) ; // Thay đổi hướng sáng một chút
31+ directionalLight . position . set ( 10 , 10 , 5 ) ;
2432scene . add ( directionalLight ) ;
2533
26- // --------------------------------------------------------------------
27- // THÊM SKYBOX
28- // --------------------------------------------------------------------
2934const cubeTextureLoader = new THREE . CubeTextureLoader ( ) ;
3035cubeTextureLoader . setPath ( 'air-skybox/' ) ;
31-
32- // ***** THAY ĐỔI DUY NHẤT: CẬP NHẬT TÊN FILE SKYBOX MỚI *****
33- // Chúng ta thay đổi tên file để khớp với bộ ảnh bạn vừa tải về.
3436const textureCube = cubeTextureLoader . load ( [
3537 'px.png' , 'nx.png' , 'py.png' , 'ny.png' , 'pz.png' , 'nz.png'
3638] ) ;
37-
38- // Đặt skybox làm background cho scene
3939scene . background = textureCube ;
40- // ***** KẾT THÚC THAY ĐỔI *****
41-
4240
4341// --------------------------------------------------------------------
44- // 2. TẢI MÔ HÌNH 3D MỚI
42+ // 2. TẢI MÔ HÌNH (Giữ nguyên)
4543// --------------------------------------------------------------------
4644const loader = new THREE . GLTFLoader ( ) ;
4745let newModel ;
@@ -50,11 +48,9 @@ loader.load(
5048 'models/air_ship_1K.glb' ,
5149 function ( gltf ) {
5250 newModel = gltf . scene ;
53-
5451 newModel . position . set ( 0 , - 2 , 0 ) ;
5552 newModel . scale . set ( 1 , 1 , 1 ) ;
5653 newModel . rotation . set ( 0 , Math . PI / 2 , 0 ) ;
57-
5854 scene . add ( newModel ) ;
5955 setupScrollAnimation ( ) ;
6056 } ,
@@ -63,10 +59,12 @@ loader.load(
6359) ;
6460
6561// --------------------------------------------------------------------
66- // 3. THIẾT LẬP LẠI HIỆU ỨNG CUỘN (GIỮ NGUYÊN )
62+ // 3. THIẾT LẬP HIỆU ỨNG CUỘN (CÓ THAY ĐỔI NHỎ )
6763// --------------------------------------------------------------------
6864function setupScrollAnimation ( ) {
65+ // ***** THÊM ID CHO TIMELINE ĐỂ CÓ THỂ ĐIỀU KHIỂN NÓ *****
6966 const tl = gsap . timeline ( {
67+ id : "main-timeline" , // Đặt một cái tên cho timeline
7068 scrollTrigger : {
7169 trigger : 'main' ,
7270 start : 'top top' ,
@@ -76,6 +74,7 @@ function setupScrollAnimation() {
7674 }
7775 } ) ;
7876
77+ // Các animation giữ nguyên
7978 tl
8079 . to ( camera . position , { z : 20 , y : 3 } )
8180 . to ( camera . position , { x : - 15 } )
@@ -88,14 +87,40 @@ function setupScrollAnimation() {
8887 . to ( camera . rotation , { x : 0 } , "<" ) ;
8988}
9089
90+ // ***** GIẢI QUYẾT XUNG ĐỘT GIỮA SCROLL VÀ KÉO CHUỘT *****
91+ controls . addEventListener ( 'start' , ( ) => {
92+ // Khi người dùng bắt đầu kéo chuột, vô hiệu hóa animation cuộn
93+ console . log ( "OrbitControls started, ScrollTrigger disabled." ) ;
94+ const mainTimeline = ScrollTrigger . getById ( "main-timeline" ) ;
95+ if ( mainTimeline ) {
96+ mainTimeline . disable ( ) ;
97+ }
98+ } ) ;
99+
100+ controls . addEventListener ( 'end' , ( ) => {
101+ // Khi người dùng thả chuột, kích hoạt lại animation cuộn
102+ console . log ( "OrbitControls ended, ScrollTrigger enabled." ) ;
103+ const mainTimeline = ScrollTrigger . getById ( "main-timeline" ) ;
104+ if ( mainTimeline ) {
105+ mainTimeline . enable ( ) ;
106+ }
107+ } ) ;
108+
109+
91110// --------------------------------------------------------------------
92- // 4. VÒNG LẶP RENDER VÀ RESIZE (GIỮ NGUYÊN )
111+ // 4. VÒNG LẶP RENDER (CÓ THAY ĐỔI )
93112// --------------------------------------------------------------------
94113function animate ( ) {
95114 requestAnimationFrame ( animate ) ;
115+
116+ // ***** CẬP NHẬT ORBIT CONTROLS TRONG MỖI FRAME *****
117+ // Điều này là bắt buộc nếu bạn bật `enableDamping`
118+ controls . update ( ) ;
119+
96120 renderer . render ( scene , camera ) ;
97121}
98122
123+ // Các hàm còn lại giữ nguyên
99124window . addEventListener ( 'resize' , ( ) => {
100125 camera . aspect = window . innerWidth / window . innerHeight ;
101126 camera . updateProjectionMatrix ( ) ;
0 commit comments