Three.js 動畫循環學習記錄

在上一篇文章中,我們學習了Three.js 坐標系系統與單位理解教程:

Three.js 坐標系系統與單位理解教程

接下來我們要學習的是Three.js 的動畫循環

一、動畫循環基礎原理

1. 什么是動畫循環?

? ? ? ? 動畫循環是連續更新場景狀態并重新渲染的過程,通過快速連續的畫面變化產生動態效果。在Three.js中,這通常以與顯示器刷新率同步的頻率執行。

2. 核心代碼結構

function animate() {requestAnimationFrame(animate); // 1. 請求下一幀updateScene();                 // 2. 更新場景狀態renderer.render(scene, camera); // 3. 渲染場景
}
animate(); // 啟動循環

二、Vue3 中的完整實現與解析

1. 組件基礎結構(Composition API)

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import * as THREE from 'three';// DOM 引用
const container = ref(null);// Three.js 核心對象
let scene, camera, renderer, cube;
let animationId = null; // 存儲動畫ID用于取消
const clock = new THREE.Clock(); // Three.js內置時鐘
</script>

2. 初始化函數詳解

function initThreeScene() {// 場景初始化scene = new THREE.Scene();scene.background = new THREE.Color(0x111111);// 相機配置(透視相機)camera = new THREE.PerspectiveCamera(75,                         // 視野角度(FOV)window.innerWidth/innerHeight, // 寬高比0.1,                        // 近裁剪面1000                        // 遠裁剪面);camera.position.z = 5;        // 相機位置// 渲染器配置renderer = new THREE.WebGLRenderer({ antialias: true,           // 開啟抗鋸齒alpha: true                // 開啟透明度});renderer.setPixelRatio(Math.min(2, window.devicePixelRatio)); // 合理設置像素比renderer.setSize(window.innerWidth, window.innerHeight);container.value.appendChild(renderer.domElement);// 添加測試物體addTestCube();// 啟動動畫循環startAnimationLoop();
}

3. 動畫循環的完整實現

基礎版本:

function animate() {// 遞歸調用形成循環animationId = requestAnimationFrame(animate);// 物體旋轉動畫cube.rotation.x += 0.01;cube.rotation.y += 0.01;// 渲染場景renderer.render(scene, camera);
}

專業版本(帶時間控制):

let previousTime = 0;function animate(currentTime) {animationId = requestAnimationFrame(animate);// 計算時間增量(毫秒轉換為秒)const deltaTime = (currentTime - previousTime) / 1000;previousTime = currentTime;// 使用時間增量確保動畫速度一致const rotationSpeed = 2; // 弧度/秒cube.rotation.x += rotationSpeed * deltaTime;cube.rotation.y += rotationSpeed * deltaTime;// 彈跳動畫(使用正弦函數)const bounceHeight = 0.5;const bounceSpeed = 2;cube.position.y = Math.sin(currentTime * 0.001 * bounceSpeed) * bounceHeight;renderer.render(scene, camera);
}

4. 動畫控制與管理

// 啟動動畫循環
function startAnimationLoop() {if (!animationId) {clock.start(); // 啟動Three.js時鐘previousTime = performance.now(); // 記錄初始時間animate(); // 開始循環}
}// 停止動畫循環
function stopAnimationLoop() {if (animationId) {cancelAnimationFrame(animationId);animationId = null;clock.stop(); // 停止時鐘}
}// 重置動畫狀態
function resetAnimation() {cube.rotation.set(0, 0, 0);cube.position.set(0, 0, 0);previousTime = performance.now(); // 重置時間記錄
}

三、高級動畫技巧

1. 性能優化策略

條件渲染(只在需要時渲染)

let needsUpdate = false;// 當場景變化時調用
function triggerRender() {needsUpdate = true;
}function animate() {animationId = requestAnimationFrame(animate);if (needsUpdate) {renderer.render(scene, camera);needsUpdate = false;}
}

分幀處理(復雜場景優化)

let frameCount = 0;function animate() {animationId = requestAnimationFrame(animate);// 每幀只更新部分元素if (frameCount % 2 === 0) updatePhysics();if (frameCount % 3 === 0) updateBackground();updateMainObjects(); // 主要物體每幀都更新renderer.render(scene, camera);frameCount++;
}

2. 混合動畫技術

function animate() {animationId = requestAnimationFrame(animate);const time = clock.getElapsedTime();// 旋轉動畫cube.rotation.x = time * 1; // 持續旋轉// 脈動動畫(縮放)const pulseSpeed = 3;const pulseIntensity = 0.2;cube.scale.setScalar(1 + Math.sin(time * pulseSpeed) * pulseIntensity);// 顏色變化cube.material.color.setHSL(Math.sin(time * 0.5) * 0.5 + 0.5, // 色相 (0-1)0.8, // 飽和度0.5  // 亮度);renderer.render(scene, camera);
}

四、Vue3 組件完整實現

<template><!-- 容器用于掛載 Three.js 渲染器 --><div ref="container" class="three-container"></div><!-- 控制面板,包含播放/暫停、重置按鈕和 FPS 顯示 --><div class="control-panel"><!-- 播放/暫停按鈕,點擊切換動畫狀態 --><button @click="toggleAnimation">{{ isPlaying ? '? 暫停' : '? 播放' }}</button><!-- 重置動畫按鈕 --><button @click="resetAnimation">? 重置</button><!-- 顯示當前 FPS 值 --><span class="fps-counter">FPS: {{ fps.toFixed(1) }}</span></div>
</template><script setup>
// 導入 Vue 的響應式 API
import { ref, onMounted, onBeforeUnmount } from 'vue';
// 導入 Three.js 庫
import * as THREE from 'three';// 創建響應式引用:容器 DOM 元素
const container = ref(null);
// 創建響應式引用:動畫播放狀態
const isPlaying = ref(true);
// 創建響應式引用:FPS 值
const fps = ref(0);// 聲明 Three.js 相關對象變量
let scene, camera, renderer, cube;
// 動畫幀 ID,用于取消動畫循環
let animationId = null;
// Three.js 時鐘對象,用于計算時間差
const clock = new THREE.Clock();// FPS 計算相關變量
let lastFpsUpdate = 0;  // 上次更新 FPS 的時間戳
let frameCount = 0;     // 幀計數器// 初始化 Three.js 場景
function initScene() {// 創建場景對象scene = new THREE.Scene();// 設置場景背景顏色scene.background = new THREE.Color(0x222222);// 創建透視相機camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.1, 100);// 設置相機位置camera.position.set(0, 1, 5);// 相機看向原點camera.lookAt(0, 0, 0);// 創建 WebGL 渲染器,開啟抗鋸齒renderer = new THREE.WebGLRenderer({ antialias: true });// 設置渲染器像素比率renderer.setPixelRatio(window.devicePixelRatio);// 設置渲染器尺寸renderer.setSize(window.innerWidth, window.innerHeight);// 將渲染器的 DOM 元素添加到容器中container.value.appendChild(renderer.domElement);// 創建環境光const ambientLight = new THREE.AmbientLight(0x404040);// 創建方向光const directionalLight = new THREE.DirectionalLight(0xffffff, 1);// 設置方向光位置directionalLight.position.set(1, 1, 1);// 將燈光添加到場景中scene.add(ambientLight, directionalLight);// 添加動畫立方體到場景addAnimatedCube();// 添加坐標軸輔助器scene.add(new THREE.AxesHelper(3));// 添加網格輔助器scene.add(new THREE.GridHelper(10, 10));// 開始動畫循環startAnimation();
}// 創建并添加動畫立方體
function addAnimatedCube() {// 創建立方體幾何體const geometry = new THREE.BoxGeometry(1, 1, 1);// 創建標準材質并設置屬性const material = new THREE.MeshStandardMaterial({ color: 0x00ff00,      // 顏色roughness: 0.3,       // 粗糙度metalness: 0.7        // 金屬度});// 創建網格對象cube = new THREE.Mesh(geometry, material);// 將立方體添加到場景中scene.add(cube);
}// 動畫循環函數
function animate(timestamp) {// 請求下一幀動畫animationId = requestAnimationFrame(animate);// 更新 FPS 計數器updateFpsCounter(timestamp);// 獲取時間差和總時間const delta = clock.getDelta();const elapsed = clock.getElapsedTime();// 更新動畫狀態updateAnimations(elapsed, delta);// 渲染場景renderer.render(scene, camera);
}// 更新所有動畫效果
function updateAnimations(time, delta) {// 立方體繞 X 軸旋轉cube.rotation.x = time * 0.5;// 立方體繞 Y 軸旋轉cube.rotation.y = time * 0.3;// 立方體上下彈跳效果cube.position.y = Math.sin(time * 2) * 0.5;// 立方體顏色隨時間變化cube.material.color.setHSL((Math.sin(time * 0.3) + 1) / 2,  // 色相0.8,                              // 飽和度0.6                               // 亮度);
}// 更新 FPS 計數器
function updateFpsCounter(timestamp) {// 幀數遞增frameCount++;// 每秒更新一次 FPS 值if (timestamp >= lastFpsUpdate + 1000) {fps.value = (frameCount * 1000) / (timestamp - lastFpsUpdate);// 重置幀計數器和更新時間frameCount = 0;lastFpsUpdate = timestamp;}
}// 控制動畫播放的方法
function startAnimation() {// 如果動畫未運行則開始動畫if (!animationId) {clock.start();animationId = requestAnimationFrame(animate);isPlaying.value = true;}
}// 停止動畫
function stopAnimation() {// 如果動畫正在運行則停止if (animationId) {cancelAnimationFrame(animationId);animationId = null;clock.stop();isPlaying.value = false;}
}// 切換動畫播放狀態
function toggleAnimation() {if (isPlaying.value) stopAnimation();else startAnimation();
}// 重置動畫狀態
function resetAnimation() {// 重置立方體旋轉cube.rotation.set(0, 0, 0);// 重置立方體位置cube.position.set(0, 0, 0);// 重置立方體縮放cube.scale.set(1, 1, 1);// 重置立方體顏色cube.material.color.set(0x00ff00);// 重啟時鐘clock.start();
}// 窗口大小調整處理函數
function onResize() {// 更新相機寬高比camera.aspect = window.innerWidth / window.innerHeight;// 更新相機投影矩陣camera.updateProjectionMatrix();// 更新渲染器尺寸renderer.setSize(window.innerWidth, window.innerHeight);
}// 組件掛載時執行
onMounted(() => {// 初始化場景initScene();// 添加窗口大小調整事件監聽器window.addEventListener('resize', onResize);
});// 組件卸載前執行清理工作
onBeforeUnmount(() => {// 停止動畫stopAnimation();// 移除窗口大小調整事件監聽器window.removeEventListener('resize', onResize);// 釋放渲染器資源if (renderer) {renderer.dispose();renderer.forceContextLoss();}
});
</script><style>
/* Three.js 容器樣式 */
.three-container {position: fixed;top: 0;left: 0;width: 100%;height: 100%;outline: none;
}/* 控制面板樣式 */
.control-panel {position: fixed;bottom: 20px;left: 50%;transform: translateX(-50%);display: flex;gap: 10px;align-items: center;background: rgba(0, 0, 0, 0.7);padding: 10px 15px;border-radius: 20px;color: white;
}/* 控制面板按鈕樣式 */
.control-panel button {background: rgba(255, 255, 255, 0.1);border: 1px solid rgba(255, 255, 255, 0.3);color: white;padding: 5px 12px;border-radius: 15px;cursor: pointer;transition: all 0.2s;
}/* 按鈕懸停效果 */
.control-panel button:hover {background: rgba(255, 255, 255, 0.2);
}/* FPS 計數器樣式 */
.fps-counter {font-family: monospace;min-width: 80px;display: inline-block;text-align: center;
}
</style>

實現效果

three.js動畫

五、關鍵知識點總結

  1. 動畫循環三要素

    • requestAnimationFrame?遞歸調用

    • 場景狀態更新

    • 渲染器執行渲染

  2. 時間控制的重要性

    • 使用?performance.now()?或?THREE.Clock

    • 通過 delta time 確保動畫速度一致

  3. 性能優化方向

    • 條件渲染(只在需要時渲染)

    • 分幀處理(復雜場景優化)

    • 合理使用?dispose()?釋放資源

  4. Vue3 集成要點

    • 在?onMounted?中初始化

    • 在?onBeforeUnmount?中清理

    • 使用 ref 管理DOM元素引用

  5. 調試技巧

    • 添加FPS計數器監控性能

    • 使用坐標輔助器查看空間關系

    • 實現動畫控制按鈕方便調試

? ? ? ?這個實現展示了專業級的Three.js動畫循環管理,包含了性能優化、時間控制、狀態管理等關鍵要素,可以直接用于生產環境項目。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93754.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93754.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93754.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ktg-mes 改造成 Saas 系統

ktg-mes 改造成 Saas 系統 快速檢驗市場&#xff0c;采用最簡單的方案&#xff0c;即添加表字段 截止2025年8月16日上傳的ktg-mes搭建存在一些問題&#xff0c;搭建可看文章&#xff1a; 搭建ktg-mes 改造 1. 添加租戶表 create table sys_tenant (tenant_id bigint au…

【新手易混】find 命令中 -perm 選項的知識點

find 命令是 Linux/Unix 系統中強大的文件查找工具&#xff0c;廣泛用于根據文件名、類型、時間、權限等條件搜索文件。其中&#xff0c;-perm 選項用于按文件權限查找文件&#xff0c;而在 -perm /mode 中出現的斜杠 / 是一種特殊的語法&#xff0c;表示“按位或&#xff08;O…

gdb的load命令和傳給opeocd的monitor flash write_image erase命令的區別

問&#xff1a; "monitor flash write_image erase ${workspaceFolder}/obj/ylad_led_blink.elf", 和 "load", "executable" : "${workspaceFolder}/obj/ylad_led_blink.elf", 的區別&#xff1f;答&#xff1a; 你提到的 "monit…

1. Docker的介紹和安裝

文章目錄1. Docker介紹核心概念核心優勢與虛擬機的區別一句話總結2. Docker的安裝Windows 10/11 安裝 Docker Desktop&#xff08;推薦 WSL2 方式&#xff09;Linux&#xff08;以 Ubuntu / Debian 系為例&#xff09;Docker 是一個開源的容器化平臺&#xff0c;它允許開發者將…

fastdds.ignore_local_endpoints 屬性

Fast DDS 的 fastdds.ignore_local_endpoints 屬性用于控制同一 DomainParticipant 下的本地端點&#xff08;即 DataWriter 和 DataReader&#xff09;是否自動匹配。以下是對該功能的詳細解釋&#xff0c;并翻譯為中文&#xff0c;結合其上下文、實現原理和使用場景&#xff…

華清遠見25072班C語言學習day11

重點內容:函數&#xff1a;定義&#xff1a;返回值類型 函數名(參數列表) { //函數體 }函數的參數列表中可以有多個數據返回值&#xff1a;如果函數沒有返回值可以寫成void 返回值的作用&#xff0c;函數的結果用來返回給主調函數的&#xff0c;如果主調函數處不需要函數的結果…

視覺語言導航(7)——VLN的數據集和評估方法 3.2

這是課上做的筆記&#xff0c;因此很多記得比較急&#xff0c;之后會逐步完善&#xff0c;每節課的邏輯流程寫在大綱部分。成功率(SR)導航誤差(NE)成功加權路徑長度&#xff08;SucceedPLength&#xff09;軌跡長度&#xff08;TL&#xff09;先知成功率&#xff08;OS&#xf…

ElasticSearch不同環境同步索引數據

目的&#xff1a;在生產環境把一個索引的數據同步到測試環境中1、在生產環境導出json數據curl -u "adims_user:xkR%cHwR5I9g" -X GET "http://172.18.251.132:9200/unify_info_mb_sp_aggregatetb_0004/_search?scroll1m" -H Content-Type: applicatio…

咨詢進階——解讀咨詢顧問技能模型

適應人群為咨詢行業從業者、咨詢團隊管理者、想提升咨詢技能的職場人士及咨詢公司培訓人員。主要內容圍繞咨詢顧問技能模型展開,核心包括五大核心能力(解決問題能力,涵蓋洞察力、分析技巧、問題構建等,從識別問題實質到構建新分析方法分層次闡述;管理能力,涉及管理他人與…

2025年- H98-Lc206--51.N皇后(回溯)--Java版

1.題目描述2.思路 二維數組集合 (1&#xff09;N皇后規則 1&#xff09;不能同行&#xff08;同一行不能出現2個皇后&#xff09; 2&#xff09;不能同列&#xff08;同一列不能出現2個皇后&#xff09; 3&#xff09;不能說45度或135度&#xff08;斜對角線不能出現2個皇后&am…

5G + AI + 云:電信技術重塑游戲生態與未來體驗

在數字娛樂蓬勃發展的今天&#xff0c;游戲產業已然成為科技創新的前沿陣地。電信網絡也經歷了一場深刻的蛻變&#xff0c;從最初僅僅是 “內容傳輸管道”&#xff0c;搖身一變成為與游戲深度綁定的技術共生體。5G 不斷刷新著體驗的邊界&#xff0c;AI 徹底顛覆傳統的創作模式&…

【React Hooks】封裝的藝術:如何編寫高質量的 React 自-定義 Hooks

【React Hooks】封裝的藝術&#xff1a;如何編寫高質量的 React 自-定義 Hooks 所屬專欄&#xff1a; 《前端小技巧集合&#xff1a;讓你的代碼更優雅高效》 上一篇&#xff1a; 【React State】告別 useState 濫用&#xff1a;何時應該選擇 useReducer 作者&#xff1a; 碼力…

華為GaussDB的前世今生:國產數據庫崛起之路

在數據庫領域&#xff0c;華為GaussDB已成為一顆耀眼的明星&#xff0c;為企業核心業務數字化轉型提供堅實的數據底座。但這并非一蹴而就&#xff0c;其背后是長達二十余年的技術沉淀、戰略投入與持續創新。本文將深入探尋華為GaussDB的歷史沿革與核心技術細節&#xff0c;展現…

數據結構初階(16)排序算法——歸并排序

2.4 歸并排序 歸并排序&#xff08;Merge Sort&#xff09;是基于分治思想的經典排序算法。核心邏輯&#xff1a; 分而治之——把復雜排序問題拆分成簡單子問題解決&#xff0c;再合并子問題的結果。聯系鏈表的合并&#xff1a;兩個有序鏈表l1、l2創建新鏈表l3&#xff08;帶頭…

MATLAB實現匈牙利算法求解二分圖最大匹配

MATLAB實現匈牙利算法求解二分圖最大匹配 匈牙利算法&#xff08;也稱為Kuhn-Munkres算法&#xff09;是解決二分圖最大匹配問題的經典算法。 代碼 function [matching, max_match] hungarian_algorithm(adjMatrix)% HUNGARIAN_ALGORITHM 實現匈牙利算法求解二分圖最大匹配% 輸…

自定義table

更好<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"utf-8"><title>數據表格</title><style>* {margin: 0;padding: 0;box-sizing: border-box;font-size: 14px;}html,body {width: 100%;height: 100%…

面向R語言用戶的Highcharts

如果您喜歡使用 R 進行數據科學創建交互式數據可視化&#xff0c;那么請你收藏。今天&#xff0c;我們將使用折線圖、柱狀圖和散點圖來可視化資產回報。對于我們的數據&#xff0c;我們將使用以下 5 只 ETF 的 5 年月回報率。 SPY (S&P500 fund)EFA (a non-US equities fun…

【測試工具】OnDo SIP Server--輕松搭建一個語音通話服務器

前言 Ondo SIP Server 是一款基于 SIP(Session Initiation Protocol)協議的服務器軟件&#xff0c;主要用于實現 VoIP(Voice over IP)通信&#xff0c;支持語音通話、視頻會議等多媒體會話管理&#xff0c;非常適合學習和測試VoIP的基本功能。本文介紹Ondo SIP Server的安裝、…

瘋狂星期四文案網第42天運營日記

網站運營第42天&#xff0c;點擊觀站&#xff1a; 瘋狂星期四 crazy-thursday.com 全網最全的瘋狂星期四文案網站 運營報告 今日訪問量 今日搜索引擎收錄情況 網站優化點 優化一些發現的seo錯誤 增加顏文字欄目 增加了一些tag

使用空模型實例調用輔助函數,確定在量化過程中哪些層會被跳過(43)

在Facebook的OPT-350M中,模型的頭部(lm_head)與解碼器的嵌入標記層(decoder.embed_tokens)共享其權重。 print(model.model.decoder.embed_tokens) print(model.lm_head)輸出結果 Embedding(50272, 512