Threejs里执行对象的多个动画

承接上文,本文讲述如何在Threejs里播放对象的多个动画,这也是研究了很久才解决的…


一 导出模型

在Blender里按照File->Export,选择glTF2.0
Threejs里执行对象的多个动画_第1张图片

然后在弹框的右上角选择导出为glTF Embedded (.gltf)
Threejs里执行对象的多个动画_第2张图片

这样就把模型导出来了,该模型里包含了2个动画。


二 Threejs样例

Threejs的安装本文不会描述,可以参考官方文档,比较简单。

main.js内容如下,

import * as THREE from 'three';

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GUI } from 'lil-gui'



const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff );
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1e-5, 1e10 );
camera.position.set(0, 15, 30);



var ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);

const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setPixelRatio(window.devicePixelRatio);
renderer.outputColorSpace = THREE.SRGBColorSpace;

document.body.appendChild( renderer.domElement );

const loader = new GLTFLoader();

const dracoLoader = new DRACOLoader();
loader.setDRACOLoader(dracoLoader);


let modelReady = false;
let gltfscene;
let horsevisible = false;
let mixer;


let actions;


loader.load(
    '/models/33.gltf',
    function (gltf) {
        gltfscene = gltf.scene;
        scene.add(gltfscene);

        console.log(gltf.animations);

        mixer = new THREE.AnimationMixer(gltf.scene);

        let Action1 = mixer.clipAction( gltf.animations[0] );
        let Action2 = mixer.clipAction( gltf.animations[1] );

        actions = [Action1, Action2];

        modelReady = true;
    },
    function(xhr) {
        console.log((xhr.loaded / xhr.total * 100) + '% loaded');
    },
    function (error) {
        console.log('An error happened.');
    }
)


const obj = {
    myFunction: function () {
        if (horsevisible == false)
        {
            actions[0].clampWhenFinished = true;
            actions[0].setLoop(THREE.LoopOnce);
            actions[0].play();
            actions[1].stop(); // 非常关键
        }
        else
        {
            actions[1].clampWhenFinished = true;
            actions[1].setLoop(THREE.LoopOnce);
            actions[1].play();
            actions[0].stop(); // 非常关键
        }

        horsevisible = !horsevisible;
    }
}
const gui = new GUI()
gui.add(obj, 'myFunction'); // 按钮


const grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
grid.material.opacity = 0.3;
grid.material.transparent = true;
scene.add( grid );

const controls = new OrbitControls(camera, renderer.domElement);
controls.update();


const clock = new THREE.Clock();

function animate() {
    requestAnimationFrame( animate );
    if (modelReady)
    {
        mixer.update(clock.getDelta());
    }

    controls.update();

    renderer.render( scene, camera );
}

animate();

然后运行npx vite来启动server,打印如下,
Threejs里执行对象的多个动画_第3张图片

然后在浏览器里打开这个地址,显示如下,
Threejs里执行对象的多个动画_第4张图片
然后点击myFunction按钮,可以看到这个立方体会移动到右边,再次点击myFunction按钮,立方体又会移到中间。

代码里关键点是myFunction对应的回调函数,因为动画的默认参数会重复执行这个动画,这里把clampWhenFinished设置为true,并把loop设置为THREE.LoopOnce,然后调用play(),这样可以保证这个动画只执行一次。

最后一定要执行一下另外一个动画的stop()函数,这样可以保证另外动画的正确执行,否则会出现位置不正确的情况。


三 总结

本文讲述了如何在Threejs里播放同一对象的多个动画。关键点有2个,

  1. 设置动画只执行一次
  2. 调用下一个动画的stop()函数来保证下一个动画可以正确执行。

你可能感兴趣的:(threejs,前端)