9-Three.js 貼圖與材質學習指南
環境準備
<!DOCTYPE html>
<html>
<head><title>Three.js Texture Demo</title><style> body { margin: 0; } </style>
</head>
<body><script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r155/three.min.js"></script><script src="https://cdn.jsdelivr.net/npm/three@0.155.0/examples/js/controls/OrbitControls.js"></script><script src="./main.js"></script>
</body>
</html>
基礎材質類型
// 基礎材質
const basicMaterial = new THREE.MeshBasicMaterial({color: 0x00ff00,wireframe: false
});// 標準物理材質
const standardMaterial = new THREE.MeshStandardMaterial({color: 0xffffff,metalness: 0.7,roughness: 0.3
});
核心知識點
1. 紋理加載
const textureLoader = new THREE.TextureLoader();
const colorMap = textureLoader.load('textures/wood_diffuse.jpg');
const normalMap = textureLoader.load('textures/wood_normal.jpg');// 帶加載管理的示例
const manager = new THREE.LoadingManager();
manager.onProgress = (url, loaded, total) => {console.log(`加載進度: ${loaded}/${total}`);
};const customLoader = new THREE.TextureLoader(manager);
2. 材質屬性配置
const material = new THREE.MeshStandardMaterial({map: colorMap, // 基礎顏色貼圖normalMap: normalMap, // 法線貼圖displacementMap: textureLoader.load('height.png'),displacementScale: 0.1,roughnessMap: textureLoader.load('roughness.jpg'),metalness: 0.5,roughness: 0.3
});
3. 環境貼圖
const cubeLoader = new THREE.CubeTextureLoader().setPath('textures/cubemap/');const envMap = cubeLoader.load(['px.jpg', 'nx.jpg','py.jpg', 'ny.jpg','pz.jpg', 'nz.jpg'
]);material.envMap = envMap;
material.envMapIntensity = 1.5;
完整示例代碼
let scene, camera, renderer;function init() {// 場景設置scene = new THREE.Scene();camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 材質創建const material = createComplexMaterial();// 幾何體const sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 64, 64),material);scene.add(sphere);// 燈光const light = new THREE.PointLight(0xffffff, 1000);light.position.set(10, 10, 10);scene.add(light);// 相機控制const controls = new THREE.OrbitControls(camera, renderer.domElement);camera.position.z = 10;animate();
}function createComplexMaterial() {const loader = new THREE.TextureLoader();return new THREE.MeshStandardMaterial({map: loader.load('diffuse.jpg'),normalMap: loader.load('normal.jpg'),roughnessMap: loader.load('roughness.jpg'),metalness: 0.5,roughness: 0.3,aoMap: loader.load('ao.jpg'),displacementMap: loader.load('height.png'),displacementScale: 0.1});
}function animate() {requestAnimationFrame(animate);renderer.render(scene, camera);
}init();
關鍵知識要點
材質類型對比
材質類型 | 特性 | 性能 | 光照支持 |
---|---|---|---|
MeshBasicMaterial | 基礎顏色顯示 | 高 | 無 |
MeshPhongMaterial | 鏡面高光 | 中 | 有 |
MeshStandardMaterial | 基于物理的渲染(PBR) | 中 | 有 |
MeshPhysicalMaterial | 擴展PBR功能(透明涂層等) | 低 | 有 |
常用貼圖類型
- 顏色貼圖 (map) - 基礎表面顏色
- 法線貼圖 (normalMap) - 表面細節凹凸
- 環境光遮蔽貼圖 (aoMap) - 環境光遮蔽效果
- 粗糙度貼圖 (roughnessMap) - 表面光滑程度
- 金屬度貼圖 (metalnessMap) - 金屬質感表現
- 置換貼圖 (displacementMap) - 真實幾何形變
優化建議
- 使用壓縮紋理格式(.basis, .ktx2)
- 合理設置紋理分辨率(避免超大尺寸)
- 復用紋理對象
- 使用mipmap提高渲染質量
- 對不可見物體禁用紋理更新
常見問題排查
-
紋理不顯示:檢查文件路徑和格式
-
法線貼圖效果異常:確認材質normalScale設置
-
性能低下:檢查紋理尺寸是否過大
-
環境貼圖失效:確認場景背景設置
-
PBR材質暗淡:確保場景中有足夠光照
紋理貼圖代碼:
import * as THREE from 'three';// 初始化場景、相機、渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);// 創建平面幾何體
const planeGeometry = new THREE.PlaneGeometry(2, 2);// 定義全局變量 plane
let plane;// 加載紋理并創建材質和網格
const textureLoader = new THREE.TextureLoader();
textureLoader.load('../public/assets/texture/2.jpg',(texture) => {const planeMaterial = new THREE.MeshBasicMaterial({ map: texture });plane = new THREE.Mesh(planeGeometry, planeMaterial); // 將 plane 賦值給全局變量scene.add(plane);// 調整相機位置camera.position.z = 5;// 啟動動畫循環animate();},(xhr) => {console.log('加載進度:', (xhr.loaded / xhr.total * 100) + '%');},(err) => {console.error('紋理加載失敗:', err);}
);// 定義動畫函數
function animate() {requestAnimationFrame(animate);if (plane) { // 確保 plane 已定義plane.rotation.x += 0.01;plane.rotation.y += 0.01;}renderer.render(scene, camera);
}
10-紋理顏色空間、霧效及基礎概念
目錄
- Three.js 基礎知識
- 紋理的顏色空間
- 霧效 (Fog)
- 示例代碼
核心概念
- 場景 (Scene):所有 3D 對象的容器。
- 相機 (Camera):用于定義觀察場景的視角。
- 渲染器 (Renderer):負責將場景渲染到屏幕上。
- 物體 (Object3D):場景中的基本元素,可以是模型、燈光等。
- 材質 (Material):定義物體表面的外觀。
- 幾何體 (Geometry):定義物體的形狀。
- 燈光 (Light):為場景提供光照。
- 紋理 (Texture):為物體表面添加圖像。
紋理的顏色空間
在 Three.js 中,紋理的顏色空間處理非常重要,因為它會影響最終的渲染效果。
sRGB 與線性顏色空間
- sRGB:標準的顏色空間,用于顯示器和圖像。
- 線性顏色空間:用于光線計算,確保物理正確的光照效果。
在 Three.js 中設置紋理顏色空間
Three.js 默認使用 sRGB 顏色空間。如果需要調整,可以通過 Texture
對象的屬性進行設置。
示例:加載紋理并設置顏色空間
<!DOCTYPE html>
<html>
<head><title>Three.js 紋理顏色空間示例</title><style>body { margin: 0; }canvas { display: block; }</style>
</head>
<body><script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script><script>const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);const renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 加載紋理const textureLoader = new THREE.TextureLoader();const texture = textureLoader.load('path/to/texture.jpg', function(texture) {// 設置紋理的顏色空間texture.encoding = THREE.sRGBEncoding; // 或 THREE.LinearEncoding});const geometry = new THREE.BoxGeometry();const material = new THREE.MeshBasicMaterial({ map: texture });const cube = new THREE.Mesh(geometry, material);scene.add(cube);camera.position.z = 5;function animate() {requestAnimationFrame(animate);cube.rotation.x += 0.01;cube.rotation.y += 0.01;renderer.render(scene, camera);}animate();</script>
</body>
</html>
霧效 (Fog)
霧效可以為場景添加真實的氛圍,模擬遠距離物體的模糊效果。
Three.js 中的霧效類型
- 線性霧 (Linear Fog):霧的密度隨距離線性變化。
- 指數霧 (Exponential Fog):霧的密度隨距離指數變化。
- 指數平方霧 (Exponential Squared Fog):霧的密度隨距離平方變化。
示例:添加霧效
<!DOCTYPE html>
<html>
<head><title>Three.js 霧效示例</title><style>body { margin: 0; }canvas { display: block; }</style>
</head>
<body><script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script><script>const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);const renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 添加霧效scene.fog = new THREE.FogExp2(0x87CEEB, 0.0025);const geometry = new THREE.BoxGeometry();const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });const cube = new THREE.Mesh(geometry, material);scene.add(cube);camera.position.z = 5;function animate() {requestAnimationFrame(animate);cube.rotation.x += 0.01;cube.rotation.y += 0.01;renderer.render(scene, camera);}animate();</script>
</body>
</html>
示例代碼
1. 創建場景并添加物體
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);camera.position.z = 5;function animate() {requestAnimationFrame(animate);cube.rotation.x += 0.01;cube.rotation.y += 0.01;renderer.render(scene, camera);
}
animate();
2. 加載并設置紋理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('path/to/texture.jpg', function(texture) {texture.encoding = THREE.sRGBEncoding;
});const material = new THREE.MeshBasicMaterial({ map: texture });
3. 添加霧效
scene.fog = new THREE.FogExp2(0x87CEEB, 0.0025);
總結
通過以上內容,您可以了解 Three.js 的基礎概念、紋理顏色空間的處理以及霧效的添加方法。希望這些知識點對您有所幫助!
### 圖片示例#### 紋理顏色空間對比
#### 霧效效果
---### 代碼示例#### 創建場景并添加物體
```javascript
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);camera.position.z = 5;function animate() {requestAnimationFrame(animate);cube.rotation.x += 0.01;cube.rotation.y += 0.01;renderer.render(scene, camera);
}
animate();
加載并設置紋理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('path/to/texture.jpg', function(texture) {texture.encoding = THREE.sRGBEncoding;
});const material = new THREE.MeshBasicMaterial({ map: texture });
添加霧效
scene.fog = new THREE.FogExp2(0x87CEEB, 0.0025);
總結
通過以上內容,您可以了解 Three.js 的基礎概念、紋理顏色空間的處理以及霧效的添加方法。希望這些知識點對您有所幫助!
以下是關于在Three.js中使用glTF模型的學習指南,包含知識點、代碼實現和資源建議:
11-Three.js glTF 模型學習指南
📚 知識點思維導圖
🛠? 學習步驟
-
環境準備
- 安裝Node.js
- 創建項目文件夾
mkdir threejs-gltf-demo && cd threejs-gltf-demo npm init -y npm install three @types/three three-orbitcontrols
-
基礎Three.js場景搭建
<!-- index.html --> <!DOCTYPE html> <html> <head><meta charset="UTF-8"><title>glTF Demo</title><style>body { margin: 0; }</style> </head> <body><script src="main.js"></script> </body> </html>
-
核心代碼實現(自己要在src的同級目錄下新建一個models然后里面放上glb即可)
import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';// 1. 初始化場景 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0xeeeeee); // 設置背景顏色 document.body.appendChild(renderer.domElement);// 2. 添加光源 const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // 環境光 const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // 平行光 directionalLight.position.set(5, 5, 5); scene.add(ambientLight, directionalLight);// 3. 加載glTF模型 const loader = new GLTFLoader(); let model;// 本地模型路徑 const modelUrl = './models/DragonAttenuation.glb';loader.load(modelUrl,(gltf) => {model = gltf.scene;scene.add(model); // 將模型添加到場景中console.log('模型加載成功');},undefined,(error) => {console.error('加載錯誤:', error);} );// 4. 相機控制 camera.position.z = 1; // 設置相機初始位置 const controls = new OrbitControls(camera, renderer.domElement); // 添加軌道控制器 controls.enableDamping = true; // 啟用阻尼效果// 5. 動畫循環 function animate() {requestAnimationFrame(animate);controls.update(); // 更新控制器renderer.render(scene, camera); // 渲染場景 } animate();// 6. 窗口響應 window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight; // 更新相機寬高比camera.updateProjectionMatrix(); // 更新相機投影矩陣renderer.setSize(window.innerWidth, window.innerHeight); // 更新渲染器大小 });
📌 關鍵知識點
-
GLTFLoader使用要點
// 帶進度回調的加載示例 loader.load('model.gltf',(gltf) => {// 成功回調},(xhr) => {// 進度回調console.log(`${(xhr.loaded / xhr.total * 100)}% loaded`);},(error) => {// 錯誤處理} );
-
模型操作示例
// 調整模型屬性 if(model) {model.position.set(0, 0, 0);model.rotation.y += 0.01;model.scale.set(1, 1, 1); }// 遍歷模型子元素 model.traverse((child) => {if (child.isMesh) {child.castShadow = true;child.receiveShadow = true;} });
-
優化建議
- 使用Draco壓縮:
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');const loader = new GLTFLoader(); loader.setDRACOLoader(dracoLoader);
🖼? 示例模型資源
推薦使用官方示例模型:
- Damaged Helmet
- Flight Helmet
?? 常見問題
-
跨域問題:建議使用本地服務器運行
npx serve
-
模型不顯示:檢查控制臺錯誤、確認模型路徑、確認光源設置
-
性能優化:使用壓縮格式、合并網格、簡化材質
📁 項目結構建議
project-root/
├── index.html
├── main.js
├── models/
│ └── your_model.gltf
├── textures/
└── package.json
完整實現需要:
- 將glTF模型文件放入models目錄
- 安裝依賴后通過本地服務器運行
- 根據模型實際尺寸調整相機位置
需要測試模型可以先用Three.js示例模型:
loader.load('https://threejs.org/examples/models/gltf/Duck/glTF/Duck.gltf',// 其他參數保持不變
);
沒有模型怎么辦?需要的可以私聊波珠,點贊收藏加關注!
以下是獲取 glTF 模型的推薦資源和詳細方法,幫助你快速找到可用的測試模型:
🌟 免費 glTF 模型資源網站
(可直接下載使用,無需注冊或商業授權)
-
官方示例模型
- Khronos Group 官方示例庫
- 包含經典模型(如 Damaged Helmet、Flight Helmet 等)
- 直接下載
.gltf
+.bin
+ 貼圖文件組合
- Khronos Group 官方示例庫
-
Sketchfab 免費專區
- Sketchfab Free Models
- 搜索時勾選 “Downloadable” 和 “glTF” 格式
- 推薦模型:
- Sci-Fi Helmet
- Low Poly Wolf
- Sketchfab Free Models
-
Three.js 官方示例模型
-
在線路徑(直接用于代碼測試):
// 示例:加載 Three.js 自帶的鴨子模型 loader.load('https://threejs.org/examples/models/gltf/Duck/glTF/Duck.gltf',(gltf) => { /*...*/ } );
-
-
其他資源庫
- Poly Haven(CC0 授權,需轉換格式)
- Clara.io(部分免費)
🛠? 快速獲取測試模型的步驟
-
手動下載模型到本地
-
從 Khronos 官方庫 下載 ZIP
-
解壓后找到
glTF
文件夾(通常包含.gltf
+.bin
+ 貼圖) -
在項目中創建
models
目錄,將文件放入其中 -
代碼中修改路徑:
loader.load('./models/YourModel/glTF/Model.gltf', (gltf) => { /*...*/ });
-
?? 注意事項
-
文件結構
glTF 模型通常由多個文件組成,確保所有文件保持相對路徑不變:models/ └── DamagedHelmet/├── glTF/ ← 使用這個文件夾│ ├── DamagedHelmet.gltf│ ├── DamagedHelmet.bin│ └── textures/ ← 貼圖文件夾
-
跨域問題
如果直接打開本地 HTML 文件加載模型會報錯,需啟動本地服務器:npx serve
然后訪問 `http://localhost:5000
🎨 最終效果參考
加上環境貼圖則是這個效果。
12-Three.js 光線投射 (Raycasting) 實現3D交互
📚 核心知識點
1. 光線投射原理
2. Three.js 實現步驟
- 坐標轉換:將鼠標位置轉換為標準化設備坐標(NDC)
- 射線生成:通過相機和鼠標位置創建射線
- 碰撞檢測:檢測射線與場景物體的交叉點
- 交互處理:根據檢測結果執行操作
3. 關鍵API
THREE.Raycaster
raycaster.setFromCamera()
raycaster.intersectObjects()
4. 應用場景
- 3D物體點擊選中
- 鼠標懸停高亮
- 第一人稱射擊游戲
- VR/AR中的交互
🛠? 完整代碼實現
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';// 初始化場景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);// 添加測試物體
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00,metalness: 0.3,roughness: 0.8
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
scene.add(ambientLight, directionalLight);// 相機位置
camera.position.z = 5;
const controls = new OrbitControls(camera, renderer.domElement);// 初始化Raycaster
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();// 鼠標移動事件監聽
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('click', onClick);function onMouseMove(event) {// 轉換鼠標坐標到標準化設備坐標 [-1, 1]mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;// 更新射線raycaster.setFromCamera(mouse, camera);// 檢測相交物體const intersects = raycaster.intersectObjects(scene.children);// 懸停效果scene.children.forEach(obj => {if (obj.isMesh) {obj.material.emissive.setHex(0x000000);}});if (intersects.length > 0) {intersects[0].object.material.emissive.setHex(0x444444);}
}function onClick(event) {raycaster.setFromCamera(mouse, camera);const intersects = raycaster.intersectObjects(scene.children);if (intersects.length > 0) {// 點擊效果:隨機改變顏色intersects[0].object.material.color.setHex(Math.random() * 0xffffff);}
}// 動畫循環
function animate() {requestAnimationFrame(animate);controls.update();renderer.render(scene, camera);
}
animate();// 窗口響應
window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);
});
📌 關鍵代碼解析
1. 坐標轉換公式
mouse.x = (event.clientX / windowWidth) * 2 - 1;
mouse.y = -(event.clientY / windowHeight) * 2 + 1;
2. 射線檢測優化
// 只檢測可交互物體
const interactiveObjects = [cube, sphere];
raycaster.intersectObjects(interactiveObjects);// 性能優化:節流檢測
let lastCheck = 0;
function throttleCheck() {const now = Date.now();if (now - lastCheck > 100) {checkIntersections();lastCheck = now;}
}
3. 高級交互示例
// 顯示點擊位置標記
const markerGeometry = new THREE.SphereGeometry(0.05, 16, 16);
const markerMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const marker = new THREE.Mesh(markerGeometry, markerMaterial);
scene.add(marker);function onClick() {const intersects = raycaster.intersectObjects(scene.children);if (intersects.length > 0) {// 在交點處顯示標記marker.position.copy(intersects[0].point);marker.visible = true;// 添加點擊動畫new TWEEN.Tween(marker.scale).to({ x: 1.5, y: 1.5, z: 1.5 }, 300).easing(TWEEN.Easing.Exponential.Out).start();}
}
?? 常見問題解決方案
-
檢測不到物體
- 確保物體
matrixWorld
已更新:object.updateMatrixWorld()
- 檢查物體是否在場景中
- 確認物體
visible
屬性為true
- 確保物體
-
坐標偏移問題
// 處理CSS偏移 const rect = renderer.domElement.getBoundingClientRect(); mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
-
性能優化建議
- 使用
intersectObject
替代intersectObjects
檢測單個物體 - 減少檢測頻率(使用節流)
- 使用簡化碰撞體(如
BoxHelper
)
- 使用
🖼? 效果示意圖
通過光線投射實現物體懸停高亮和點擊變色效果
13-補間動畫(Tween Animation)知識點與代碼示例
1.1 什么是補間動畫?
補間動畫(Tween Animation)是一種在動畫的起始狀態和結束狀態之間自動生成中間幀的動畫技術。開發者只需要定義動畫的起始狀態和結束狀態,系統會自動計算并生成中間的狀態,從而實現平滑的動畫效果。
1.2 補間動畫的常見屬性
- 位移(Translation):改變對象的位置。
- 縮放(Scale):改變對象的大小。
- 旋轉(Rotation):改變對象的旋轉角度。
- 透明度(Alpha):改變對象的透明度。
1.3 補間動畫的優缺點
- 優點:
- 簡單易用,只需定義起始和結束狀態。
- 適用于簡單的動畫效果。
- 缺點:
- 無法處理復雜的交互和動畫邏輯。
- 動畫效果較為單一,缺乏靈活性。
1.4 補間動畫的實現方式
- XML定義:通過XML文件定義動畫效果,然后在代碼中加載并應用。
- 代碼實現:直接在代碼中定義動畫效果并應用。
2. 代碼示例
2.1 XML定義補間動畫
2.1.1 定義動畫XML文件(res/anim/scale_anim.xml
)
<set xmlns:android="http://schemas.android.com/apk/res/android"><scaleandroid:fromXScale="1.0"android:toXScale="2.0"android:fromYScale="1.0"android:toYScale="2.0"android:pivotX="50%"android:pivotY="50%"android:duration="1000" />
</set>
2.1.2 在代碼中加載并應用動畫
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageView imageView = findViewById(R.id.imageView);Animation scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.scale_anim);imageView.startAnimation(scaleAnimation);}
}
2.2 代碼實現補間動畫
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.ImageView;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageView imageView = findViewById(R.id.imageView);ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 2.0f, // 起始和結束的X縮放比例1.0f, 2.0f, // 起始和結束的Y縮放比例Animation.RELATIVE_TO_SELF, 0.5f, // 縮放中心點X坐標Animation.RELATIVE_TO_SELF, 0.5f // 縮放中心點Y坐標);scaleAnimation.setDuration(1000); // 動畫持續時間imageView.startAnimation(scaleAnimation);}
}
好玩的代碼示例:
import * as THREE from 'three';
import * as TWEEN from '@tweenjs/tween.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
import { GlitchPass } from 'three/addons/postprocessing/GlitchPass.js';// 初始化場景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);// 添加銀河系背景
const starGeometry = new THREE.BufferGeometry();
const starPositions = [];
for (let i = 0; i < 5000; i++) {starPositions.push((Math.random() - 0.5) * 2000,(Math.random() - 0.5) * 2000,(Math.random() - 0.5) * 2000);
}
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starPositions, 3));
const starMaterial = new THREE.PointsMaterial({ color: 0xFFFFFF, size: 0.7 });
const stars = new THREE.Points(starGeometry, starMaterial);
scene.add(stars);// 創建炫光立方體
const geometry = new THREE.IcosahedronGeometry(1, 3);
const material = new THREE.MeshStandardMaterial({color: 0x00ff00,metalness: 0.9,roughness: 0.1,emissive: 0x00ff00,emissiveIntensity: 0.5
});
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
cube.receiveShadow = true;
scene.add(cube);// 添加粒子環繞系統
const particleGeometry = new THREE.BufferGeometry();
const particleCount = 2000;
const posArray = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount * 3; i++) {posArray[i] = (Math.random() - 0.5) * 5;
}
particleGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
const particleMaterial = new THREE.PointsMaterial({size: 0.005,color: 0x00ffff,transparent: true,blending: THREE.AdditiveBlending
});
const particleMesh = new THREE.Points(particleGeometry, particleMaterial);
cube.add(particleMesh);// 動態光源配置
const pointLight = new THREE.PointLight(0x00ff00, 1, 100);
pointLight.position.set(5, 5, 5);
pointLight.castShadow = true;
scene.add(pointLight);// 后期處理配置
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight),1.5, 0.4, 0.85
);
composer.addPass(bloomPass);
const glitchPass = new GlitchPass();
glitchPass.goWild = false;
composer.addPass(glitchPass);// 鏡頭光暈特效
const textureLoader = new THREE.TextureLoader();
const flareTexture = textureLoader.load('./textures/lensflare/lensflare0.png');
const lensFlare = new THREE.Sprite(new THREE.SpriteMaterial({map: flareTexture,color: 0xffffff,transparent: true,blending: THREE.AdditiveBlending
}));
lensFlare.scale.set(2, 2, 1);
lensFlare.position.copy(pointLight.position);
scene.add(lensFlare);// 相機控制
camera.position.z = 7;
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;// 光線投射交互
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();window.addEventListener('mousemove', onMouseMove);
window.addEventListener('click', onClick);function onMouseMove(event) {mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;raycaster.setFromCamera(mouse, camera);const intersects = raycaster.intersectObjects([cube]);scene.traverse(obj => {if (obj.isMesh && obj !== cube) obj.material.emissive.setHex(0x000000);});if (intersects.length > 0) {intersects[0].object.material.emissive.setHex(0xff00ff);bloomPass.strength = 2.0;} else {bloomPass.strength = 1.5;}
}function onClick() {raycaster.setFromCamera(mouse, camera);const intersects = raycaster.intersectObjects([cube]);if (intersects.length > 0) {// 點擊爆炸粒子效果new TWEEN.Tween(particleMesh.material).to({ size: 0.02 }, 300).easing(TWEEN.Easing.Exponential.Out).onComplete(() => {particleMesh.material.size = 0.005;}).start();// 顏色漸變new TWEEN.Tween(cube.material.color).to({ r: Math.random(), g: Math.random(), b: Math.random() }, 500).start();}
}// 動畫循環
const clock = new THREE.Clock();
function animate() {requestAnimationFrame(animate);const time = clock.getElapsedTime();// 立方體動態旋轉cube.rotation.x = time * 0.5;cube.rotation.y = time * 0.3;// 粒子系統動畫const positions = particleMesh.geometry.attributes.position.array;for (let i = 0; i < positions.length; i += 3) {positions[i] += Math.sin(time + i) * 0.001;positions[i + 1] += Math.cos(time + i) * 0.001;}particleMesh.geometry.attributes.position.needsUpdate = true;// 光源顏色變化pointLight.color.setHSL(Math.sin(time * 0.5) * 0.5 + 0.5, 1, 0.5);// 更新光暈位置lensFlare.position.copy(pointLight.position);// 更新后期處理TWEEN.update();controls.update();composer.render();
}
animate();// 窗口響應
window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);composer.setSize(window.innerWidth, window.innerHeight);
});
3. 總結
補間動畫是一種簡單易用的動畫技術,適用于實現基本的動畫效果。通過XML定義或代碼實現,開發者可以輕松地創建位移、縮放、旋轉和透明度變化的動畫效果。然而,對于更復雜的動畫需求,建議使用屬性動畫(Property Animation)或其他更高級的動畫技術。