zoukankan      html  css  js  c++  java
  • 基于路径集合的三维动画链

    目前数据信息可视化的发展趋势越来越快,纬度宽广、数量庞大、结构复杂的数据展示仅仅只依靠二维平面图表已经不能满足了。为了更加清晰,快速的认知和理解一份数据,构建基于现实的三维虚拟可视化效果,被广泛应用到各行业中,迅速成为信息数字化管理的重要组成部分。
     
    三维场景还原了现实的虚拟效果,而各种各样的动画则赋予其更加饱满、灵动的视觉冲击。基于路径集合的三维动画链,它充实了场景中元素的动画效果,接下来我会具体介绍动画链的实现过程。

    1、配置路径数据

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

    scene.loadGLTF('./static/gltf/car1/1.gltf', {
      generateTangent: true,
      useIBLWhenMissingTexture: true,
      loadTexture: true,
    }).then((data) => {
      const element = data.root;
      element.scale = [0.1, 0.1, 0.1];
      element.ry = Math.PI;
      element.position = [98, 1.5, -125];
      
      const { y } = element;
      const animatePath = [
        [98, y, -135],
        [50, y, -135],
        [10, y, -95],
        [-30, y, -125]
      ]
      
      // 创建动画链
      element.animate = this.createPathAnimates(element, animatePath, () => {
        // 动画链结束后的处理
        ... ...
      });
      
      element.animate.play();
    })
    
    

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

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

    2、创建动画链

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

    createPathAnimates(element, points, done) {
      // 声明一个有序的动画集合,方便后面进行动画链处理
      const animates = [];
      if (points && points.length > 0) {
        // 获取小车的初始位置和旋转角度
        let { x, y, z } = element;
        let angle = element.ry;
        for (let i = 0, len = points.length; i < len; i++) {
          const point = points[i];
          const x1 = point[0];
          const z1 = point[2];
          // 计算下一段与上一段之间的角度,创建rotateAnimate 
          const rotate = Math.atan2(-(z1 - z), x1 - x);
          const rotateAnimate = this.createRotateAnimate(element, rotate, angle);
          if (rotateAnimate) {
            animates.push(rotateAnimate);
            angle = rotateAnimate.toAngle;
          }
          // 创建moveAnimate 
          const moveAnimate = this.createMoveAnimate(element, [x, z], [x1, z1]);
          if (moveAnimate) {
            animates.push(moveAnimate);
            x = x1;
            z = z1;
          }
        }
      }
    
      // done为动画链接结束后的回调处理函数
      animates[animates.length - 1].onDone = done;
      let animate;
      for (let i = 0, len = animates.length; i < len; i++) {
        if (i > 0) {
          animates[i - 1].chain(animates[i]);
        } else {
          animate = animates[i];
        }
      }
    
      return animate;
    }
    
    

    2.1、rotationAniamte

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


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

    createRotateAnimate(element, toAngle, angle) {
      if (toAngle !== angle) {
        if (toAngle - angle > Math.PI) {
          toAngle -= Math.PI * 2;
        }
        if (toAngle - angle < -Math.PI) {
          toAngle += Math.PI * 2;
        }
      }
      const rotateAnimate = new Animate({
        from: angle,
        to: toAngle,
        type: 'number',
        dur: Math.abs(toAngle - angle) * 300,
        easing: 'easeNone',
        onPlay() {
          element.animate = this;
        },
        onUpdate(value) {
          element.ry = value + (Math.PI / 2);
        },
      });
      rotateAnimate.toAngle = toAngle;
      return rotateAnimate;
    }
    

    2.2、moveAnimate

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

    createMoveAnimate(element, [x, z], [x1, z1]) {
      return new Animate({
        from: [x, z],
        to: [x1, z1],
        type: 'point',
        dur: Math.sqrt((x1 - x) ** 2 + (z1 - z) ** 2) * 100 || 100,
        easing: 'easeNone',
        onUpdate(value) {
          const [x, z] = value;
          element.position = vec3.fromValues(x, element.y, z);
        },
      });
    }
    

    2.3、动画链式衔接

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

    createPathAnimates(element, points, done) {
      // 声明一个有序的动画集合,方便后面进行动画链处理
      const animates = [];
      ... ...
      // done为动画链接结束后的回调处理函数
      animates[animates.length - 1].onDone = done;
      let animate;
      for (let i = 0, len = animates.length; i < len; i++) {
        if (i > 0) {
          animates[i - 1].chain(animates[i]);
        } else {
          animate = animates[i];
        }
      }
    
      return animate;
    }
    
    

    3、动画链效果

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

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

    例如,仓库中的作业流程:
    1.gif
    除了把路径动画链应用到场景元素上,我们还可以应用到三维场景的镜头上面,这样一来就能够实现巡航的动画效果:
    2.gif

    如果大家有好的想法,可以微信:541002349 讨论。

    也欢迎关注公众号“ITman彪叔”,接收更多消息。

  • 相关阅读:
    Oracle中有大量的sniped会话
    Error 1130: Host '127.0.0.1' is not allowed to connect to this MySQL server
    汉字转换为拼音以及缩写(javascript)
    高效率随机删除数据(不重复)
    vs2010 舒服背景 优雅字体 配置
    mvc中的ViewData用到webfrom中去
    jquery ajax return值 没有返回 的解决方法
    zShowBox (图片放大展示jquery版 兼容性好)
    动感效果的TAB选项卡 jquery 插件
    loading 加载提示······
  • 原文地址:https://www.cnblogs.com/flyfox1982/p/14041026.html
Copyright © 2011-2022 走看看