Three.js - KeyframeTrack 帧动画

本文介绍了Three.js中的KeyframeTrack和AnimationAction对象用于实现帧动画的方法。KeyframeTrack用于创建帧动画,包括构造函数、使用示例和小示例。AnimationAction提供了暂停、播放、设置播放时间、定位到特定时间点等功能,还讲解了如何添加动画事件监听。文章通过具体的代码示例详细阐述了这两个对象在实现3D动画中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

THREE.KeyframeTrack是用来设置动画的对象,以一定的时间过渡到一定值的对象。

1、KeyframeTrack 对象

1.1、构造函数

KeyframeTrack( name : String, times : Array, values : Array, interpolation : Constant )
name、 一个帧动画的标识符,指以这个标识进行帧动画
times、时间数组,内部会转换为Float32Array
values、帧数组,在times时间内所要执行的数值,Float32Array类型
interpolation、常用差值模式(离散的,线性,平滑),默认是InterpolateLinear线性,是一个常量

常量设置参考 Animation Constants

1.2、使用示例

例如:让一个几何体BoxGeometry10秒内从向量Vector3(0, 0, 0)平滑过渡到Vector3(100, 0, 0),那么可以通过KeyframeTrack来创建帧动画对象。

var times = [0, 10];.//离散的时间序列
var values = [0, 0, 0, 100, 0, 0];//过渡的值

// 创建帧动画对象,使box几何体,在10秒内移动到(100, 0, 0)的位置
var posKeyframeTrack = new THREE.KeyframeTrack('box.position', times, values)

这个帧动画是以几何体的position属性进行线性过渡的,同样可以使几何体的材质属性以及其他是离散值的属性都可以进行帧跟踪变化。

1.3、小示例

在这里插入图片描述
如图需要实现的效果,需要三个几何体对象在特定的时间,位置和颜色的过渡。

1.3.1、创建几何体

1、首先,需要创建三个几何体,并且把他们放到一个Group中,然后加入到场景中。

var group = new THREE.Group();

var geometry = new THREE.BoxGeometry(10, 10, 10);

var material = new THREE.MeshLambertMaterial({color: 0xff0000});
var materia2 = new THREE.MeshLambertMaterial({color: 0x00ff00});
var materia3 = new THREE.MeshLambertMaterial({color: 0x0000ff});

var box1 = new THREE.Mesh(geometry, material);
var box2 = new THREE.Mesh(geometry, materia2);
var box3 = new THREE.Mesh(geometry, materia3);
box1.name = 'box1';
box2.name = 'box2';
box3.name = 'box3';

group.add(box1, box2, box3);

scene.add(group);

2、然后,分别创建关于这三个几何体的KeyframeTrack对象,进行位置和颜色的变化

/*1、创建帧动画序列 KeyframeTrack */
var times = [0, 10];

/* 离散的时间点序列 */
var position_x = [0, 0, 0, 100, 0, 0];
var position_y = [0, 0, 0, 0, 100, 0];
var position_z = [0, 0, 0, 0, 0, 100];

/* 在 times 时间执行的过渡值  Float32Array 类型 */
var color_r = [1, 0, 0, 0, 1, 0];
var color_g = [0, 1, 0, 0, 0, 1];
var color_b = [0, 0, 1, 1, 0, 0];

上面分别以类型化数组的形式定义了,需要过渡的点和颜色。
3、创建KeyframeTrack对象

/* 创建 KeyframeTrack 对象 0 -- 10 之内位置变化*/
var pos1_Keyframe = new THREE.KeyframeTrack('box1.position', times, position_x);
var pos2_Keyframe = new THREE.KeyframeTrack('box2.position', times, position_y);
var pos3_Keyframe = new THREE.KeyframeTrack('box3.position', times, position_z);

/* 0 -- 10 之内颜色的变化 */
var color1_Keyframe = new THREE.KeyframeTrack('box1.material.color', times, color_r);
var color2_Keyframe = new THREE.KeyframeTrack('box2.material.color', times, color_g);
var color3_Keyframe = new THREE.KeyframeTrack('box3.material.color', times, color_b);

4、创建好帧动画之后就需要对keyframeTrack进行剪辑 需要通过THREE.AnimationClip对象
THREE.AnimationClip对象是一组可以重复使用的关键帧轨迹,它代表一个动画,多个KeyframeTrack构成一个剪辑动画AnimationClip

/* 2、 剪辑keyframeTrack 对象 */
var duration = 10;

/* 多个帧动画创建一个 剪辑对象 命名为 boxAnimation */
var clip = new THREE.AnimationClip('boxAnimation', duration, [
    pos1_Keyframe,
    pos2_Keyframe,
    pos3_Keyframe,
    color1_Keyframe,
    color2_Keyframe,
    color3_Keyframe
]);

5、通过THREE.AnimationMixer混合器,通过KeyframeTrack以及剪辑器AnimationClip对象,仅仅构成动画的数据基础,实际的动画播放需要通过AnimationMixer来控制,AnimationMixer就想一个动画控制台,它可以同时控制多个动画,以及合并它们。

 var mixer = new THREE.AnimationMixer(group);
// 通过混合器获取AnimationAction对象,来进一步的对动画进行控制
 var action = mixer.clipAction(clip);

6、AnimationMixer是一个混合器,每个动画或者多个动画组都有一个AnimationMixer,也就是一个动画播放器,但是AnimationMixer需要通过AnimationAction去控制动画实际的状态,具体是暂停还是循环以及淡入淡出等等,都需要AnimationAction来控制,通过播放器对象AnimationMixer可以获得一个控制器对象AnimationAction


// 通过混合器获取到该动画的控制器对象
var action = mixer.clipAction(clip);

 /* 可以调节播放速度 默认值为 1 */
 action.timeScale = 5;

 /* 开始播放  setLoop()设置循环换模式和次数, play()播放动画*/
 action.setLoop(THREE.LoopPingPong, 4).play();

常量设置参考 Animation Constants

7、创建好播放器以及控制之后还需要对动画进行每一帧的跟踪

/* 数据更新 */
var clock = new THREE.Clock();// three.js 时钟对象
function update() {
    mixer.update(clock.getDelta());// 更新每一帧的混合器
}

8、示例效果
示例:帧动画效果

2、AnimationAction对象

通过THREE.AnimationMixer() 可以获取到一个THREE.AnimationAction()然后可以控制动画的循环模式一个次数,淡入淡出等等

2.1、暂停和播放

AnimationActionpaused可以控制动画的暂停和播放,是一个boolean
修改上述小示例,可以在执行动画期间来控制动画的播放和暂停。

<!-- 添加按钮 -->
<input type="button" value="on/off" id="control-btn">

修改paused的值

document.getElementById('control-btn').onclick = function () {
   action.paused = !action.paused;
};

示例:控制帧动画暂停和播放

2.2、动画暂停在最后一帧

可以通过设置AnimationAction.clampWhenFinished的属性值(布尔值),若为ture的话可以使动画暂停在最后一帧播放的状态。

2.3、设置开始播放的时间

通过设置AnimationAction.time属性,可以设置开始播放动画的时间,如果AnimationAction.time = 5,则动画会在5秒后开始执行

2.4、播放特定的时间段

通过设置AnimationAction.time属性和AnimationClip.duration可以在特定的时间段来播放动画,例如一个动画需要执行20秒,设置动画开始时间为10秒即设置AnimationAction.time = 10,以及动画结束时间为AnimationClip.duration = 15,则动画会 10 - 15 之间运行。

2.5、将动画定位谋个时间点

将动画定位在谋个时间点,只需要设置动画开始的时间和结束的时间一致就可以。
例如:首先,设置动画开始时间为AnimationAction.time = 10,然后设置AnimationClip.duration = AnimationAction.time即可直接定位到特定的时间点。

3、动画事件

可以通过给AnimationMixer播放器添加loopfinished事件来获取动画一个循环一个动画结束时的回调

mixer.addEventListener( 'loop', function( e ) {} ); // properties of e: type, action and loopDelta
mixer.addEventListener( 'finished', function( e ) {} ); // properties of e: type, action and direction

小示例添加如下代码

mixer.addEventListener('loop', function (e) {
    console.log('帧动画循环回调:', e);
});

mixer.addEventListener('finished', function (e) {
    console.log('帧动画结束回调:', e);
})

运行并查看控制台
在这里插入图片描述
具体其他动画操作的属性和方法,请参考THREE.AnimationAction

4、示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="icon" href="../../../three.png">
    <title>暂停和播放帧动画</title>
    <style>
        body {
            margin: 0;
            overflow: hidden; /* 溢出隐藏 */
        }
        input {
            position: absolute;
            right: 0;
            top: 1px;
        }
    </style>
    <script src="../../libs/build/three-r93.js"></script>
    <script src="../../libs/examples/js/controls/OrbitControls.js"></script>
</head>
<body>

<input type="button" value="on/off" id="control-btn">

<script>

    var scene, camera, renderer, controls;
    var clock = new THREE.Clock();


    /* 场景 */
    function initScene() {

        scene = new THREE.Scene();

    }

    /* 相机 */
    function initCamera() {

        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
        camera.position.set(100, 100, 200);
        camera.lookAt(new THREE.Vector3(0, 0, 0));

    }

    /* 渲染器 */
    function initRender() {

        renderer = new THREE.WebGLRenderer({antialias: true});
        renderer.setSize(window.innerWidth, window.innerHeight);

        document.body.appendChild(renderer.domElement);

    }

    /* 灯光 */
    function initLight() {

        scene.add(new THREE.AmbientLight(0xffffff));

        var spotLight1 = new THREE.SpotLight(0xffffff);
        spotLight1.position.set(-400, -400, -400);

        var spotLight2 = new THREE.SpotLight(0xffffff);
        spotLight2.position.set(400, 400, 400);

        scene.add(spotLight1);
        scene.add(spotLight2);

    }

    /* 控制器 */
    function initControls() {

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

    }

    var mixer;
    var action;

    document.getElementById('control-btn').onclick = function () {
        action.paused = !action.paused;
    };

    function initContent() {

        var axesHelper = new THREE.AxesHelper(100);
        scene.add(axesHelper);

        var group = new THREE.Group();

        var geometry = new THREE.BoxGeometry(10, 10, 10);

        var material = new THREE.MeshLambertMaterial({color: 0xff0000});
        var materia2 = new THREE.MeshLambertMaterial({color: 0x00ff00});
        var materia3 = new THREE.MeshLambertMaterial({color: 0x0000ff});

        var box1 = new THREE.Mesh(geometry, material);
        var box2 = new THREE.Mesh(geometry, materia2);
        var box3 = new THREE.Mesh(geometry, materia3);
        box1.name = 'box1';
        box2.name = 'box2';
        box3.name = 'box3';

        group.add(box1, box2, box3);

        scene.add(group);

        /*1、创建帧动画序列 KeyframeTrack */
        var times = [0, 10];

        /* 离散的时间点序列 */
        var position_x = [0, 0, 0, 100, 0, 0];
        var position_y = [0, 0, 0, 0, 100, 0];
        var position_z = [0, 0, 0, 0, 0, 100];

        /* 在 times 时间执行的过渡值  Float32Array 类型 */
        var color_r = [1, 0, 0, 0, 1, 0];
        var color_g = [0, 1, 0, 0, 0, 1];
        var color_b = [0, 0, 1, 1, 0, 0];
        /* 颜色过渡值 */

        /* 创建 KeyframeTrack 对象 0 -- 10 之内位置变化*/
        var pos1_Keyframe = new THREE.KeyframeTrack('box1.position', times, position_x);
        var pos2_Keyframe = new THREE.KeyframeTrack('box2.position', times, position_y);
        var pos3_Keyframe = new THREE.KeyframeTrack('box3.position', times, position_z);

        /* 0 -- 10 之内颜色的变化 */
        var color1_Keyframe = new THREE.KeyframeTrack('box1.material.color', times, color_r);
        var color2_Keyframe = new THREE.KeyframeTrack('box2.material.color', times, color_g);
        var color3_Keyframe = new THREE.KeyframeTrack('box3.material.color', times, color_b);

        /* 2、 剪辑keyframeTrack 对象 */
        var duration = 10;

        /* 多个帧动画创建一个 剪辑对象 命名为 boxAnimation */
        var clip = new THREE.AnimationClip('boxAnimation', duration, [
            pos1_Keyframe,
            pos2_Keyframe,
            pos3_Keyframe,
            color1_Keyframe,
            color2_Keyframe,
            color3_Keyframe
        ]);

        /* 3、播放编辑好的动画对象 */
        mixer = new THREE.AnimationMixer(group);

        action = mixer.clipAction(clip);

        /* 可以调节播放速度 默认值为 1 */
        action.timeScale = 5;

        /* 开始播放 */

        action.setLoop(THREE.LoopPingPong, 4).play();

    }


    /* 窗口变动触发 */
    function onWindowResize() {

        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);

    }

    /* 数据更新 */
    function update() {

        controls.update();
        mixer.update(clock.getDelta());

    }

    /* 初始化 */
    function init() {

        initScene();
        initCamera();
        initRender();
        initLight();
        initControls();
        initContent();
        /* 监听事件 */
        window.addEventListener('resize', onWindowResize, false);

        mixer.addEventListener('loop', function (e) {
            console.log('帧动画循环回调:', e);
        });

        mixer.addEventListener('finished', function (e) {
            console.log('帧动画结束回调:', e);
        })

    }

    /* 循环渲染 */
    function animate() {

        requestAnimationFrame(animate);
        renderer.render(scene, camera);
        update();

    }

    /* 初始加载 */
    (function () {
        init();
        animate();
    })();

</script>
</body>
</html>

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值