Skip to content

属性动画(PropertyAnimation)

属性动画组件 PropertyAnimation 通过持续更改对象的属性值来变更目标的状态,实现动画效果。 Animation

应用举例

  1. Object3D 的属性:position, scale, rotation
  2. 材质球 中的贴图:uv, offset, tiling
  3. 后处理效果 参数:color, strength

以上属性都可以提供为属性动画更改的内容。

基本使用

指定场景中的某个节点 Object3D,为其添加组件 PropertyAnimation;然后给组件追加影片剪辑后即能使用。

目前引擎只支持导入经过专业建模软件制作的 Clip 素材,暂不支持代码中自定义属性动画,后续版本会加入

ts
// load test model
let node = new Object3D();
scene.addChild(node);
// 添加组件
let animation = node.addComponent(PropertyAnimation);

// 加载clip素材
let res = await fetch('path/to/clip.json')
let json = await res.json()
// 初始化clip
let animClip = new PropertyAnimClip();
// 解析clip
animClip.parse(json);
animClip.wrapMode = WrapMode.Once;
animation.defaultClip = animClip.name;
animation.autoPlay = false;
// 将clip追加至组件
animation.appendClip(animClip);
// load test model
let node = new Object3D();
scene.addChild(node);
// 添加组件
let animation = node.addComponent(PropertyAnimation);

// 加载clip素材
let res = await fetch('path/to/clip.json')
let json = await res.json()
// 初始化clip
let animClip = new PropertyAnimClip();
// 解析clip
animClip.parse(json);
animClip.wrapMode = WrapMode.Once;
animation.defaultClip = animClip.name;
animation.autoPlay = false;
// 将clip追加至组件
animation.appendClip(animClip);

播放动画

可以使用 play 方法来播放对应名称 (name)PropertyAnimClip

ts
animation.play('anim_0', true); // 默认 true 从头播放
animation.play('anim_0', true); // 默认 true 从头播放

暂停动画

可以使用 stop 方法来播放指定的 PropertyAnimClip

ts
animation.stop();
animation.stop();

切换动画

可以使用 toggle 方法来播放已暂停的动画,或暂停正在播放的动画。

ts
animation.toggle();
animation.toggle();

指定动画时间

可以使用 seek 方法来指定动画播放起始时间。

ts
animation.seek(1.2);//指定到1.2s
animation.seek(1.2);//指定到1.2s

获取影片剪辑

使用 getClip 方法,获取组件已绑定过的影片剪辑 PropertyAnimClip,参数为 PropertyAnimClipname

ts
let clip: PropertyAnimClip = animation.getClip('anim_0');
let clip: PropertyAnimClip = animation.getClip('anim_0');

获取当前剪辑

可以使用 currentClip 方法来获取当前正在播放的 PropertyAnimClip

ts
const currentClip = animation.currentClip;
const currentClip = animation.currentClip;

获取动画时间

可以使用 time 方法来获取当前时间。

ts
const currentClip = animation.time;
const currentClip = animation.time;

示例


WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { DirectLight, Engine3D, AtmosphericComponent, View3D, HoverCameraController, KelvinUtil, Object3D, Scene3D, CameraUtil, webGPUContext, PropertyAnimation, PropertyAnimClip, WrapMode } from '@orillusion/core'
import * as dat from 'dat.gui'

export class Sample_PropertyAnim {
    lightObj: Object3D
    scene: Scene3D
    private animation: PropertyAnimation

    constructor() {}

    async run() {
        await Engine3D.init()

        this.scene = new Scene3D()
        let camera = CameraUtil.createCamera3DObject(this.scene, 'camera')
        camera.perspective(60, webGPUContext.aspect, 1, 2000.0)
        let ctrl = camera.object3D.addComponent(HoverCameraController)
        ctrl.setCamera(180, -20, 15)

        await this.initScene(this.scene)

        this.scene.addComponent(AtmosphericComponent).sunY = 0.6

        // create a view with target scene and camera
        let view = new View3D()
        view.scene = this.scene
        view.camera = camera
        // start render
        Engine3D.startRenderView(view)

        let guiData = {
            click: () => this.animation.play('anim_0', true),
            Seek: 0,
            Speed: 1
        }
        const GUIHelp = new dat.GUI()
        GUIHelp.add(guiData, 'click').name('Restart')
        GUIHelp.add(guiData, 'Seek', 0, 1, 0.01).onChange((v) => {
            this.animation.stop()
            this.animation.seek(v)
        })
        GUIHelp.add(guiData, 'Speed', 0, 1, 0.01).onChange((v) => {
            this.animation.speed = v
        })
        this.animation.onLateUpdate = () => {
            guiData.Seek = this.animation.time
            GUIHelp.updateDisplay()
        }
    }

    private async makePropertyAnim(node: Object3D) {
        // add PropertyAnimation
        let animation = node.addComponent(PropertyAnimation)
        // load clip source
        let res = await fetch('https://cdn.orillusion.com/json/anim_0.json')
        let json = await res.json()
        // init clip
        let animClip = new PropertyAnimClip()
        // parse clip
        animClip.parse(json)
        animClip.wrapMode = WrapMode.Loop
        animation.defaultClip = animClip.name
        animation.autoPlay = true
        // add clip to animation
        animation.appendClip(animClip)
        return animation
    }

    async initScene(scene: Scene3D) {
        /******** light *******/
        {
            this.lightObj = new Object3D()
            this.lightObj.x = 0
            this.lightObj.y = 30
            this.lightObj.z = -40
            this.lightObj.rotationX = 45
            this.lightObj.rotationY = 0
            this.lightObj.rotationZ = 45
            let lc = this.lightObj.addComponent(DirectLight)
            lc.lightColor = KelvinUtil.color_temperature_to_rgb(5355)
            lc.intensity = 20
            scene.addChild(this.lightObj)
        }

        let duck = await Engine3D.res.loadGltf('https://cdn.orillusion.com/PBR/Duck/Duck.gltf')
        this.scene.addChild(duck)
        duck.scaleX = duck.scaleY = duck.scaleZ = 0.02

        this.animation = await this.makePropertyAnim(duck)
        this.animation.play(this.animation.defaultClip)

        return true
    }
}

new Sample_PropertyAnim().run()
import { DirectLight, Engine3D, AtmosphericComponent, View3D, HoverCameraController, KelvinUtil, Object3D, Scene3D, CameraUtil, webGPUContext, PropertyAnimation, PropertyAnimClip, WrapMode } from '@orillusion/core'
import * as dat from 'dat.gui'

export class Sample_PropertyAnim {
    lightObj: Object3D
    scene: Scene3D
    private animation: PropertyAnimation

    constructor() {}

    async run() {
        await Engine3D.init()

        this.scene = new Scene3D()
        let camera = CameraUtil.createCamera3DObject(this.scene, 'camera')
        camera.perspective(60, webGPUContext.aspect, 1, 2000.0)
        let ctrl = camera.object3D.addComponent(HoverCameraController)
        ctrl.setCamera(180, -20, 15)

        await this.initScene(this.scene)

        this.scene.addComponent(AtmosphericComponent).sunY = 0.6

        // create a view with target scene and camera
        let view = new View3D()
        view.scene = this.scene
        view.camera = camera
        // start render
        Engine3D.startRenderView(view)

        let guiData = {
            click: () => this.animation.play('anim_0', true),
            Seek: 0,
            Speed: 1
        }
        const GUIHelp = new dat.GUI()
        GUIHelp.add(guiData, 'click').name('Restart')
        GUIHelp.add(guiData, 'Seek', 0, 1, 0.01).onChange((v) => {
            this.animation.stop()
            this.animation.seek(v)
        })
        GUIHelp.add(guiData, 'Speed', 0, 1, 0.01).onChange((v) => {
            this.animation.speed = v
        })
        this.animation.onLateUpdate = () => {
            guiData.Seek = this.animation.time
            GUIHelp.updateDisplay()
        }
    }

    private async makePropertyAnim(node: Object3D) {
        // add PropertyAnimation
        let animation = node.addComponent(PropertyAnimation)
        // load clip source
        let res = await fetch('https://cdn.orillusion.com/json/anim_0.json')
        let json = await res.json()
        // init clip
        let animClip = new PropertyAnimClip()
        // parse clip
        animClip.parse(json)
        animClip.wrapMode = WrapMode.Loop
        animation.defaultClip = animClip.name
        animation.autoPlay = true
        // add clip to animation
        animation.appendClip(animClip)
        return animation
    }

    async initScene(scene: Scene3D) {
        /******** light *******/
        {
            this.lightObj = new Object3D()
            this.lightObj.x = 0
            this.lightObj.y = 30
            this.lightObj.z = -40
            this.lightObj.rotationX = 45
            this.lightObj.rotationY = 0
            this.lightObj.rotationZ = 45
            let lc = this.lightObj.addComponent(DirectLight)
            lc.lightColor = KelvinUtil.color_temperature_to_rgb(5355)
            lc.intensity = 20
            scene.addChild(this.lightObj)
        }

        let duck = await Engine3D.res.loadGltf('https://cdn.orillusion.com/PBR/Duck/Duck.gltf')
        this.scene.addChild(duck)
        duck.scaleX = duck.scaleY = duck.scaleZ = 0.02

        this.animation = await this.makePropertyAnim(duck)
        this.animation.play(this.animation.defaultClip)

        return true
    }
}

new Sample_PropertyAnim().run()