本文介绍三块内容,如何为不同对象添加动画,如何实现淡入淡出特效,动画刷不出来可能的错误是什么。
一、如何为不同的对象添加动画
对于每个场景,也就是不同的物体添加动画的话,需要以下一套操作,第三步,只需在一开始时声明一次。
(一)定义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 animationconst 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 animationconst 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-blockconst 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 arrowconst 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 textconst 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();
转载请注明出处。
