一. 我们在使用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父级场景;
- // 立方体
- 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;