官网效果图:
demo代码及我添加的注释:
html
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - animation - keyframes</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<style>
body {
background-color: #bfe3dd;
color: #000;
}
a {
color: #2983ff;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - animation - keyframes<br/>
Model: <a href="https://artstation.com/artwork/1AGwX" target="_blank" rel="noopener">Littlest Tokyo</a> by
<a href="https://artstation.com/glenatron" target="_blank" rel="noopener">Glen Fox</a>, CC Attribution.
</div>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js'; //性能监视器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; //控制控制器,控制旋转
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; //用于创建逼真的室内环境,它接受一个参数 path,用于指定立方体贴图的路径。典型的用法是加载预先准备好的立方体贴图,然后将其作为参数传递给 RoomEnvironment
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; //加载gltf模型的加载器
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';//模型解压
let mixer;
const clock = new THREE.Clock(); //初始化时钟
const container = document.getElementById( 'container' );
const stats = new Stats();
container.appendChild( stats.dom ); //左上角的性能监视器
const renderer = new THREE.WebGLRenderer( { antialias: true } ); //antialias:锯齿属性
renderer.setPixelRatio( window.devicePixelRatio ); //设置像素比,如果你遇到你的canvas画布输出模糊问题一定要设置
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
const pmremGenerator = new THREE.PMREMGenerator( renderer ); //预处理环境,HDR 纹理以获得最佳质量
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0xbfe3dd );
scene.environment = pmremGenerator.fromScene( new RoomEnvironment( renderer ), 0.04 ).texture; //光源
const camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 ); //透视投影相机,如果渲染远小近大--透视投影相机,不需要远小近大--正投影相机
camera.position.set( 5, 2, 8 ); //0,0,0将是模型的几何中心,通过调整参数可以调整我们第一次看到的位置
// 注意相机控件OrbitControls会影响lookAt设置,注意手动设置OrbitControls的目标参数
const controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 0.5, 0 ); //相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0
controls.update();update()函数内会执行camera.lookAt(controls.targe)
controls.enablePan = false;
controls.enableDamping = true;
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'jsm/libs/draco/gltf/' ); //设置draco路径
const loader = new GLTFLoader();
loader.setDRACOLoader( dracoLoader ); //设置gltf加载器draco编码器
loader.load( 'models/gltf/LittlestTokyo.glb', function ( gltf ) {//加载模型
// const obj = gltf.scene.getObjectByName("OSG_Scene"); //可以获取某个名字的模型节点
// console.log(gltf.scene,obj,'加载的模型数据')
// 递归遍历所有模型节点
// gltf.scene.traverse(function(obj) {
// if (obj.isMesh) {//判断是否是网格模型
// console.log('模型节点',obj);
// console.log('模型节点名字',obj.name);
// }
// });
// 递归遍历所有模型节点批量修改材质
// gltf.scene.traverse(function(obj) {
// if (obj.isMesh) {
// // 重新设置材质
// obj.material = new THREE.MeshLambertMaterial({
// color:0xffffff,
// });
// }
// });
const model = gltf.scene;
model.position.set( 1, 1, 0 );
model.scale.set( 0.01, 0.01, 0.01 );
scene.add( model );
mixer = new THREE.AnimationMixer( model ); 包含关键帧动画的模型对象作为AnimationMixer的参数创建一个播放器mixer
let clipAction = mixer.clipAction( gltf.animations[ 0 ] )
clipAction.play(); 创建动画clipAction对象 并播放动画
// 人走路、跑步美术美术一般设置很短时间运动,如果你想一直看到运动动作,不用设置非循环。
// //不循环播放
// clipAction.loop = THREE.LoopOnce;
// // 物体状态停留在动画结束的时候
// clipAction.clampWhenFinished = true
animate();
}, undefined, function ( e ) {
console.error( e );
} );
window.onresize = function () { //更改浏览器大小自适应
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
};
function animate() {
requestAnimationFrame( animate );
const delta = clock.getDelta(); //自动开启时钟, 获取2帧之间的时间间隔
mixer.update( delta ); 更新播放器相关的时间
controls.update(); update()函数内会执行camera.lookAt(controls.target)
stats.update();//requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
renderer.render( scene, camera );
}
</script>
</body>
</html>
备注:我们在官网能看到车车的动画,其实是模型中有关键帧动画,在threejs引入后使用AnimationMixer
播放器进行播放的。
demo中车车是循环走的,我们要是遇到只动画一次的情况,可以设置
js
//不循环播放
lipAction.loop = THREE.LoopOnce;
// // 物体状态停留在动画结束的时候
clipAction.clampWhenFinished = true