Three开关门

一. 我们在使用Three想要达成开关门动作时,如何避坑?

 

图一为我们理想例子

图二图三为非理想例子

二. Three开关门如何点击获取该模型?

1. 我们以双击触发该事件,通过鼠标xy轴在屏幕创建three向量位置;

2. 将创建屏幕坐标转换为Three场景中的坐标;

3. 通过THREE.Raycaster发射光线获取到点击模型;

4. 通过TWEEN动画旋转该模型父级场景(重点为旋转父级场景,自身旋转可就如图二喽)rotationY角度;

document.addEventListener('dblclick', onDocumentMouseDown, false);
// 获取与射线相交的对象数组
  const onDocumentMouseDown = (event) => {
    event.preventDefault();
    //1、先基于我们在屏幕上点击的位置创建一个向量
    var vector = new THREE.Vector3(
      (event.clientX / window.innerWidth) * 2 - 1,
      -(event.clientY / window.innerHeight) * 2 + 1,
      0.5
    );
    //2、然后用unproject函数将点击位置转换成Thres.js场景中的坐标
    vector = vector.unproject(Three.camera);
    //3、用THREE.Raycaster对象向点击位置发射光线
    var raycaster = new THREE.Raycaster(Three.camera.position, vector.sub(Three.camera.position).normalize());
    var intersects = raycaster.intersectObjects(Three.scene.children, true);

    var currObj = intersects[0].object; //currObj为点击到的第一个对象
    if (currObj.parent.rotation.y == 0) {
      new TWEEN.Tween(currObj.parent.rotation)
        .to(
          {
            y: 1.5,
          },
          1500
        )
        .start();
    } else {
      new TWEEN.Tween(currObj.parent.rotation)
        .to(
          {
            y: 0,
          },
          300
        )
        .start();
    }
  };

 三. 模型核心场景代码

1. 重点在于mesh与menGroup;mesh为门模型,menGroup为mesh父级场景;

  1.  门宽为40,模型默认在中心轴上那么中心轴的位置在门20的位置;
  2. 我们建立父级场景menGroup将门mesh包裹;
  3. 父级menGroup向右平移20,此时menGroup中心轴在门右侧门框上,随着父级平移,mesh作为子级也会平移,此时我们将mesh门本身向左平移-20,门又回到了原始位置,但是父级menGroup的中心轴还停留在右侧门框;
  4. 此时参考上边currObj.parent.rotation代码,我们获取到门之后旋转父级,大功告成;
  5. 注意事项:不平移自转为图二,平移计算不对为图三;
// 立方体
  const cube = () => {
    const cubeSize = 40;
    const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, 1);
    const cubeMat = new THREE.MeshPhongMaterial({ color: '#8AC' });
    const mesh = new THREE.Mesh(cubeGeo, cubeMat); // 创景门模型
    mesh.position.set(-cubeSize / 2, 0, 0);

    const menGroup = new THREE.Group();
    menGroup.position.set(cubeSize, 0, 0);
    menGroup.add(mesh);
    
    Three.scene.add(menGroup);
  };

 四. react版全部代码

import React, { useRef, useEffect, useMemo } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import TWEEN from 'tween/tween.js';

const ThreeRoom = () => {
  const createDom = useRef(null);
  const ThreeModel = useRef({
    scene: null,
    camera: null,
    renderer: null,
    controller: null,
  });
  const Three = ThreeModel.current;

  // 相机加载
  const cameraInit = () => {
    Three.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
    Three.camera.position.set(0, 50, 50);
    // Three.camera.lookAt(0, 0, 0);
  };

  // 渲染器实例
  const renderer = () => {
    Three.renderer = new THREE.WebGL1Renderer();
    Three.renderer.setSize(createDom.current.clientWidth,         createDom.current.clientHeight);
    Three.renderer.setPixelRatio(window.devicePixelRatio);
    Three.renderer.setClearAlpha(0);
    Three.renderer.setClearColor('rgb(135,206,250)', 1.0);
    Three.renderer.setClearColor(0xffffff, 1.0);
    Three.renderer.setClearColor('#428bca', 1.0);
    Three.renderer.setClearColor('rgba(135,206,250,0.5)', 1.0);
    createDom.current.appendChild(Three.renderer.domElement);
  };

  // 加载场景
  const animate = () => {
    requestAnimationFrame(animate);
    // if (resizeRendererToDisplaySize(Three.renderer)) {
    //   Three.camera.aspect = window.innerWidth / window.innerHeight;
    //   Three.camera.updateProjectionMatrix();
    // }
    TWEEN.update();
    Three.renderer.render(Three.scene, Three.camera);
  };

  // 立方体
  const cube = () => {
    const cubeSize = 40;
    const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, 1);
    const cubeMat = new THREE.MeshPhongMaterial({ color: '#8AC' });

    const mesh = new THREE.Mesh(cubeGeo, cubeMat); // 创景门模型
    mesh.position.set(-cubeSize / 2, 0, 0);
    const menGroup = new THREE.Group();
    menGroup.position.set(cubeSize, 0, 0);
    menGroup.add(mesh);

    Three.scene.add(menGroup);
  };

  // 加载控制器
  const controls = () => {
    Three.controller = new OrbitControls(Three.camera, Three.renderer.domElement);
    Three.controller.maxDistance = 2000;
  };

  useEffect(() => {
    Three.scene = new THREE.Scene();
    cameraInit();
    cube();
    renderer();
    controls();
    const light = new THREE.AmbientLight(0xffffff, 1);
    Three.scene.add(light);
    animate();
    document.addEventListener('dblclick', onDocumentMouseDown, false);
  }, []);

  // 获取与射线相交的对象数组
  const onDocumentMouseDown = (event) => {
    event.preventDefault();
    //1、先基于我们在屏幕上点击的位置创建一个向量
    var vector = new THREE.Vector3(
      (event.clientX / window.innerWidth) * 2 - 1,
      -(event.clientY / window.innerHeight) * 2 + 1,
      0.5
    );
    //2、然后用unproject函数将点击位置转换成Thres.js场景中的坐标
    vector = vector.unproject(Three.camera);
    //3、用THREE.Raycaster对象向点击位置发射光线
    var raycaster = new THREE.Raycaster(Three.camera.position, vector.sub(Three.camera.position).normalize());
    var intersects = raycaster.intersectObjects(Three.scene.children, true);

    var currObj = intersects[0].object; //currObj为点击到的第一个对象
    if (currObj.parent.rotation.y == 0) {
      new TWEEN.Tween(currObj.parent.rotation)
        .to(
          {
            y: 1.5,
          },
          1500
        )
        .start();
    } else {
      new TWEEN.Tween(currObj.parent.rotation)
        .to(
          {
            y: 0,
          },
          300
        )
        .start();
    }
  };

  return <div style={{ width: '100%', height: '100%' }} id="scene" ref={createDom} />;
};

export default ThreeRoom;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值