H5使用ThreeJs展示3D模型(gltf格式)案例

news/2024/7/9 9:14:57 标签: 3d, threejs, H5

一、安装所需js

npm install three
npm i @tweenjs/tween.js@^18

二、引入和定义全局变量

// 引入three.js
import * as THREE from 'three';
// 引入扩展库GLTFLoader.js
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import * as TWEEN from 'tween.js';
var model, camera, scene, renderer, controls

三、主要内容

页面元素

<template>
	<div class="index-body">
		<div ref="container" style="width: 100%;height: 100%;overflow: hidden;"></div>
	</div>
</template>

js内容

export default {
	data() {
		return {
			spriteScaleUp: true,//精灵图动画执行放大还是缩小
		}
	},
	mounted() {
		this.$nextTick(() => {
			this.renderInitializeModelFn()
		});
	},
	methods: {
		//渲染模型
		renderInitializeModelFn(){
			let that = this
			// 创建一个场景
			scene = new THREE.Scene();
			// 创建一个相机
			camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
			// 设置相机的位置
			camera.position.set(200, 80, 0);
			// 设置相机的观察点
			camera.lookAt(new THREE.Vector3(0, 0, 0));
			// 创建渲染器
			renderer = new THREE.WebGLRenderer({
				antialias: true,     //抗锯齿
			});
			renderer.setPixelRatio(window.devicePixelRatio);
			renderer.setSize(window.innerWidth, window.innerHeight);
			this.$refs.container.appendChild(renderer.domElement);
			// 添加光源
			const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
			scene.add(ambientLight);
		
			const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
			directionalLight.position.set(0, 1, 1);
			scene.add(directionalLight);
		
			// 创建GLTF加载器对象
			const loader = new GLTFLoader();
			loader.load( '3D模型路径(尽量为网络路径)', function ( gltf ) {
				console.log('控制台查看加载gltf文件返回的对象结构',gltf);
				// console.log('gltf对象场景属性',gltf.scene);
				// 返回的场景对象gltf.scene插入到threejs场景中
				// scene.add( gltf.scene );
				model = gltf.scene;
				console.log("加载完成!")
				console.log(model, "模型内容")
				// 创建一个空的Object3D对象作为中心点
				const center = new THREE.Object3D();
				// 将模型添加到中心点对象上
				center.add(model);
				// 在中心点对象上调整模型的位置
				model.position.set(35, 0, -25); // 设置模型的相对位置
				// 将中心点对象添加到场景中
				scene.add(center);
			}, (e) => {
				console.log(e, "进度");
			}, function (e) {
				console.log(e);
			})
			
			// 创建纹理加载器添加精灵图
			let sprite = new THREE.Sprite(new THREE.SpriteMaterial({
				map: new THREE.TextureLoader().load("精灵图路径"),
				sizeAttenuation:false // false的话 不随着相机拉远而变小
			}))
			sprite.position.set(0,0,0)//精灵图在模型中的位置
			sprite.scale.set(0.12, 0.12, 1)//精灵图大小
			sprite.name = "jinglingtu"
			// 将精灵添加到场景中
			scene.add(sprite)
			//给精灵图添加脉冲效果
			this.spriteAnimateFn(sprite);
		
			// 点击事件处理函数
			const onClick = (event) => {
				console.log(event)
				// 获取点击位置的屏幕坐标
				const mouse = new THREE.Vector2();
				mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
				mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
		
				// 通过射线检测点击位置是否与精灵图相交
				const raycaster = new THREE.Raycaster();
				raycaster.setFromCamera(mouse, camera);
				let intersects = raycaster.intersectObject(item);
				if (intersects.length > 0) {
					console.log("点击精灵")
					this.changeCameraPosition()
				}
			};
		
			// 监听点击事件
			this.$refs.container.addEventListener('click', onClick, false);
		
			// 设置光源投射阴影
			directionalLight.castShadow = true;
			directionalLight.shadow.mapSize.width = 1024;
			directionalLight.shadow.mapSize.height = 1024;
			directionalLight.shadow.camera.near = 0.1;
			directionalLight.shadow.camera.far = 100;
			renderer.shadowMap.enabled = true;
		
			// 添加交互控制器
			controls = new OrbitControls(camera, renderer.domElement);
			//开启阻尼效果
			controls.enableDamping = true
			controls.dampingFactor = 0.2
			
			//启动相机旋转
			controls.enableRotate = true
			// 位移的速度,其默认值为1。
			controls.panSpeed = 3
			// 旋转的速度,其默认值为1。
			controls.rotateSpeed = 3
			//启用或禁用摄像机平移,默认为true。
			controls.enablePan = false
			//启动相机缩放
			controls.enableZoom = true
			// 设置相机距离目标点的最小和最大距离
			controls.minDistance = 65;
			controls.maxDistance = 200;
			// 设置相机在 y 轴上的最低和最高可视角度
			// controls.minPolarAngle = Math.PI / 4;
			controls.maxPolarAngle = Math.PI / 2.2;
			// 设置相机在 x 轴上的最低和最高可视角度
			// controls.minAzimuthAngle = -Math.PI / 4;
			// controls.maxAzimuthAngle = Math.PI / 4;
			controls.update();
		
			// 渲染场景
			function animate() {
				requestAnimationFrame(animate);
				TWEEN.update();
				renderer.render(scene, camera);
			}
			animate();
		},
		//精灵图动画
		spriteAnimateFn (sprite) {
			// 更新精灵图的缩放
			if (this.spriteScaleUp) {
				sprite.scale.x += 0.003;
				sprite.scale.y += 0.003;
			} else {
				sprite.scale.x -= 0.003;
				sprite.scale.y -= 0.003;
			}
			// 调整缩放方向
			if (sprite.scale.x >= 0.14) {
				this.spriteScaleUp = false
			} else if (sprite.scale.x <= 0.12) {
				this.spriteScaleUp = true
			}
			// 更新精灵图
			sprite.updateMatrix()
			// 循环调用 animate 函数
			setTimeout(() => {
				this.spriteAnimateFn(sprite);
			}, 100);
		},
		//更新相机位置
		changeCameraPosition() {
			//解除滑动限制
			controls.minDistance = 0;
			controls.maxPolarAngle = Math.PI / 1;
			controls.enableRotate = false
			controls.enableZoom = false
			controls.update();
			// 相机从当前位置camera.position飞行三维场景中某个世界坐标附近
			new TWEEN.Tween({
				// 相机开始坐标
				x: camera.position.x,
				y: camera.position.y,
				z: camera.position.z,
				// 相机开始指向的目标观察点
				tx: 0,
				ty: 0,
				tz: 0,
			})
			.to({
				// 相机结束坐标
				x: 0,
				y: 0,
				z: 0,
				// 相机结束指向的目标观察点
				tx: 0,
				ty: 0,
				tz: 0,
			}, 1000)
			.onUpdate(function (e) {
				// 动态改变相机位置
				camera.position.set(this.x, this.y, this.z);
				// 模型中心点
				controls.target.set(this.tx, this.ty, this.tz);
				controls.update();//内部会执行.lookAt()
			})
			.start();
		},
	}
}



http://www.niftyadmin.cn/n/1756035.html

相关文章

Java实现 LeetCode 34 在排序数组中查找元素的第一个和最后一个位置 (解法详解)

34. 在排序数组中查找元素的第一个和最后一个位置 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计并实现时间复杂度为…

Mysql:创建和管理表(全面详解)

创建和管理表 前言一、基础知识1、一条数据存储的过程2、标识符命名规则3、MySQL中的数据类型 二、创建和管理数据库1、创建数据库2、使用数据库3、修改数据库4、删除数据库 三、创建表1、创建方式12、创建方式23、查看数据表结构 四、修改表1、追加一个列2、修改一个列3、重命…

php导出pdf

插件官网&#xff1a;TCPDF 博主用的是tp6框架 、tcpdf插件 composer require tecnickcom/tcpdf --ignore-platform-reqs 后面是忽略平台要求的参数 ---------------中文乱码start------------------ 关于中文乱码问题&#xff1a; 网上说的下载字体放入fonts 利用tools…

【C#】并行编程实战:使用 PLINQ(1)

PLINQ 是语言集成查询&#xff08;Language Integrate Query , LINQ&#xff09;的并行实现&#xff08;P 表示并行&#xff09;。本章将介绍其编程的各个方面以及与之相关的一些优缺点。 PLINQ 介绍 | Microsoft Learn了解如何使用 .NET 中的 PLINQ 并行执行查询。 PLINQ 代表…

1.2.1 Qt中事件是如何进行传递——实例篇(下)

1.2.1 Qt中事件是如何进行传递 1.2.2 Qt中的事件过滤器(eventFilter) 1.2.3 如何自己模拟发送事件消息 一、Qt中事件是如何进行传递-代码篇 上一篇中我们讲解了Qt中的事件,通过流程图给大家展示了事件的传递过程,今天就通过代码来给大家实操一下,验证流程图的走向。 …

简单回顾一下kafka的学习

简单回顾一下kafka的学习 WhatBrokerControllerPartitionReplicationTopicProducerConsumer Why为什么有多个分区为什么有副本 How搭建集群Java简单使用ProducerConsumeroffset提交方式自动提交 - 默认手动提交 消费者poll消息的过程指定分区消费消息回溯消费指定offset消费新消…

【C++】函数绑定器技术

1. 函数绑定器技术&#xff0c;什么情况可以用到这种技术实现? 占位符和函数绑定器技术&#xff08;如std::bind&#xff09;可以在以下情况下使用&#xff1a; 参数绑定&#xff1a;当你想要绑定函数的某些参数&#xff0c;但又不想立即提供这些参数的值时&#xff0c;可以…

记录--组件库的 Table 组件表头表体是如何实现同步滚动?

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前言 在使用 Vue 3 组件库 Naive UI 的数据表格组件 DataTable 时碰到的问题&#xff0c;NaiveUI 的数据表格组件 DataTable 在固定头部和列的示例中&#xff0c;在键盘操作下表格横向滚动会有问题&am…