本文介绍三块内容,如何为不同对象添加动画,如何实现淡入淡出特效,动画刷不出来可能的错误是什么。
一、如何为不同的对象添加动画
对于每个场景,也就是不同的物体添加动画的话,需要以下一套操作,第三步,只需在一开始时声明一次。
(一)定义KeyframeTrack
KeyframeTrack使用来定义关键帧的,也就是在定义什么物体,在什么时间做什么事情,是最基础的第一步,可以想象成拍电视剧中拍的每一场戏。用法如下:
const times = [0, 2];
const liftValues = [-40, 0];
const liftTrack1 = new THREE.KeyframeTrack(
'block.position[y]',
times,
liftValues
);
//意思是在0~2秒之间,把名字为block的物体的y值,从-40提升到0的位置。
const liftTrack3 = new THREE.BooleanKeyframeTrack(
'innerblock.material.transparent',
[5, 6],
[false, true]
);
//意思是对于innerblock物体,在5秒的时候设置其material属性中的transparent属性为true,其实这是为了下一步。
const liftTrack4 = new THREE.NumberKeyframeTrack(
'innerblock.material.opacity',
[6, 8],
[1, 0]
);//在6到8秒的时候让这个innerblock的material的透明度降到0,也就是淡出的效果。
此处需要注意的是这个block.position[y]的block,必须是这个物体的name值,即如下设置过它的name值为’block’。
const block = new THREE.Object3D();
block.name = 'block';
或者(官方说法)你也可以通过它的uuid值去引用它(但那么长,谁会去用uuid值引用-。-)。
第二个要注意的地方是,第9行,15行,用的是BooleanKeyframeTrack和NumberKeyframeTrack,原因是因为后面要更改的属性类型分别是Boolean和Number。至于还有其他的什么类型可以看这里。
(二)剪辑AnimationClip
用来保存之前拍的每场戏的剪辑片段,使其有顺序的链接在一起,形成一集电视剧(但还未精修剪辑),官网解释可以看这里,用法如下(可以直接看最后一行)
//blcok animation
const times = [0, 2];
const liftValues = [-40, 0];
const liftTrack1 = new THREE.KeyframeTrack(
'block.position[y]',
times,
liftValues
);
const liftTrack2 = new THREE.KeyframeTrack(
'block.position[y]',
[4, 6],
[0, 46]
);
const liftTrack3 = new THREE.BooleanKeyframeTrack(
'innerblock.material.transparent',
[5, 6],
[false, true]
);
const liftTrack4 = new THREE.NumberKeyframeTrack(
'innerblock.material.opacity',
[6, 8],
[1, 0]
);
const liftTrack5 = new THREE.BooleanKeyframeTrack(
'frameblock.material.transparent',
[5, 6],
[false, true]
);
const liftTrack6 = new THREE.NumberKeyframeTrack(
'frameblock.material.opacity',
[6, 8],
[1, 0]
);
const duration = 10;
const clip = new THREE.AnimationClip('lift', duration, [liftTrack1, liftTrack2, liftTrack3, liftTrack4, liftTrack5, liftTrack6]);
注意这个duration参数的值,不能小于各个track时间之和,不然刷不出来。
(三)生成混合器AnimationMixer
我把这个理解成后台剪辑软件,用来混合每一集电视剧。
关于这个mixer,官网的解释是
动画混合器是用于场景中特定对象的动画的播放器。当场景中的多个对象独立动画时,每个对象都可以使用同一个动画混合器。
非常重要的一点是“每个对象都可以使用同一个动画混合器”,那么我这里建议,不管你的场景图有多少层,也就是不管你图中节点的层次结构有多深,直接这样写:
const mixer = new THREE.AnimationMixer(scene);//这里直接写scene
这里参数直接写scene,不写scene的话,会有坑,写别的节点的话,你没囊括的节点的动画就会刷不出来。
注意,这个mixer只需要申明一次就可以了。以后给别的对象添加动画时,只需要用这个mixer去根据clip生成一个Action。类似下面这样
......
const mixer = new THREE.AnimationMixer(scene);
......
const AnimationAction = mixer.clipAction(clip);
......
const upAction = mixer.clipAction(upClip);
(四)生成动作AnimationAction
这个我的理解是精修的每一集电视剧,也就是说是一集分装好的连续的动画,所以要用软件剪辑之前保存的clip动画数据。所以它是这样生成的,
AnimationAction( mixer : AnimationMixer, clip : AnimationClip, localRoot : Object3D ) mixer - 被此动作控制的 动画混合器 clip - 动画剪辑 保存了此动作当中的动画数据 localRoot - 动作执行的根对象
所以也就可以直接Action.xxx用来操作这个整体动画做xxx的事情。官网解释是:
AnimationActions 用来调度存储在AnimationClips中的动画。
注意!如果你想这个动画只做一次(因为你试了就知道,做完上述这些步骤,它会反复的去做这个动画。)
那么只需要加上以下中间三行代码:
const AnimationAction = mixer.clipAction(clip);
AnimationAction.setLoop(THREE.LoopOnce);
AnimationAction.clampWhenFinished = true;
AnimationAction.enabled = true;
AnimationAction.play();
(五)最后的点睛之笔
做完以上的你会发现没有动画,没错,因为还有最关键的最后一步。
const animate = function () {
requestAnimationFrame(animate);
renderer.render(scene, camera);
mixer.update(clock.getDelta());
};
animate();
二、如何实现淡入淡出特效
本以为这是一个很好解决的问题,结果搜了很久,都没找到一个满意的答案。现在我来提供最简洁的做法,其实也很简单。只有先把transparent设置为true,才能更改物体的opacity,从而实现淡出/淡入效果。
const liftTrack3 = new THREE.BooleanKeyframeTrack(
'innerblock.material.transparent',
[5, 6],
[false, true]
);
const liftTrack4 = new THREE.NumberKeyframeTrack(
'innerblock.material.opacity',
[6, 8],
[1, 0]
);
三、动画刷不出来可能的错误是什么
1、总时长小于每个track的时长之和
2、关键帧中使用的KeyframeTrack不对,没考虑到后面要更改的属性类型值,即可能是BooleanKeyframeTrack或是其他类型。
3、生成混合器时,混合对象不对,即节点不是最根部节点。
参考一部分内容第三点,const mixer = new THREE.AnimationMixer(scene),你没写scene,写了其他节点(其他对象)。
四、完整代码
//blcok animation
const times = [0, 2];
const liftValues = [-40, 0];
const liftTrack1 = new THREE.KeyframeTrack(
'block.position[y]',
times,
liftValues
);
const liftTrack2 = new THREE.KeyframeTrack(
'block.position[y]',
[4, 6],
[0, 46]
);
const liftTrack3 = new THREE.BooleanKeyframeTrack(
'innerblock.material.transparent',
[5, 6],
[false, true]
);
const liftTrack4 = new THREE.NumberKeyframeTrack(
'innerblock.material.opacity',
[6, 8],
[1, 0]
);
const liftTrack5 = new THREE.BooleanKeyframeTrack(
'frameblock.material.transparent',
[5, 6],
[false, true]
);
const liftTrack6 = new THREE.NumberKeyframeTrack(
'frameblock.material.opacity',
[6, 8],
[1, 0]
);
const duration = 10;
const clip = new THREE.AnimationClip('lift', duration, [liftTrack1, liftTrack2, liftTrack3, liftTrack4, liftTrack5, liftTrack6]);
const mixer = new THREE.AnimationMixer(scene);//??new THREE.AnimationMixer(scene)??
//up-block
const AnimationAction = mixer.clipAction(clip);
AnimationAction.setLoop(THREE.LoopOnce);
AnimationAction.clampWhenFinished = true;
AnimationAction.enabled = true;
AnimationAction.play();
//up-part entire + 圆圈 + 字
const upTrack1 = new THREE.BooleanKeyframeTrack(
'upArrowMesh.visible',
[1, 2],
[false, true]
);
const upTrack2 = new THREE.BooleanKeyframeTrack(
'upCircle.material.transparent',
[0, 1],
[false, true]
);
const upTrack3 = new THREE.NumberKeyframeTrack(
'upCircle.material.opacity',
[2, 3],
[0, 1]
);
const upClip = new THREE.AnimationClip('uppartshow', 10, [upTrack1, upTrack2, upTrack3]);
const upAction = mixer.clipAction(upClip);
upAction.setLoop(THREE.LoopOnce);
upAction.clampWhenFinished = true;
upAction.enabled = true;
upAction.play();
//up-part arrow
const arrowTrack1 = new THREE.KeyframeTrack(
'upArrowHelperMesh.position[y]',
[0, 2],
[-8, 3]
);
const arrowClip = new THREE.AnimationClip('arrowlift', 2, [arrowTrack1]);
const arrowAction = mixer.clipAction(arrowClip);
arrowAction.play();
// up-part text
const textTrack1 = new THREE.BooleanKeyframeTrack(
'textMesh.visible',
[0, 7],
[false, true]
);
const textClip = new THREE.AnimationClip('textshow', 20, [textTrack1]);
const textAction = mixer.clipAction(textClip);
textAction.setLoop(THREE.LoopOnce);
textAction.clampWhenFinished = true;
textAction.enabled = true;
textAction.play();
const animate = function () {
requestAnimationFrame(animate);
earth.rotation.z -= 0.03;
renderer.render(scene, camera);
mixer.update(clock.getDelta());
};
animate();
转载请注明出处。