three js模型旋转

如何让立方体模型旋转到指定的面
父页面

 <b-modal ref="modal_mini" size="lg" centered 
              static 
              :hide-footer="true"
              :dialog-class="['modal_mini']"
              :content-class="'position-static'"
              :body-class="'p-0'"
              :header-class="['modal_headers','justify-content-between','align-items-center']" >
      <template v-slot:modal-header="{close}" style=" color: #FFF; background-color: #343a40;">
        <h6 class="m-0 text-truncate">{{ Module.name }}</h6>
        <div class="btn-group">
          <button class="btn mr-2" @click="maximize($refs['threeD'+Module.id])">
            <font-awesome-icon :icon="['far', 'window-maximize']"/>
          </button>
          <button class="btn" @click="close()">
            <font-awesome-icon icon="times-circle"/>
          </button>
        </div>
      </template>
      <div class="modal_body">
        <div class="row col-auto justify-content-between align-items-center"
              style="padding: 5px 20px">
          <div class="btn-group col-auto">
            <b-button variant="primary" class="btn" size="sm"
                      @click="showModalSave(Module.id, Module.rawPath)">
              <font-awesome-icon icon="cogs"/>
              {{ $t('设置') }}
            </b-button>
            <b-button variant="primary" class="btn" size="sm"
                      @click="capture($refs['threeD'+Module.id])">
              <font-awesome-icon icon="save"/>
              {{ $t('保存截图') }}
            </b-button>
          </div>
          <b-input-group :key="Module.id" size="sm" class="col flex-shrink-1 ml-4">
            <b-input-group-prepend>
              <b-input-group-text class="px-3">x</b-input-group-text>
            </b-input-group-prepend>

            <b-form-input style="height: 38px" v-model="Module.x" :formatter="format_positiveNumber" type="number"
                          min="0.00"></b-form-input>
            <b-input-group-prepend>
              <b-input-group-text class="px-3">y</b-input-group-text>
            </b-input-group-prepend>
            <b-form-input style="height: 38px" v-model="Module.y" :formatter="format_positiveNumber" type="number"
                          min="0.00"></b-form-input>
            <b-input-group-prepend>
              <b-input-group-text class="px-3">z</b-input-group-text>
            </b-input-group-prepend>
            <b-form-input style="height: 38px" v-model="Module.z" :formatter="format_positiveNumber" type="number"
                          min="0.00"></b-form-input>

            <b-input-group-append>
              <b-input-group-text class="px-3 cursor-pointer"
                                  @click="XYZ($refs['threeD'+Module.id],0,Module)">{{ $t('确定') }}
              </b-input-group-text>
              <b-input-group-text class="px-3 cursor-pointer"
                                  @click="reset($refs['threeD'+Module.id],Module)">{{ $t('还原') }}
              </b-input-group-text>
            </b-input-group-append>
          </b-input-group>
        </div>
        <div class="row col-auto justify-content-between align-items-center"
              style="padding: 5px 20px">
          <b-input-group-prepend>
            <img :src="modelPath" alt="" class="modelePathImg">
            <el-select v-model="value" placeholder="" @change="takeAxialDirectionType($event,Module)" style="width: 156px;">
              <el-option
              v-for="item in modelImgList"
              :key="item.value"
              :label="item.label"
              :value="item.value">
                <div class="modeleBox">
                  <img :src="item.img" alt="" class="modelImg">
                </div> 
              </el-option>

            </el-select>
            
            <!-- <b-form-select v-model="axialDirection" @change="takeAxialDirectionType($event,Module)" style="width: 156px;">
              <option value="top" style="width:10px;height: 10px;">
                <div style="width:10px;height: 20px;">
                  <img src="../assets/3D/顶部视角.png" alt="" style="display: block;width:10px;height: 10px;">
                </div>
              </option>
              <option value="left">{{ $t('固定Y轴') }}</option>
              <option value="right">{{ $t('固定Z轴') }}</option>
              <option value="bottom">{{ $t('固定Z轴') }}</option>
              <option value="ago">{{ $t('固定Z轴') }}</option>
              <option value="after">{{ $t('固定Z轴') }}</option>
            </b-form-select> -->
          </b-input-group-prepend>
          <b-input-group :key="Module.id" size="sm" class="col flex-shrink-1 ml-4">

            <b-input-group-prepend>
              <b-input-group-text class="px-3">x</b-input-group-text>
            </b-input-group-prepend>

            <b-form-input style="height: 38px" v-model="Module.scaleX" :formatter="bigZeroNumAndFloat" type="number"
                          min="0"></b-form-input>
            <b-input-group-prepend>
              <b-input-group-text class="px-3">y</b-input-group-text>
            </b-input-group-prepend>
            <b-form-input style="height: 38px" v-model="Module.scaleY" :formatter="bigZeroNumAndFloat" type="number"
                          min="0.00"></b-form-input>
            <b-input-group-prepend>
              <b-input-group-text class="px-3">z</b-input-group-text>
            </b-input-group-prepend>
            <b-form-input style="height: 38px" v-model="Module.scaleZ" :formatter="bigZeroNumAndFloat" type="number"
                          min="0.00"></b-form-input>

            <b-input-group-append>
              <b-input-group-text class="px-5 cursor-pointer"
                                  @click="takeScaleType('big',Module)">确定
              </b-input-group-text>
              <!-- <b-input-group-text class="px-3 cursor-pointer"
                                  @click="takeScaleType('big',Module)">{{ $t('放大') }}
              </b-input-group-text>
              <b-input-group-text class="px-3 cursor-pointer"
                                  @click="takeScaleType('small',Module)">{{ $t('缩小') }}
              </b-input-group-text> -->
            </b-input-group-append>
          </b-input-group>
        </div>
        <div class="row cards" style="padding: 5px 20px">
          <three-d
              v-if="Module.isShow"
              :ref="'threeD'+Module.id"
              :xyz="Module.xyz"
              :imgs="Module.imgs"
              :key="Module.id"
              :axialDirection="axialDirection"
              :scaleType="scaleType"
              :scaleXYZ="[Module.scaleX,Module.scaleY,Module.scaleZ]"
              :aspect_w="794"
              :aspect_h="450"></three-d>
        </div>
        <!-- <div class="select_color" @click="getRatio"  :style="{'background':colorBar}">
          <el-color-picker v-model="selectColor" show-alpha :predefine="predefineColors"
                @change="setColor($event,0,Module.id,$refs['threeD'+Module.id],Module)"></el-color-picker>
        </div> -->
      </div>
    </b-modal>

效果:在这里插入图片描述

<template>
  <div :class="{fullModal:fullScreen}">
    <font-awesome-icon v-show="fullScreen" icon="times-circle" class="fullModal_close" @click="mini()"/>
    <div ref="container" id="container"></div>
  </div>
</template>

<script>
import {
  BoxGeometry,
  BufferAttribute,
  Color,
  DirectionalLight,
  DoubleSide,
  EdgesGeometry,
  Fog,
  Group,
  LineBasicMaterial,
  LineSegments,
  Mesh,
  MeshBasicMaterial,
  PerspectiveCamera,
  PlaneBufferGeometry,
  Scene,
  TextureLoader,
  WebGLRenderer
} from "three";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module.js";
import dat from "three/examples/jsm/libs/dat.gui.module.js";

export default {
  name: "ThreeD",
  data() {
    return {
      rotation:'',
      rotationType:null,
      scene: null,
      box: { 
        scale:{
        x:null,
        y:null,
        z:null
      }
      }, // 立方体Group
      plane:{
        scale:{
          x:null,
          y:null,
          z:null
        }
      }, // 三个平面Group
      plane_x: null, // x方向的面
      plane_y: null, // y方向的面
      plane_z: null, // z方向的面
      camera: null,
      container: null,
      renderer: null,
      stats: null,
      // aspect_w: 600, // 宽高比_宽
      // aspect_h: 450, // 宽高比_高
      fullScreen: false,
      // aspect_w: window.innerWidth, // 宽高比_宽
      // aspect_h: window.innerHeight, // 宽高比_高
      boxCopy: null
    };
  },
  props: {
    static: {
      type: Boolean,
      default: () => false,
    },
    xyz: {
      type: Array,
      default: () => [100, 100, 100],
    },
    imgs: {
      type: Array,
      // default: () => [
      // 	require('@/assets/3d_demo/right.jpg'),
      // 	require('@/assets/3d_demo/left.jpg'),
      // 	require('@/assets/3d_demo/top.jpg'),
      // 	require('@/assets/3d_demo/bottom.jpg'),
      // 	require('@/assets/3d_demo/front.jpg'),
      // 	require('@/assets/3d_demo/back.jpg'),
      // ]
    },
    axialDirection: {
      type: String,
      default: ''
    },
    scaleType: {
      type: String,
      default: ''
    },
    aspect_w: {
      type: Number,
      default: 600
    },
    aspect_h: {
      type: Number,
      default: 450
    },
    scaleXYZ: {
      type: Array,
      default: () => {
        return [1, 1, 1]
      }
    },
  },
  methods: {
    takeScale() {
          this.box.scale.x = this.scaleXYZ[0];
          this.plane.scale.x = this.scaleXYZ[0];
          this.box.scale.y = this.scaleXYZ[1];
          this.plane.scale.y = this.scaleXYZ[1];
          this.box.scale.z = this.scaleXYZ[2];
          this.plane.scale.z = this.scaleXYZ[2];
      // if (this.scaleType === 'big') {
      //   if (this.axialDirection === 'X') {
      //     console.log(this.scaleXYZ[0])
      //     this.box.scale.x += this.scaleXYZ[0];
      //     console.log(this.scaleXYZ[0])
      //     this.plane.scale.x += this.scaleXYZ[0];
      //   } else if (this.axialDirection === 'Y') {
      //     this.box.scale.y += this.scaleXYZ[1];
      //     this.plane.scale.y += this.scaleXYZ[1];
      //   } else if (this.axialDirection === 'Z') {
      //     this.box.scale.z += this.scaleXYZ[2];
      //     this.plane.scale.z += this.scaleXYZ[2];
      //   }else if(this.axialDirection === ''){
      //     console.log(this.scaleXYZ)
      //     this.box.scale.x += this.scaleXYZ[0];
      //     this.plane.scale.x += this.scaleXYZ[0];
      //     this.box.scale.y += this.scaleXYZ[1];
      //     this.plane.scale.y += this.scaleXYZ[1];
      //     this.box.scale.z += this.scaleXYZ[2];
      //     this.plane.scale.z += this.scaleXYZ[2];
      //   }
      // } else if (this.scaleType === 'small') {
      //   if (this.axialDirection === 'X') {
      //     this.box.scale.x -= this.scaleXYZ[0];
      //     this.plane.scale.x -= this.scaleXYZ[0];
      //   } else if (this.axialDirection === 'Y') {
      //     this.box.scale.y -= this.scaleXYZ[1];
      //     this.plane.scale.y -= this.scaleXYZ[1];
      //   } else if (this.axialDirection === 'Z') {
      //     this.box.scale.z -= this.scaleXYZ[2];
      //     this.plane.scale.z -= this.scaleXYZ[2];
      //   }else if(this.axialDirection === ''){
      //     this.box.scale.x -= this.scaleXYZ[0];
      //     this.plane.scale.x -= this.scaleXYZ[0];
      //     this.box.scale.y -= this.scaleXYZ[1];
      //     this.plane.scale.y -= this.scaleXYZ[1];
      //     this.box.scale.z -= this.scaleXYZ[2];
      //     this.plane.scale.z -= this.scaleXYZ[2];
      //   }
      // } else {
      //   this.box.scale.x = 1
      //   this.plane.scale.x = 1
      //   this.box.scale.y = 1
      //   this.plane.scale.y = 1
      //   this.box.scale.z = 1
      //   this.plane.scale.z = 1
      // }
    },
    **// 模型旋转
    **modelRotation(type){
      if(this.rotationType == 'x'){
        if(this.rotation == 'Math.PI/2'){
          this.box.rotateX(Math.PI/-2);//上面
        }else if(this.rotation == 'Math.PI/-2'){
          this.box.rotateX(Math.PI/2)
        }
      }else if(this.rotationType == 'y'){
        if(this.rotation == 'Math.PI/2'){
          this.box.rotateY(Math.PI/-2)
        }else if(this.rotation == 'Math.PI/-2'){
          this.box.rotateY(Math.PI/2)
        }else if(this.rotation == 'Math.PI/180'){
          this.box.rotateY(Math.PI/-180)
        }else if(this.rotation == 'Math.PI/1'){
          this.box.rotateY(Math.PI/1)
        }
      }
      if(type ==1){
        this.box.rotateX(Math.PI/2);//上面
        this.rotation = 'Math.PI/2'
        this.rotationType = 'x'
      }else if(type==2){
        this.box.rotateX(Math.PI/-2);//下面
        this.rotation = 'Math.PI/-2'
        this.rotationType = 'x'
      }else if(type==3){
        this.box.rotateY(Math.PI/2);//左面
        this.rotation = 'Math.PI/2'
        this.rotationType = 'y'
      }else if(type==4){
        this.box.rotateY(Math.PI/-2);//右面
        this.rotation = 'Math.PI/-2'
        this.rotationType = 'y'
      }else if(type ==5){
        this.box.rotateY(Math.PI/180);//前面
        this.rotation = 'Math.PI/180'
        this.rotationType = 'y'
      }else if(type ==6){
        this.box.rotateY(Math.PI/1);//前面
        this.rotation = 'Math.PI/1'
        this.rotationType = 'y'
      }
    },****
    initGui() { // 调试插件
      new dat.GUI();
    },
    initScene() { // 场景
      this.scene = new Scene();
      this.scene.background = new Color(0x3c3838);
      this.scene.fog = new Fog(0x050505, 2000, 3500);
    },
    initCamera() { // 相机
      this.camera = new PerspectiveCamera(35, this.aspect_w / this.aspect_h, 0.1, 50000);
      const max = Math.max(...this.xyz) + 20;
        let num = 1000/max  
        this.camera.position.z = max+100;
        this.camera.position.x = max-4000*num;
        this.camera.position.y = max-300;
      console.log(max)
      

    },
    initLight() { // 灯光
      const light = new DirectionalLight(0xffffff, 5.0); 
      light.position.set(500, 500, 3500);
      this.scene.add(light);
      const light1 = new DirectionalLight(0xffffff, 1); 
      light.position.set(1400, -3500, 1400);
      this.scene.add(light1);
      const light2 = new DirectionalLight(0xffffff, 1); 
      light.position.set(-1400, -3500, 1400);
      this.scene.add(light2);
      const light3 = new DirectionalLight(0xffffff, 1); 
      light.position.set(-3500, -3500, -3500);
      this.scene.add(light3);
    },
    initRenderer() { // 渲染器
      this.renderer = new WebGLRenderer();
      this.renderer.setPixelRatio(window.devicePixelRatio);
      const h = this.container.clientWidth / this.camera.aspect;
      const w = this.container.clientWidth;
      if (w === 0) {
        this.renderer.setSize(this.aspect_w, this.aspect_h);
      } else {
        this.renderer.setSize(w, h);
      }
      this.container.appendChild(this.renderer.domElement);
    },
    initStats() { // 性能插件
      this.stats = new Stats();
      this.container.appendChild(this.stats.dom);
    },
    initControls() { // 控制器
      this.controls = new OrbitControls(this.camera, this.container);
    },
    initContent() {
      if (!this.imgs || this.imgs.length < 1) return;
      const box = new BoxGeometry(this.xyz[0], this.xyz[1], this.xyz[2]); // XYZ
      // 加载六个面的纹理贴图
      const textureLoader = new TextureLoader();
      // textureLoader.setCrossOrigin('');
      // 材质数组
      const materialArr = this.imgs.map(img => new MeshBasicMaterial({map: textureLoader.load(img)}));
      const mesh = new Mesh(box, materialArr);
      this.mesh = mesh
 
      // mesh.scale.set(2, 1.5, 2); 模型缩放
      // 边缘棱线
      const edges = new EdgesGeometry(box, 1);
      const line = new LineSegments(edges, new LineBasicMaterial({color: 0xffffff}));
      this.box = new Group();
      this.box.add(mesh);
      this.box.add(line);
      this.boxCopy = JSON.parse(JSON.stringify(this.box))
      this.scene.add(this.box);
    },
    update() {
      // this.stats.update();
      this.controls.update();
    },
    animate() {
      requestAnimationFrame(this.animate);
      this.renderer.render(this.scene, this.camera);
      this.update();
    },

    init() {
      if (this.fullScreen) {
        this.aspect_w = window.innerWidth; // 宽高比_宽
        this.aspect_h = window.innerHeight; // 宽高比_高
      }
      this.container = this.$refs.container;
      this.initScene();
      this.initCamera();
      this.initRenderer();
      this.initLight();
      // this.initGui();
      // this.initStats();
      this.initContent();
      this.initControls();

      // 窗口变动触发的方法
      const onWindowResize = () => {
        // 重新设置相机的宽高比
        this.camera.aspect = this.aspect_w / this.aspect_h;
        // 更新相机投影矩阵
        this.camera.updateProjectionMatrix();
        // 更新渲染器大小
        const h = this.container.clientWidth / this.camera.aspect;
        const w = this.container.clientWidth;
        this.renderer.setSize(w, h);
      };
      window.addEventListener('resize', onWindowResize, false);
    },
    capture() { // 截图
      this.renderer.render(this.scene, this.camera); // 先渲染一帧,防止截图内容为空
      const src = this.renderer.domElement.toDataURL("image/jpeg"); //转化为base64
      this.$emit('capture', src);
      return src;
    },
    // initThreePlanes({x = 0, y = 0, z = 0} = {}, {xImgUrl, yImgUrl, zImgUrl}) {
    initThreePlanes(obj, {xImgUrl, yImgUrl, zImgUrl}) {
      // 注意:后端的z对应前端的y,后端的x对应前端的z,后端的y对应前端的x
      // let y = obj.y;
      // let z = obj.z;
      // let x = obj.x;

      // let y = obj.z;
      // let z = obj.x;
      // let x = obj.y;

      let x = obj.x;
      let y = obj.y;
      let z = obj.z;

      x -= 1;
      y -= 1;
      z -= 1;
      const textureLoader = new TextureLoader();
      // const [z_length, x_length, y_length] = this.xyz;
      const [x_length, y_length, z_length] = this.xyz;
      // y = y_length - y; // 后端的这个方向相反
      // z = y_length - z; // 后端的这个方向相反

      if (this.plane) {
        this.scene.remove(this.plane);
      }
      this.plane = new Group();

      if (zImgUrl) {
        // z方向的面
        const geometry_z = new PlaneBufferGeometry();
        const vertices_z = new Float32Array([
          0, y_length, z, // 左上
          x_length, y_length, z, // 右上
          0, 0, z, // 左下
          x_length, 0, z // 右下
          // 0, y_length, 360, // 左上
          // x_length, y_length, 360, // 右上
          // 0, 0, 360, // 左下
          // x_length, 0, 360 // 右下
        ]);
        geometry_z.addAttribute("position", new BufferAttribute(vertices_z, 3));
        const material_z = new MeshBasicMaterial({side: DoubleSide, map: textureLoader.load(zImgUrl)});
        this.plane_z = new Mesh(geometry_z, material_z);
        this.plane.add(this.plane_z);
      }

      if (yImgUrl) {
        const geometry_y = new PlaneBufferGeometry();
        const vertices_y = new Float32Array([
          0, y, 0, // 左上
          x_length, y, 0, // 右上
          0, y, z_length, // 左下
          x_length, y, z_length, // 右下
        ]);
        geometry_y.addAttribute("position", new BufferAttribute(vertices_y, 3));
        const material_y = new MeshBasicMaterial({side: DoubleSide, map: textureLoader.load(yImgUrl)});
        this.plane_y = new Mesh(geometry_y, material_y);
        this.plane.add(this.plane_y);
      }

      if (xImgUrl) {
        const geometry_x = new PlaneBufferGeometry();
        const vertices_x = new Float32Array([
          x, y_length, 0, // 左上
          x, y_length, z_length, // 右上
          x, 0, 0, // 左下
          x, 0, z_length // 右下
        ]);
        geometry_x.addAttribute("position", new BufferAttribute(vertices_x, 3));
        const material_x = new MeshBasicMaterial({side: DoubleSide, map: textureLoader.load(xImgUrl)});
        this.plane_x = new Mesh(geometry_x, material_x);
        this.plane.add(this.plane_x);
      }
      // 将模型中心移到坐标系原点
      this.plane.translateX(-x_length / 2);
      this.plane.translateY(-y_length / 2);
      this.plane.translateZ(-z_length / 2);
      this.scene.add(this.plane);
    },
    showThreePlane(xyz, imgs) {
      this.initThreePlanes(xyz, imgs);
      this.plane.visible = true;
      this.box.visible = false;
      const max = Math.max(...this.xyz) + 250;
      this.camera.position.z = max;
      this.camera.position.x = max;
      this.camera.position.y = max;
    },
    showBox() {
      this.box.visible = true;
      this.plane.visible = false;
      const max = Math.max(...this.xyz) + 50;
      this.camera.position.z = max;
      this.camera.position.x = max;
      this.camera.position.y = max;
    },
    maximize() {
      this.aspect_w = window.innerWidth; // 宽高比_宽
      this.aspect_h = window.innerHeight; // 宽高比_高
      this.camera.aspect = this.aspect_w / this.aspect_h;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(this.aspect_w, this.aspect_h);
      this.fullScreen = true;
    },
    mini() {
      this.aspect_w = this.aspect_w || 600; // 宽高比_宽
      this.aspect_h = this.aspect_h || 450; // 宽高比_高
      this.camera.aspect = this.aspect_w / this.aspect_h;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(this.aspect_w, this.aspect_h);
      this.fullScreen = false;
    },
  },
  mounted() {
    this.init();
    this.animate();
  }
};
</script>

<style scoped lang="scss">
#container {
  height: 100%;
  width: 100%;
}

::v-deep canvas {
  width: 100% !important;
  height: 100% !important;
}

.fullModal {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 2042;
  width: 100vw;
  height: 100vh;
  background-color: #000;

  &_close {
    position: fixed;
    cursor: pointer;
    top: 5px;
    right: 5px;
    height: 25px;
    width: 25px;
    z-index: 2043;
    color: #FFF;
  }
}
</style>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值