记录Three.js代码组件封装片段 , 支持定制旋转位置大小配置
three.js官方连接:Three.js中文网
3D模型文件下载地址:3D模型可视化编辑器
完整效果图片
封装文件位置 : utils文件夹下
依赖安装:
"dependencies": {
"three": "^0.165.0",
"three-obj-mtl-loader": "^1.0.3",
"three-orbit-controls": "^82.1.0",
}
封装文件:
<!--THREEJS组件-->
<template>
<div class="d3-container">
<div
class="camera-icon"
v-show="
this.camera &&
(Math.round(this.camera.position.x) !== 1 ||
Math.round(this.camera.position.z) !== 20) &&
ishome
"
>
<el-tooltip effect="light" content="复位" placement="top">
<i class="el-icon-refresh" @click="onResetModelCamera"></i>
</el-tooltip>
</div>
<div
element-loading-text="模型加载中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0)"
id="d3Container"
v-loading="loading"
ref="mainContent"
></div>
</div>
</template>
<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
export default {
name: "ThreePage",
props: {
/*模型资源地址*/
ossPath: {
type: String,
default() {
return "/scene.gltf";
},
},
/*是否主页*/
ishome: {
type: Boolean,
default() {
return true;
},
},
// 是否缩放
isEnableZoom: {
type: Boolean,
default() {
return true;
},
},
// 是否拖拽
isEnablePan: {
type: Boolean,
default() {
return true;
},
},
/*d3物件的初始位置*/
d3Position: {
type: Array,
default() {
return [0, -6, 0];
},
},
/*d3物件的初始缩放*/
d3Scale: {
type: Array,
default() {
return [2, 2, 2];
},
},
/*d3相机的位置*/
cameraPosition: {
type: Array,
default() {
return [1, 1, 20];
},
},
/*d3相机的观测点*/
cameraLookAt: {
type: Array,
default() {
return [1, 10, 0];
},
},
/*是否开启模型动画*/
startModelAnimaion: {
type: Boolean,
default() {
return true;
},
},
/*模型动画运转速度*/
mixerTimeScale: {
type: Number,
default() {
return 1;
},
},
/*文件类型*/
suffix: {
type: String,
default() {
return "gltf";
},
},
AutoFresh: {
type: Boolean,
default() {
return true;
},
},
/*是否开启自动旋转*/
autoAnimate: {
type: Boolean,
default() {
return true;
},
},
/*当前模型的颜色*/
currentColor: {
type: String,
default() {
return "";
},
},
/*配准后的颜色*/
matchedColor: {
type: String,
default() {
return "";
},
},
/*配准后的地址*/
matchedOssPatch: {
type: String,
default() {
return "";
},
},
showMatchWatch: {
type: Boolean,
default() {
return false;
},
},
},
data() {
return {
OricameraUp: null,
OricamerPosition: null,
OricameraQuaternion: null,
OricontrolTarget: null,
composer: null,
renderPass: null,
outlinePass: null,
loading: false,
publicPath: process.env.BASE_URL,
mesh: null,
camera: null,
scene: null,
originX: 10,
originY: 20,
originZ: 10,
renderer: null,
controls: null,
fileLoaderMap: {
glb: new GLTFLoader(),
fbx: new FBXLoader(),
gltf: new GLTFLoader(),
obj: new OBJLoader(),
},
clock: new THREE.Clock(),
mixer: null,
};
},
mounted() {
this.init();
},
watch: {
//监听地址变化时需要更新地址,防止多次点击同一个渲染多次;
ossPath(val, oldVal) {
if (val != oldVal) {
this.init();
}
},
//监测是否更新整个场景
AutoFresh(val, oldVal) {
if (val) {
this.init();
} else {
//自我清理
this.destroyed();
}
},
//监测是否展示配准,更新场景,该属性的变化只负责更新场景,具体业务交给按钮的最终展现结果,按钮勾中就展示配准,没有勾中就不展示配准,属性没变就是原来的状态。
showMatchWatch(val, oldVal) {
this.init();
},
//由于上传标签时,CAD会绕过。
currentColor(val, oldVal) {
if (val != oldVal) {
this.init();
}
},
},
//组件被销毁时,干掉所有3D资源;
methods: {
// 重置相机位置
onResetModelCamera() {
// // 设置相机位置
this.camera.position.set(1, 1, 20);
// 初始化位置
this.scene.up.copy(this.OricameraUp);
this.scene.position.copy(this.OricamerPosition);
this.scene.quaternion.copy(this.OricameraQuaternion);
this.controls.target.copy(this.OricontrolTarget);
},
destroyed() {
this.clear();
},
// 初始化
init() {
/*利用vue单项数据流的特性做最后的守卫,在最底层监听是否需要展示配准图,只影响该组件的内部数据而不影响外部的matchedOssPatch*/
if (!this.showMatchWatch) {
this.matchedOssPatch = "";
}
this.createScene(); // 创建场景
this.loadLoader(); // 加载P模型
this.createLight(); // 创建光源
this.createCamera(); // 创建相机
this.createRender(); // 创建渲染器
this.createControls(); // 创建控件对象
this.render(); // 渲染
// 获取初始模型位置
this.OricameraUp = this.scene.up.clone();
this.OricamerPosition = this.scene.position.clone();
this.OricameraQuaternion = this.scene.quaternion.clone();
this.OricontrolTarget = this.controls.target.clone();
// console.log( this.OricontrolTarget);
// console.log(
// this.OricameraUp,
// this.OricamerPosition,
// this.OricameraQuaternion,
// this.OricontrolTarget
// );
},
//清除当前所有场景
clear() {
this.mesh = null;
this.camera = null;
this.scene = null;
this.renderer = null;
this.controls = null;
this.clock = null;
this.mixer = null;
cancelAnimationFrame(this.animationId);
console.log("我要清除啦");
},
// 创建场景
createScene() {
this.loading = true;
this.scene = new THREE.Scene();
//开启 底部网格
var grid = new THREE.GridHelper(24, 24, 0xff0000, 0x444444);
grid.material.opacity = 0.4;
grid.material.transparent = true;
grid.rotation.x = Math.PI / 2.0;
grid.rotation.z = Math.PI / 4;
// makeRotationAxis(axis, angle)
// this.scene.add()
this.scene = new THREE.Scene();
const texture = new THREE.TextureLoader().load(
require("@/assets/images/view-4.png")
);
texture.mapping = THREE.EquirectangularReflectionMapping;
texture.colorSpace = THREE.SRGBColorSpace;
this.scene.background = texture;
this.scene.environment = texture;
},
// 加载PLY模型
loadLoader() {
const THIS = this;
let fileType = THIS.ossPath.substring(THIS.ossPath.lastIndexOf(".") + 1);
const loader = this.fileLoaderMap[fileType];
return new Promise((resolve, reject) => {
loader.load(
THIS.ossPath,
(result) => {
//加载不同类型的文件
switch (fileType) {
case "glb":
this.mesh = result.scene;
break;
case "fbx":
this.mesh = result;
break;
case "gltf":
this.mesh = result.scene;
break;
case "obj":
this.mesh = result;
break;
default:
break;
}
this.mesh.traverse(() => {
});
// 新建一个AnimationMixer
this.mixer = new THREE.AnimationMixer(this.mesh);
this.mixer.clipAction(result.animations[0]).play();
this.mixer.timeScale = this.mixerTimeScale; //默认1,可以调节播放速度
this.loading = false; //关闭载入中效果
// 设置模型大小
this.mesh.scale.set(
this.d3Scale[0],
this.d3Scale[1],
this.d3Scale[2]
);
// 设置模型位置
this.mesh.position.set(
this.d3Position[0],
this.d3Position[1],
this.d3Position[2]
); //设置3d物品的位置
// 将模型添加到场景中去
this.scene.add(this.mesh);
resolve(true);
},
(xhr) => {
const percent = Math.floor((xhr.loaded / xhr.total) * 100);
},
(err) => {
console.log(err);
reject(new Error());
}
);
});
//如果有配准结果,加载配准结果,配准结果未ply格式;
},
// 创建光源
createLight() {
// 环境光
let pointColor = " #000";
const ambientLight = new THREE.AmbientLight(pointColor, 0.35); // 创建环境光
this.scene.add(ambientLight); // 将环境光添加到场景
const spotLight = new THREE.SpotLight(0xffffff); // 创建聚光灯
spotLight.position.set(1, 1, 1);
// spotLight.castShadow = true //平行光开启阴影
// spotLight.receiveShadow = true
this.scene.add(spotLight);
},
// 创建相机
createCamera() {
const element = this.$refs.mainContent;
const width = element.clientWidth; // 窗口宽度
const height = element.clientHeight; //
this.cWidth = width;
this.cHeight = height;
const k = width / height; // 窗口宽高比
this.aspect = k;
// PerspectiveCamera( fov, aspect, near, far )
this.camera = new THREE.PerspectiveCamera(35, k, 1, 20000);
this.camera.position.set(
this.cameraPosition[0],
this.cameraPosition[1],
this.cameraPosition[2]
); // 设置相机位置
this.camera.up.set(0, 1, 0);
this.camera.lookAt(
new THREE.Vector3(
this.cameraLookAt[0],
this.cameraLookAt[1],
this.cameraLookAt[2]
)
); // 设置相机方向
this.scene.add(this.camera);
},
// 创建渲染器
createRender() {
const element = this.$refs.mainContent;
this.renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
preserveDrawingBuffer: true,
});
this.renderer.setSize(element.clientWidth, element.clientHeight); // 设置渲染区域尺寸
this.renderer.shadowMap.enabled = true; // 显示阴影
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.scene.background = null; //设置背景颜色,null为透明
// this.renderer.setClearColor(new THREE.Color(0xeeeeee)) // 设置背景颜色
// this.renderer.setClearColor(new THREE.Color(0x111111)) // 设置背景颜色
this.renderer.toneMappingExposure = 1.5; //曝光
this.renderer.toneMapping = THREE.ACESFilmicToneMapping; //色调映射
element.innerHTML = "";
element.appendChild(this.renderer.domElement);
},
render() {
//TODO:更新模型动画
if (this.mixer && this.startModelAnimaion) {
const delta = this.clock.getDelta();
this.mixer.update(delta);
}
this.animationId = requestAnimationFrame(this.render); //旋转动画;
this.renderer.render(this.scene, this.camera);
this.controls.update();
// 点击事件影像处理 有问题
// if (this.composer) {
// this.composer.render()
// }
},
// 创建控件对象
createControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
// const newMesh = this.mesh.clone();
// console.log(this.mesh.position);
// console.log(this.mesh);
// this.controls.minDistance = 1 //设置相机距离原点的最远距离
// this.controls.maxDistance = 1000 //设置相机距离原点的最远距离
// this.controls.maxPolarAngle = Math.PI / 1 //设置可旋转的范围
this.controls.enableZoom = true; //是否可以缩放
this.controls.enablePan = true; //是否可拖拽
this.controls.enableDamping = true; // 开启阻尼效果
this.controls.dampingFactor = 1; // 阻尼系数
this.controls.autoRotate = this.autoAnimate; // 自动旋转
this.controls.autoRotateSpeed = 5; // 自动旋转速度
this.controls.minPolarAngle = 0; // 设置最小旋转角度
this.controls.maxPolarAngle = Math.PI; // 设置最大旋转角度
},
onWindowResize() {
this.camera.aspect = this.aspect;
this.camera.updateProjectionMatrix();
},
},
beforeDestroy() {
this.destroyed();
},
};
</script>
<style scoped lang="less">
.d3-container {
width: 100%;
height: 100%;
.camera-icon {
position: absolute;
top: 20px;
left: 50%;
cursor: pointer;
}
}
#d3Container {
width: 100%;
height: 100%;
}
.el-icon-refresh {
font-size: 28px;
color: #ccc;
z-index: 99;
}
</style>
使用方法
<template>
<d3
:cameraPosition="[1, 3, 20]"
:d3Position="[0, 0, 0]"
:d3Scale="[.85, .85, .85]"
:isEnableZoom="true"
:isEnableRotate="true"
:autoAnimate="true"
ossPath="HN.gltf"
class="three-canvas"
></d3>
</template>
<script>
import d3 from "../../utils/index.vue";
</script>
标签:return,default,scene,js,Three,controls,new,null,gltf
From: https://blog.csdn.net/m0_72840382/article/details/140640141