基于路径集合的三维动画链

简介: 基于路径集合的三维动画链

1、配置路径数据2、创建动画链2.1、rotationAniamte2.2、moveAnimate2.3、动画链式衔接3、动画链效果

目前数据信息可视化的发展趋势越来越快,纬度宽广、数量庞大、结构复杂的数据展示仅仅只依靠二维平面图表已经不能满足了。为了更加清晰,快速的认知和理解一份数据,构建基于现实的三维虚拟可视化效果,被广泛应用到各行业中,迅速成为信息数字化管理的重要组成部分。


三维场景还原了现实的虚拟效果,而各种各样的动画则赋予其更加饱满、灵动的视觉冲击。基于路径集合的三维动画链,它充实了场景中元素的动画效果,接下来我会具体介绍动画链的实现过程。


微信图片_20220425131601.png


1、配置路径数据


此次示例中模型均采用gltf格式,以上图场景中小车为例,在模型加载完成后,我们定义配置好一系列的路径点(animatePath),让小车按照该路径执行动画。


1scene.loadGLTF('./static/gltf/car1/1.gltf', {
 2  generateTangent: true,
 3  useIBLWhenMissingTexture: true,
 4  loadTexture: true,
 5}).then((data) => {
 6  const element = data.root;
 7  element.scale = [0.1, 0.1, 0.1];
 8  element.ry = Math.PI;
 9  element.position = [98, 1.5, -125];
10
11  const { y } = element;
12  const animatePath = [
13    [98, y, -135],
14    [50, y, -135],
15    [10, y, -95],
16    [-30, y, -125]
17  ]
18
19  // 创建动画链
20  element.animate = this.createPathAnimates(element, animatePath, () => {
21    // 动画链结束后的处理
22    ... ...
23  });
24
25  element.animate.play();
26})


其中animatePath的配置,为了满足不同的场景需求,我们可以通过各种方式去实现它的定义;


例如:

  • 在场景中通过可视化的打点操作形成路径
  • 在该场景元素的属性面板中,进行JSON数据的配置
  • 对于精准规范的路径,可以通过接口返回的数据处理
  • … …


2、创建动画链


实现小车路径集合的动画链,在遍历路径的时候,需要注意两个过程,一个是小车的moveAnimate(小车沿路径移动的动画),另一个则是每一次moveAniamte之前的rotationAniamte(小车在下一段路径动画前的朝向动画);


1createPathAnimates(element, points, done) {
 2  // 声明一个有序的动画集合,方便后面进行动画链处理
 3  const animates = [];
 4  if (points && points.length > 0) {
 5    // 获取小车的初始位置和旋转角度
 6    let { x, y, z } = element;
 7    let angle = element.ry;
 8    for (let i = 0, len = points.length; i < len; i++) {
 9      const point = points[i];
10      const x1 = point[0];
11      const z1 = point[2];
12      // 计算下一段与上一段之间的角度,创建rotateAnimate 
13      const rotate = Math.atan2(-(z1 - z), x1 - x);
14      const rotateAnimate = this.createRotateAnimate(element, rotate, angle);
15      if (rotateAnimate) {
16        animates.push(rotateAnimate);
17        angle = rotateAnimate.toAngle;
18      }
19      // 创建moveAnimate 
20      const moveAnimate = this.createMoveAnimate(element, [x, z], [x1, z1]);
21      if (moveAnimate) {
22        animates.push(moveAnimate);
23        x = x1;
24        z = z1;
25      }
26    }
27  }
28
29  // done为动画链接结束后的回调处理函数
30  animates[animates.length - 1].onDone = done;
31  let animate;
32  for (let i = 0, len = animates.length; i < len; i++) {
33    if (i > 0) {
34      animates[i - 1].chain(animates[i]);
35    } else {
36      animate = animates[i];
37    }
38  }
39
40  return animate;
41}


2.1、rotationAniamte


通过Math.atan2()方法,获取相对的偏移弧度:



与小车的当前弧度进行比较创建rotationAnimate:


1createRotateAnimate(element, toAngle, angle) {
 2  if (toAngle !== angle) {
 3    if (toAngle - angle > Math.PI) {
 4      toAngle -= Math.PI * 2;
 5    }
 6    if (toAngle - angle < -Math.PI) {
 7      toAngle += Math.PI * 2;
 8    }
 9  }
10  const rotateAnimate = new Animate({
11    from: angle,
12    to: toAngle,
13    type: 'number',
14    dur: Math.abs(toAngle - angle) * 300,
15    easing: 'easeNone',
16    onPlay() {
17      element.animate = this;
18    },
19    onUpdate(value) {
20      element.ry = value + (Math.PI / 2);
21    },
22  });
23  rotateAnimate.toAngle = toAngle;
24  return rotateAnimate;
25}


2.2、moveAnimate


通过上一段与下一段的point创建moveAniamte:


1createMoveAnimate(element, [x, z], [x1, z1]) {
 2  return new Animate({
 3    from: [x, z],
 4    to: [x1, z1],
 5    type: 'point',
 6    dur: Math.sqrt((x1 - x) ** 2 + (z1 - z) ** 2) * 100 || 100,
 7    easing: 'easeNone',
 8    onUpdate(value) {
 9      const [x, z] = value;
10      element.position = vec3.fromValues(x, element.y, z);
11    },
12  });
13}


2.3、动画链式衔接


处理好对应的rotationAniamte和moveAnimate后,采用动画实例对象的chain方法进行链式衔接,最终的动画实力对象就能够实现我们的动画效果了,同时可以处理动画链结束后的其他操作:


1createPathAnimates(element, points, done) {
 2  // 声明一个有序的动画集合,方便后面进行动画链处理
 3  const animates = [];
 4  ... ...
 5  // done为动画链接结束后的回调处理函数
 6  animates[animates.length - 1].onDone = done;
 7  let animate;
 8  for (let i = 0, len = animates.length; i < len; i++) {
 9    if (i > 0) {
10      animates[i - 1].chain(animates[i]);
11    } else {
12      animate = animates[i];
13    }
14  }
15
16  return animate;
17}


3、动画链效果


以上述示例来说,最终小车的动画效果:


微信图片_20220425131608.gif


至此,一个基于路径集合的动画链就完成了,实现的原理也并不复杂;有了这样的动画链机制,我们就可以实现以路径为核心的不同的动画效果,广泛应用到各种动画需求的场景当中。


例如,仓库中的作业流程:


微信图片_20220425131612.gif

1.gif


除了把路径动画链应用到场景元素上,我们还可以应用到三维场景的镜头上面,这样一来就能够实现巡航的动画效果:

   

微信图片_20220425131616.gif

2.gif

相关文章
|
11月前
|
存储 人工智能 Java
第一个动态结构:链表
大家好,我是王有志。今天我们一起学习线性表中的第二种数据结构:链表,也是真正意义上的第一个动态数据结构。
81 0
第一个动态结构:链表
|
11月前
|
移动开发 编解码 资源调度
分集与路径合并方式
分集与路径合并方式
112 0
分集与路径合并方式
数据结构与算法__01--单链表无顺序添加时,节点对象形成封闭环问题,无法添加同一个对象导致遍历输出时一直执行输出
单链表无顺序添加时,节点对象形成封闭环问题,无法添加同一个对象导致遍历输出时一直执行输出
有关使用Map结构替换掉复杂的if-else结构【项目使用】
有关使用Map结构替换掉复杂的if-else结构【项目使用】
169 0
|
PHP 开发者
对象遍历学习路径|学习笔记
快速学习对象遍历学习路径,有时需要把对象里的内容全部遍历出来,这种情况下还需要去控制这种遍历,这时就会用到对象遍历。
51 0
C#编程-18:遍历某一路径下所有文件和文件夹
C#编程-18:遍历某一路径下所有文件和文件夹
456 0
Dataway - 解决 DataQL 返回数据单复数结构不一致情况
Dataway - 解决 DataQL 返回数据单复数结构不一致情况
279 0
集合或映射迭代过程进行删除或修改操作的时候会导致并发异常
集合或映射迭代过程进行删除或修改操作的时候会导致并发异常
127 0
集合或映射迭代过程进行删除或修改操作的时候会导致并发异常