253 lines
7.2 KiB
Vue
253 lines
7.2 KiB
Vue
<template>
|
||
<div>
|
||
<canvas id="three"></canvas>
|
||
<div class="button" @click="toHomeView">
|
||
主视角
|
||
</div>
|
||
</div>
|
||
|
||
</template>
|
||
<script setup>
|
||
import * as THREE from "three";
|
||
import { ref, onMounted } from "vue";
|
||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
||
// 引入轨道控制器:支持鼠标左中右键操作和键盘方向键操作
|
||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
||
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
|
||
import {
|
||
CSS2DObject,
|
||
CSS2DRenderer,
|
||
} from "three/examples/jsm/renderers/CSS2DRenderer";
|
||
// import renderModel from "../renderModel";
|
||
import homeIcon from '@/assets/image/bg7.jpg'
|
||
import TWEEN from "@tweenjs/tween.js";
|
||
import bus from '@/utils/bus.js'
|
||
|
||
let scene = ref(null);
|
||
let renderer = ref(null);
|
||
let camera = ref(null);
|
||
// 控制器
|
||
let orbit = ref(null);
|
||
let group = ref(null);
|
||
let mouse = new THREE.Vector2();
|
||
let raycaster = new THREE.Raycaster();
|
||
let labelRenderer = new CSS2DRenderer(); //新建CSS2DRenderer
|
||
let positionObj = ref(null);
|
||
// 选中的模型
|
||
let selectedObjects = ref([]);
|
||
|
||
const props = defineProps({
|
||
background: { // 背景颜色
|
||
default: '#fff',
|
||
type: String
|
||
},
|
||
sceneUrl: { // 模型路径
|
||
default: '/jz/glb/scene.gltf',
|
||
type: String
|
||
},
|
||
light: {
|
||
default: '0xffffff',
|
||
type: String
|
||
}
|
||
})
|
||
const data = reactive({
|
||
reductionMaterial: null
|
||
});
|
||
const { reductionMaterial } = toRefs(data);
|
||
|
||
watch(() => props.sceneUrl, val => {
|
||
init();
|
||
loadSence();
|
||
});
|
||
onMounted(() => {
|
||
bus.on('foo', e => {
|
||
|
||
})
|
||
init();
|
||
loadSence();
|
||
// 启动动画
|
||
renderScene();
|
||
document.addEventListener("click", onMouseDown);
|
||
});
|
||
const init = () => {
|
||
scene = new THREE.Scene("#00ffff");
|
||
const texture = new THREE.TextureLoader().load(homeIcon);
|
||
texture.mapping = THREE.EquirectangularReflectionMapping;
|
||
scene.background = texture
|
||
scene.environment = texture
|
||
|
||
const canvas = document.querySelector("#three");
|
||
var cubeLoader = new THREE.CubeTextureLoader();
|
||
// 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景
|
||
renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true, preserveDrawingBuffer: true });
|
||
//曝光
|
||
renderer.toneMappingExposure = 2;
|
||
renderer.shadowMap.enabled = true;
|
||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||
|
||
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 2000)
|
||
// 将摄像机对准场景的中心
|
||
camera.position.set(0, 104.545, -21.637);
|
||
// camera.lookAt(scene.position);
|
||
|
||
// 创建控件对象
|
||
orbit = new OrbitControls(camera, renderer.domElement);
|
||
orbit.autoRotate = true;
|
||
setTimeout(function () {
|
||
orbit.autoRotate = false;
|
||
}, 7000);
|
||
renderer.setClearColor(new THREE.Color("#0e0934"));
|
||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||
// 环境光 光源颜色从0x444444更改为0x888888,可以看到threejs场景中的网格模型表面变的更亮。
|
||
// intensity 强度属性0.5
|
||
var alight = new THREE.AmbientLight(props.light, 0.5);
|
||
alight.name = "aLight";
|
||
scene.add(alight);
|
||
};
|
||
|
||
const loadSence = () => {
|
||
const gltfLoader = new GLTFLoader();
|
||
gltfLoader.load(props.sceneUrl, (gltf) => {
|
||
var model = gltf.scene;
|
||
scene.add(model);
|
||
});
|
||
};
|
||
|
||
const renderScene = () => {
|
||
orbit.update(); // 拖动
|
||
// 使用requestAnimationFrame函数进行渲染
|
||
requestAnimationFrame(renderScene);
|
||
renderer.render(scene, camera);
|
||
// 写在requestAnimationFrame之后,否则会报错
|
||
labelRenderer.render(scene, camera);
|
||
};
|
||
// 相机移动动画
|
||
const initTween = (targetX, targetY, targetZ) => {
|
||
let initPosition = {
|
||
x: camera.position.x,
|
||
y: camera.position.y,
|
||
z: camera.position.z,
|
||
};
|
||
let tween = new TWEEN.Tween(initPosition)
|
||
.to({ x: targetX, y: targetY, z: targetZ }, 2000)
|
||
.easing(TWEEN.Easing.Sinusoidal.InOut);
|
||
let onUpdate = (pos) => {
|
||
let x = pos.x;
|
||
let y = pos.y;
|
||
let z = pos.z;
|
||
if (pos.z < 0) {
|
||
camera.position.set(x, y, z - 12);
|
||
} else {
|
||
camera.position.set(x, y, z + 12);
|
||
}
|
||
};
|
||
tween.onUpdate(onUpdate);
|
||
tween.start();
|
||
if (positionObj.z < 0) {
|
||
orbit.target.set(
|
||
positionObj.x,
|
||
positionObj.y - 0.4,
|
||
-12
|
||
);
|
||
} else {
|
||
orbit.target.set(
|
||
positionObj.x,
|
||
positionObj.y - 0.4,
|
||
12
|
||
);
|
||
}
|
||
}
|
||
|
||
// 储存被选中的模型和材质
|
||
let selectedObject = null;
|
||
let selectedMaterial = null;
|
||
|
||
const onMouseDown = (event) => {
|
||
var raycaster = new THREE.Raycaster()
|
||
// 恢复之前被选中模型的材质
|
||
if (selectedObject && selectedMaterial) {
|
||
selectedObject.material = selectedMaterial;
|
||
selectedObject = null;
|
||
selectedMaterial = null;
|
||
}
|
||
// 计算鼠标点击位置的归一化设备坐标
|
||
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||
|
||
// 更新射线的起点和方向
|
||
raycaster.setFromCamera(mouse, camera);
|
||
// 计算射线和场景中所有可点击物体的相交情况
|
||
const intersects = raycaster.intersectObjects(scene.children, true);
|
||
// 如果找到了模型,将其材质修改为绿色
|
||
if (intersects.length > 0) {
|
||
const clickedObject = intersects[0].object;
|
||
// 储存被选中的模型和材质
|
||
selectedObject = clickedObject;
|
||
selectedMaterial = clickedObject.material;
|
||
// 修改材质为绿色
|
||
selectedObject.material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
|
||
}
|
||
// 拉近场景
|
||
if (!intersects[0]) {
|
||
return;
|
||
} else {
|
||
|
||
if (intersects[0].object.name.indexOf('box') > -1) {
|
||
selectedObjects = [];
|
||
selectedObjects.push(intersects[0].object);
|
||
positionObj = {
|
||
x: intersects[0].object.position.x,
|
||
y: intersects[0].object.position.y,
|
||
z: intersects[0].object.position.z,
|
||
};
|
||
|
||
initTween(
|
||
positionObj.x,
|
||
positionObj.y,
|
||
positionObj.z
|
||
);
|
||
}
|
||
}
|
||
}
|
||
// 返回主页
|
||
const toHomeView = () => {
|
||
let initPosition = {
|
||
x: camera.position.x,
|
||
y: camera.position.y,
|
||
z: camera.position.z,
|
||
};
|
||
let tween = new TWEEN.Tween(initPosition)
|
||
.to({ x: 0, y: -(5 * 24) / 12, z: (6 * 100) / 5 }, 2000)
|
||
.easing(TWEEN.Easing.Sinusoidal.InOut);
|
||
let onUpdate = (pos) => {
|
||
let x = pos.x;
|
||
let y = pos.y;
|
||
let z = pos.z;
|
||
camera.position.set(x, y, z);
|
||
};
|
||
tween.onUpdate(onUpdate);
|
||
tween.start();
|
||
orbit.target.set(0, 0, 0);
|
||
}
|
||
</script>
|
||
<style lang='scss' scoped>
|
||
#three {
|
||
height: 100%;
|
||
width: 100%;
|
||
}
|
||
.button {
|
||
width: 80px;
|
||
height: 80px;
|
||
line-height: 80px;
|
||
border-radius: 50%;
|
||
border: 1px solid rgb(7, 207, 221);
|
||
position: absolute;
|
||
z-index: 1000000;
|
||
bottom: 15px;
|
||
left: 10px;
|
||
color: #fff;
|
||
text-align: center;
|
||
background: rgb(7, 207, 221);
|
||
cursor: pointer;
|
||
}
|
||
</style> |