本文介绍三块内容,如何为不同对象添加动画,如何实现淡入淡出特效,动画刷不出来可能的错误是什么。

一、如何为不同的对象添加动画

对于每个场景,也就是不同的物体添加动画的话,需要以下一套操作,第三步,只需在一开始时声明一次。

(一)定义KeyframeTrack

KeyframeTrack使用来定义关键帧的,也就是在定义什么物体,在什么时间做什么事情,是最基础的第一步,可以想象成拍电视剧中拍的每一场戏。用法如下:

  1. const times = [0, 2];
  2. const liftValues = [-40, 0];
  3. const liftTrack1 = new THREE.KeyframeTrack(
  4. 'block.position[y]',
  5. times,
  6. liftValues
  7. );
  8. //意思是在0~2秒之间,把名字为block的物体的y值,从-40提升到0的位置。
  9. const liftTrack3 = new THREE.BooleanKeyframeTrack(
  10. 'innerblock.material.transparent',
  11. [5, 6],
  12. [false, true]
  13. );
  14. //意思是对于innerblock物体,在5秒的时候设置其material属性中的transparent属性为true,其实这是为了下一步。
  15. const liftTrack4 = new THREE.NumberKeyframeTrack(
  16. 'innerblock.material.opacity',
  17. [6, 8],
  18. [1, 0]
  19. );//在6到8秒的时候让这个innerblock的material的透明度降到0,也就是淡出的效果。

此处需要注意的是这个block.position[y]的block,必须是这个物体的name值,即如下设置过它的name值为’block’。

  1. const block = new THREE.Object3D();
  2. block.name = 'block';

或者(官方说法)你也可以通过它的uuid值去引用它(但那么长,谁会去用uuid值引用-。-)。
第二个要注意的地方是,第9行,15行,用的是BooleanKeyframeTrack和NumberKeyframeTrack,原因是因为后面要更改的属性类型分别是Boolean和Number。至于还有其他的什么类型可以看这里

(二)剪辑AnimationClip

用来保存之前拍的每场戏的剪辑片段,使其有顺序的链接在一起,形成一集电视剧(但还未精修剪辑),官网解释可以看这里,用法如下(可以直接看最后一行)

  1. //blcok animation
  2. const times = [0, 2];
  3. const liftValues = [-40, 0];
  4. const liftTrack1 = new THREE.KeyframeTrack(
  5. 'block.position[y]',
  6. times,
  7. liftValues
  8. );
  9. const liftTrack2 = new THREE.KeyframeTrack(
  10. 'block.position[y]',
  11. [4, 6],
  12. [0, 46]
  13. );
  14. const liftTrack3 = new THREE.BooleanKeyframeTrack(
  15. 'innerblock.material.transparent',
  16. [5, 6],
  17. [false, true]
  18. );
  19. const liftTrack4 = new THREE.NumberKeyframeTrack(
  20. 'innerblock.material.opacity',
  21. [6, 8],
  22. [1, 0]
  23. );
  24. const liftTrack5 = new THREE.BooleanKeyframeTrack(
  25. 'frameblock.material.transparent',
  26. [5, 6],
  27. [false, true]
  28. );
  29. const liftTrack6 = new THREE.NumberKeyframeTrack(
  30. 'frameblock.material.opacity',
  31. [6, 8],
  32. [1, 0]
  33. );
  34. const duration = 10;
  35. const clip = new THREE.AnimationClip('lift', duration, [liftTrack1, liftTrack2, liftTrack3, liftTrack4, liftTrack5, liftTrack6]);

注意这个duration参数的值,不能小于各个track时间之和,不然刷不出来。

(三)生成混合器AnimationMixer

我把这个理解成后台剪辑软件,用来混合每一集电视剧。
关于这个mixer,官网的解释是

动画混合器是用于场景中特定对象的动画的播放器。当场景中的多个对象独立动画时,每个对象都可以使用同一个动画混合器。

非常重要的一点是“每个对象都可以使用同一个动画混合器”,那么我这里建议,不管你的场景图有多少层,也就是不管你图中节点的层次结构有多深,直接这样写:

  1. const mixer = new THREE.AnimationMixer(scene);//这里直接写scene

这里参数直接写scene,不写scene的话,会有坑,写别的节点的话,你没囊括的节点的动画就会刷不出来。
注意,这个mixer只需要申明一次就可以了。以后给别的对象添加动画时,只需要用这个mixer去根据clip生成一个Action。类似下面这样

  1. ......
  2. const mixer = new THREE.AnimationMixer(scene);
  3. ......
  4. const AnimationAction = mixer.clipAction(clip);
  5. ......
  6. const upAction = mixer.clipAction(upClip);

(四)生成动作AnimationAction

这个我的理解是精修的每一集电视剧,也就是说是一集分装好的连续的动画,所以要用软件剪辑之前保存的clip动画数据。所以它是这样生成的,

AnimationAction( mixer : AnimationMixer, clip : AnimationClip, localRoot : Object3D ) mixer - 被此动作控制的 动画混合器 clip - 动画剪辑 保存了此动作当中的动画数据 localRoot - 动作执行的根对象

所以也就可以直接Action.xxx用来操作这个整体动画做xxx的事情。官网解释是:

AnimationActions 用来调度存储在AnimationClips中的动画。

注意!如果你想这个动画只做一次(因为你试了就知道,做完上述这些步骤,它会反复的去做这个动画。)
那么只需要加上以下中间三行代码:

  1. const AnimationAction = mixer.clipAction(clip);
  2. AnimationAction.setLoop(THREE.LoopOnce);
  3. AnimationAction.clampWhenFinished = true;
  4. AnimationAction.enabled = true;
  5. AnimationAction.play();

(五)最后的点睛之笔

做完以上的你会发现没有动画,没错,因为还有最关键的最后一步。

  1. const animate = function () {
  2. requestAnimationFrame(animate);
  3. renderer.render(scene, camera);
  4. mixer.update(clock.getDelta());
  5. };
  6. animate();

二、如何实现淡入淡出特效

本以为这是一个很好解决的问题,结果搜了很久,都没找到一个满意的答案。现在我来提供最简洁的做法,其实也很简单。只有先把transparent设置为true,才能更改物体的opacity,从而实现淡出/淡入效果。

  1. const liftTrack3 = new THREE.BooleanKeyframeTrack(
  2. 'innerblock.material.transparent',
  3. [5, 6],
  4. [false, true]
  5. );
  6. const liftTrack4 = new THREE.NumberKeyframeTrack(
  7. 'innerblock.material.opacity',
  8. [6, 8],
  9. [1, 0]
  10. );

三、动画刷不出来可能的错误是什么

1、总时长小于每个track的时长之和

2、关键帧中使用的KeyframeTrack不对,没考虑到后面要更改的属性类型值,即可能是BooleanKeyframeTrack或是其他类型。

3、生成混合器时,混合对象不对,即节点不是最根部节点。

参考一部分内容第三点,const mixer = new THREE.AnimationMixer(scene),你没写scene,写了其他节点(其他对象)。

四、完整代码

  1. //blcok animation
  2. const times = [0, 2];
  3. const liftValues = [-40, 0];
  4. const liftTrack1 = new THREE.KeyframeTrack(
  5. 'block.position[y]',
  6. times,
  7. liftValues
  8. );
  9. const liftTrack2 = new THREE.KeyframeTrack(
  10. 'block.position[y]',
  11. [4, 6],
  12. [0, 46]
  13. );
  14. const liftTrack3 = new THREE.BooleanKeyframeTrack(
  15. 'innerblock.material.transparent',
  16. [5, 6],
  17. [false, true]
  18. );
  19. const liftTrack4 = new THREE.NumberKeyframeTrack(
  20. 'innerblock.material.opacity',
  21. [6, 8],
  22. [1, 0]
  23. );
  24. const liftTrack5 = new THREE.BooleanKeyframeTrack(
  25. 'frameblock.material.transparent',
  26. [5, 6],
  27. [false, true]
  28. );
  29. const liftTrack6 = new THREE.NumberKeyframeTrack(
  30. 'frameblock.material.opacity',
  31. [6, 8],
  32. [1, 0]
  33. );
  34. const duration = 10;
  35. const clip = new THREE.AnimationClip('lift', duration, [liftTrack1, liftTrack2, liftTrack3, liftTrack4, liftTrack5, liftTrack6]);
  36. const mixer = new THREE.AnimationMixer(scene);//??new THREE.AnimationMixer(scene)??
  37. //up-block
  38. const AnimationAction = mixer.clipAction(clip);
  39. AnimationAction.setLoop(THREE.LoopOnce);
  40. AnimationAction.clampWhenFinished = true;
  41. AnimationAction.enabled = true;
  42. AnimationAction.play();
  43. //up-part entire + 圆圈 + 字
  44. const upTrack1 = new THREE.BooleanKeyframeTrack(
  45. 'upArrowMesh.visible',
  46. [1, 2],
  47. [false, true]
  48. );
  49. const upTrack2 = new THREE.BooleanKeyframeTrack(
  50. 'upCircle.material.transparent',
  51. [0, 1],
  52. [false, true]
  53. );
  54. const upTrack3 = new THREE.NumberKeyframeTrack(
  55. 'upCircle.material.opacity',
  56. [2, 3],
  57. [0, 1]
  58. );
  59. const upClip = new THREE.AnimationClip('uppartshow', 10, [upTrack1, upTrack2, upTrack3]);
  60. const upAction = mixer.clipAction(upClip);
  61. upAction.setLoop(THREE.LoopOnce);
  62. upAction.clampWhenFinished = true;
  63. upAction.enabled = true;
  64. upAction.play();
  65. //up-part arrow
  66. const arrowTrack1 = new THREE.KeyframeTrack(
  67. 'upArrowHelperMesh.position[y]',
  68. [0, 2],
  69. [-8, 3]
  70. );
  71. const arrowClip = new THREE.AnimationClip('arrowlift', 2, [arrowTrack1]);
  72. const arrowAction = mixer.clipAction(arrowClip);
  73. arrowAction.play();
  74. // up-part text
  75. const textTrack1 = new THREE.BooleanKeyframeTrack(
  76. 'textMesh.visible',
  77. [0, 7],
  78. [false, true]
  79. );
  80. const textClip = new THREE.AnimationClip('textshow', 20, [textTrack1]);
  81. const textAction = mixer.clipAction(textClip);
  82. textAction.setLoop(THREE.LoopOnce);
  83. textAction.clampWhenFinished = true;
  84. textAction.enabled = true;
  85. textAction.play();
  86. const animate = function () {
  87. requestAnimationFrame(animate);
  88. earth.rotation.z -= 0.03;
  89. renderer.render(scene, camera);
  90. mixer.update(clock.getDelta());
  91. };
  92. animate();

转载请注明出处。