vue2.x+threejs 实现转动动画

父组件:
DOM:

<device-info-animate
    :depth-value='depthValue'
    :scope-value='scopeValue'
    :rotate-value='rotateValue'
    :propeller-run='true'
    style='height: 180px'>
</device-info-animate>
data(){
return{
depthValue:0,
scopeValue:0,
rotateValue:0
}
}

动画组件:

<template>
  <div id='container' class='device_info_animate'></div>
</template>
<script>
import * as THREE from 'three';
import { cloneDeep } from 'lodash';
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
let _scene = undefined;
let _camera = undefined;
let _renderer = undefined;
let _renderXFrame_new = undefined;
let _renderZFrame_new = undefined;
let _renderYFrame_new = undefined;
let _animateFrame = undefined;
export default {
  name: 'DeviceInfoAnimate',
  props: {
    depthValue: {
      type: Number,
      value: 0,
    },
    scopeValue: {
      type: Number,
      value: 0,
    },
    rotateValue: {
      type: Number,
      value: 0,
    },
    propellerRun: {
      type: Boolean,
      value: true,
    },
  },
  watch: {
    depthValue: {
      handler(val) {
        if (val || val === 0) {
          let num = Number(val).toFixed(2);
          this.cDepthValueNew = Number(Math.round(num / 0.2) * 0.2);
          this.animateXRenderNew();
        }
      },
      immediate: true,
    },
    scopeValue: {
      handler(val) {
        if (val || val === 0) {
          let num = Number(val).toFixed(2);
          this.cScopeValueNew = Number(Math.round(num / 0.2) * 0.2);
          this.animateZRenderNew();
        }
      },
      immediate: true,
    },
    rotateValue: {
      handler(val) {
        if (val || val === 0) {
          let num = Number(val).toFixed(2);
          this.cRotateValueNew = Number(Math.round(num / 0.2) * 0.2);
          this.animateYRenderNew();
        }
      },
      immediate: true,
    },
    propellerRun: {
      handler(val) {
        if (val) {
          if (this.AnimationAction) {
            this.AnimationAction.play();
          }
        } else {
          if (this.AnimationAction) {
            this.AnimationAction.stop();
          }
        }
      },
      immediate: true,
    },
  },
  data() {
    return {
      mixer: undefined,
      clock: undefined,
      saveDepthPositionNew: 0, // 当前depth的值
      saveScopePositionNew: 0, // 当前depth的值
      saveRotatePositionNew: 0, // 当前depth的值
      cDepthValueNew: 0, // 当前需要转动到的depth目标值
      cScopeValueNew: 0, // 当前需要转动到的scope目标值
      cRotateValueNew: 0, // 当前需要转动到的rotate目标值
      AnimationAction: undefined,
      renderXaxis: 0,
      renderYaxis: 0,
      renderZaxis: 0,
    };
  },
  mounted() {
    this.init();
  },
  destroyed() {},
  methods: {
    initAnimation() {
      window.cancelAnimationFrame(_renderXFrame_new); //可以取消该次动画。
      this.animateXRenderNew();
      window.cancelAnimationFrame(_renderZFrame_new); //可以取消该次动画。
      this.animateZRenderNew();
      window.cancelAnimationFrame(_renderYFrame_new); //可以取消该次动画。
      this.animateYRenderNew();
    },
    async init() {
      let _this = this;
      _scene = new THREE.Scene();
      // 立方体网格模型
      let geometry1 = new THREE.BoxGeometry(30, 20, 50);
      let material1 = new THREE.MeshLambertMaterial({
        color: 0xff0000,
      }); //材质对象Material
      let cube = new THREE.Mesh(geometry1, material1); //网格模型对象Mesh
      cube.name='sheng-max'
      _scene.add(cube)
      let axesHelper = new THREE.AxesHelper(250);
      _scene.add(axesHelper);
      console.log('______scene', _scene);
      _this.renderXaxis = new THREE.Vector3(500, 0, 500); //创建一个三维向量
      _this.renderYaxis = new THREE.Vector3(0, 500, 0); //创建一个三维向量
      _this.renderZaxis = new THREE.Vector3(500, 0, -500); //创建一个三维向量
      // // 环境光
      // let ambient = new THREE.AmbientLight(0xffffff, 0.5);
      // _scene.add(ambient);
      // 方向光1
      let _directionalLight = new THREE.DirectionalLight(0xffffff, 1);
      _directionalLight.position.set(-50, 5, -50);
      _scene.add(_directionalLight);
      // 方向光2
      let directionalLight = new THREE.DirectionalLight(0xffffff, 0.7);
      directionalLight.position.set(50, 50, 50);
      _scene.add(directionalLight);
      // 方向光2
      let directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.7);
      directionalLight2.position.set(-70, 5, 50);
      _scene.add(directionalLight2);
      // 方向光2
      let directionalLight3 = new THREE.DirectionalLight(0xffffff, 0.7);
      directionalLight3.position.set(70, 5, -50);
      _scene.add(directionalLight3);
      const element = document.getElementById('container');
      const width = element.clientWidth; // 窗口宽度
      const height = element.clientHeight; // 窗口高度
      const k = width / height; // 窗口宽高比
      let s = 150; //三维场景显示范围控制系数,系数越大,显示的范围越大
      // PerspectiveCamera( fov, aspect, near, far )
      _camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
      _camera.position.set(200, 300, 200); //设置相机位置
      _camera.lookAt(_scene.position); //设置相机方向(指向的场景对象)
      _scene.add(_camera);
      _renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: false,
      });
      _renderer.setSize(element.clientWidth, element.clientHeight); // 设置渲染区域尺寸
      _renderer.shadowMap.enabled = true; // 显示阴影
      // _renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      _renderer.setClearColor(0x000000, 0); // 设置背景颜色
      element.appendChild(_renderer.domElement);
      let _controls;
      _controls = new OrbitControls(_camera, _renderer.domElement);
      this.render();
    },
    render() {
      _renderer.render(_scene, _camera); //执行渲染操作
    },
    animateXRenderNew() {
      let model = undefined;
      if (_scene && _scene.children) {
        _scene.children.forEach((item) => {
          if (item.name === 'sheng-max') {
            model = item;
          }
        });
      }
      let rotWorldMatrix = new THREE.Matrix4(); //创建一个4*4矩阵
      // console.log('_________rotWorldMatrix', rotWorldMatrix);
      if (Number(this.saveDepthPositionNew) === this.cDepthValueNew) {
        window.cancelAnimationFrame(_renderXFrame_new); // 取消该次动画。
      } else {
        let geometry = new THREE.SphereGeometry(3); //创建一个球体几何对象
        let material = new THREE.MeshLambertMaterial({
          color: 0x0000ff,
        }); //材质对象Material
        let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
        mesh.position.x = 0;
        mesh.position.y = 0;
        mesh.position.z = 0;
        _scene.add(mesh); //网格模型添加到场景中
        // 'AxesHelper'
        let arr1 = [];
        _scene.children.forEach((obj) => {
          if (obj.type !== 'AxesHelper') {
            arr1.push(obj);
          }
        });
        this.$set(_scene, 'children', arr1);
        let axesHelper = new THREE.AxesHelper(250);
        _scene.add(axesHelper);
        // console.log('________normalize', this.renderXaxis.normalize());
        if (model && model.matrix) {
          if (this.cDepthValueNew === 0) {
            if (Number(this.saveDepthPositionNew) > 0) {
              this.saveDepthPositionNew = Number(
                  Number(this.saveDepthPositionNew) - 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderXaxis.normalize(),
                  (-1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            } else if (Number(this.saveDepthPositionNew) < 0) {
              this.saveDepthPositionNew = Number(
                  Number(this.saveDepthPositionNew) + 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderXaxis.normalize(),
                  (1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            }
          }
          if (this.cDepthValueNew > 0 && this.cDepthValueNew <= 90) {
            if (Number(this.saveDepthPositionNew) <= 0) {
              this.saveDepthPositionNew = Number(
                  Number(this.saveDepthPositionNew) + 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderXaxis.normalize(),
                  (1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            } else {
              if (this.cDepthValueNew > this.saveDepthPositionNew) {
                this.saveDepthPositionNew = Number(
                    Number(Number(this.saveDepthPositionNew)) + 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderXaxis.normalize(),
                    (1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              } else {
                this.saveDepthPositionNew = Number(
                    Number(this.saveDepthPositionNew) - 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderXaxis.normalize(),
                    (-1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              }
            }
          }
          if (this.cDepthValueNew < 0 && this.cDepthValueNew >= -90) {
            if (Number(this.saveDepthPositionNew) >= 0) {
              this.saveDepthPositionNew = Number(
                  Number(this.saveDepthPositionNew) - 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderXaxis.normalize(),
                  (-1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            } else {
              if (this.cDepthValueNew > this.saveDepthPositionNew) {
                this.saveDepthPositionNew = Number(
                    Number(this.saveDepthPositionNew) + 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderXaxis.normalize(),
                    (1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              } else {
                this.saveDepthPositionNew = Number(
                    Number(this.saveDepthPositionNew) - 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderXaxis.normalize(),
                    (-1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              }
            }
          }
          if (this.cDepthValueNew < -90 || this.cDepthValueNew > 90) {
            window.cancelAnimationFrame(_renderXFrame_new); // 取消该次动画。
          }
          _renderXFrame_new = requestAnimationFrame(this.animateXRenderNew);
        }
      }
    },
    animateYRenderNew() {
      let model = undefined;
      if (_scene && _scene.children) {
        _scene.children.forEach((item) => {
          if (item.name === 'sheng-max') {
            model = item;
          }
        });
      }
      let rotWorldMatrix = new THREE.Matrix4(); //创建一个4*4矩阵
      if (Number(this.saveRotatePositionNew) === this.cRotateValueNew) {
        window.cancelAnimationFrame(_renderYFrame_new); // 取消该次动画。
      } else {
        if (model && model.matrix) {
          if (this.cRotateValueNew === 0) {
            if (Number(this.saveRotatePositionNew) > 0) {
              this.saveRotatePositionNew = Number(
                  Number(this.saveRotatePositionNew) - 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderYaxis.normalize(),
                  (-1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            } else if (Number(this.saveRotatePositionNew) < 0) {
              this.saveRotatePositionNew = Number(
                  Number(this.saveRotatePositionNew) + 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderYaxis.normalize(),
                  (1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            }
          }
          if (this.cRotateValueNew > 0 && this.cRotateValueNew <= 180) {
            if (Number(this.saveRotatePositionNew) <= 0) {
              this.saveRotatePositionNew = Number(
                  Number(this.saveRotatePositionNew) + 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderYaxis.normalize(),
                  (1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            } else {
              if (this.cRotateValueNew > this.saveRotatePositionNew) {
                this.saveRotatePositionNew = Number(
                    Number(Number(this.saveRotatePositionNew)) + 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderYaxis.normalize(),
                    (1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              } else {
                this.saveRotatePositionNew = Number(
                    Number(this.saveRotatePositionNew) - 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderYaxis.normalize(),
                    (-1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              }
            }
          }
          if (this.cRotateValueNew < 0 && this.cRotateValueNew >= -180) {
            if (Number(this.saveRotatePositionNew) >= 0) {
              this.saveRotatePositionNew = Number(
                  Number(this.saveRotatePositionNew) - 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderYaxis.normalize(),
                  (-1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            } else {
              if (this.cRotateValueNew > this.saveRotatePositionNew) {
                this.saveRotatePositionNew = Number(
                    Number(this.saveRotatePositionNew) + 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderYaxis.normalize(),
                    (1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              } else {
                this.saveRotatePositionNew = Number(
                    Number(this.saveRotatePositionNew) - 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderYaxis.normalize(),
                    (-1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              }
            }
          }
          if (this.cRotateValueNew < -180 || this.cRotateValueNew > 180) {
            window.cancelAnimationFrame(_renderYFrame_new); // 取消该次动画。
          }
          _renderYFrame_new = requestAnimationFrame(this.animateYRenderNew);
        }
      }
    },
    animateZRenderNew() {
      let model = undefined;
      if (_scene && _scene.children) {
        _scene.children.forEach((item) => {
          if (item.name === 'sheng-max') {
            model = item;
          }
        });
      }
      let rotWorldMatrix = new THREE.Matrix4(); //创建一个4*4矩阵
      if (Number(this.saveScopePositionNew) === this.cScopeValueNew) {
        window.cancelAnimationFrame(_renderZFrame_new); // 取消该次动画。
      } else {
        if (model && model.matrix) {
          if (this.cScopeValueNew === 0) {
            if (Number(this.saveScopePositionNew) > 0) {
              this.saveScopePositionNew = Number(
                  Number(this.saveScopePositionNew) - 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderZaxis.normalize(),
                  (-1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            } else if (Number(this.saveScopePositionNew) < 0) {
              this.saveScopePositionNew = Number(
                  Number(this.saveScopePositionNew) + 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderZaxis.normalize(),
                  (1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            }
          }
          if (this.cScopeValueNew > 0 && this.cScopeValueNew <= 90) {
            if (Number(this.saveScopePositionNew) <= 0) {
              this.saveScopePositionNew = Number(
                  Number(this.saveScopePositionNew) + 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderZaxis.normalize(),
                  (1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            } else {
              if (this.cScopeValueNew > this.saveScopePositionNew) {
                this.saveScopePositionNew = Number(
                    Number(Number(this.saveScopePositionNew)) + 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderZaxis.normalize(),
                    (1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              } else {
                this.saveScopePositionNew = Number(
                    Number(this.saveScopePositionNew) - 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderZaxis.normalize(),
                    (-1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              }
            }
          }
          if (this.cScopeValueNew < 0 && this.cScopeValueNew >= -90) {
            if (Number(this.saveScopePositionNew) >= 0) {
              this.saveScopePositionNew = Number(
                  Number(this.saveScopePositionNew) - 1.0
              ).toFixed(2);
              rotWorldMatrix.makeRotationAxis(
                  this.renderZaxis.normalize(),
                  (-1.0 * Math.PI) / 180
              );
              rotWorldMatrix.multiply(model.matrix);
              model.matrix = rotWorldMatrix;
              model.rotation.setFromRotationMatrix(rotWorldMatrix);
            } else {
              if (this.cScopeValueNew > this.saveScopePositionNew) {
                this.saveScopePositionNew = Number(
                    Number(this.saveScopePositionNew) + 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderZaxis.normalize(),
                    (1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              } else {
                this.saveScopePositionNew = Number(
                    Number(this.saveScopePositionNew) - 1.0
                ).toFixed(2);
                rotWorldMatrix.makeRotationAxis(
                    this.renderZaxis.normalize(),
                    (-1.0 * Math.PI) / 180
                );
                rotWorldMatrix.multiply(model.matrix);
                model.matrix = rotWorldMatrix;
                model.rotation.setFromRotationMatrix(rotWorldMatrix);
              }
            }
          }
          if (this.cScopeValueNew < -90 || this.cScopeValueNew > 90) {
            window.cancelAnimationFrame(_renderZFrame_new); // 取消该次动画。
          }
          _renderZFrame_new = requestAnimationFrame(this.animateZRenderNew);
        }
      }
    },
    animate() {
      _animateFrame = requestAnimationFrame(this.animate);
      // _controls.update();
      if (this.mixer) {
        //clock.getDelta()方法获得两帧的时间间隔
        // 更新混合器相关的时间
        this.mixer.update(this.clock.getDelta());
      }
      _renderer.render(_scene, _camera);
    },
  },
  beforeDestroy() {
    window.cancelAnimationFrame(_renderXFrame_new); //可以取消该次动画。
    window.cancelAnimationFrame(_renderZFrame_new); //可以取消该次动画。
    window.cancelAnimationFrame(_renderYFrame_new); //可以取消该次动画。
    window.cancelAnimationFrame(_animateFrame); //可以取消该次动画。
  },
};
</script>
<style lang='scss' scoped>
.device_info_animate {
  width: 96%;
  height: 100%;
  margin-left: 2%;
  margin-top: 8px;
  border-radius: 6px;
  //background-color: rgba(0, 0, 0, 0);
  background: rgba(255, 255, 255, 0.15);
  overflow: hidden;
}
</style>

问题描述:
子组件实时就收三个方向的角度值。其中 -90<=depthValue<=90 绕固定轴旋转;-90<=scopeValue<=90 绕固定轴旋转;-180<=rotateValue<=180 绕固定轴旋转
当传递一个值 执行动画时不会又问题,但是当一个动画在运行当中,接收到另外一个值或两个值,模型转动就回偏离预期效果,
例如:当接收 depthValue = 90 时开始执行动画,当执行到一半接收到 scopeValue = 90scopeValue 转动动画的转动方向不对了,当两个动画都结束后,分别接收 depthValue = 0与scopeValue = 0; 模型不会回到转动前的初始位置;为什么?解决方案与代码实现是什么?

阅读 2.6k
1 个回答

你说 depthValue 会控制转动,但是我又没有看到你基于这个值做赋值

那么大概是自动转,那么就有一个问题,比如说你基于 90 做旋转,旋转到 45 的时候,重新赋值了,暂停定时器,重新开始 90 的选择,那么这个时候结束的位置其实是 135。

所以解决办法有两个

  1. depthValue 参与进去。而不是累加
  2. 首次记录初始 x、y、z,为 0 的时候覆盖为初始值。
已参与了 SegmentFault 思否社区 10 周年「问答」打卡 ,欢迎正在阅读的你也加入。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏