当前位置: 首页 > news >正文

vue2 创建threejs场景

vue2 创建threejs场景

<template><div class="bgbox"><div ref="canvasContainer" class="threebox"></div></div>
</template><script>
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'
export default {data() {return {scene: null,camera: null,renderer: null,controls: null,raycaster: null,objects: [],selectedObject: null,}},mounted() {if (this.$refs.canvasContainer) {this.initThreeJS()this.loadModel()this.$refs.canvasContainer.addEventListener('click', this.handleClick)}},beforeDestroy() {window.removeEventListener('resize', this.handleResize)if (this.$refs.canvasContainer) {this.$refs.canvasContainer.removeEventListener('click', this.handleClick)}// 清理Three.js资源if (this.renderer) {this.renderer.dispose()}if (this.scene) {this.scene.clear()}this.objects = []},methods: {// 窗口大小变化处理handleResize() {if (!this.renderer || !this.camera) returnconst { clientWidth, clientHeight } = this.$refs.canvasContainerthis.camera.aspect = clientWidth / clientHeightthis.camera.updateProjectionMatrix()this.renderer.setSize(clientWidth, clientHeight)},// 初始化Three.js场景initThreeJS() {// 创建场景this.scene = new THREE.Scene()//    this.scene.background = new THREE.Color(0x1a1a1a);this.scene.background = new THREE.Color(0xffffff)// 创建相机this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)this.camera.position.set(-0.15310678117827767, 2.5787246941667266, -1.218985004357268)// 创建渲染器this.renderer = new THREE.WebGLRenderer({ antialias: true })this.renderer.setSize(this.$refs.canvasContainer.clientWidth, this.$refs.canvasContainer.clientHeight)this.$refs.canvasContainer.appendChild(renderer.domElement)// 添加轨道控制器this.controls = new OrbitControls(this.camera, this.renderer.domElement)this.controls.enableDamping = truethis.controls.dampingFactor = 0.1this.controls.target.set(0.24809240311931033, 0.07199656930229599, 0.06308049587088951)//   this.controls.addEventListener('change', () => {//     console.log('相机位置更新:', this.camera.position); // 打印当前相机位置//     console.log('控制器目标:', this.controls.target); // 打印相机环绕的目标点//   });// 添加光源const ambientLight = new THREE.AmbientLight(0xffffff, 0.9)this.scene.add(ambientLight)const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)directionalLight.position.set(5, 5, 5)this.scene.add(directionalLight)// 添加辅助网格const gridHelper = new THREE.GridHelper(10, 10, 0x333333, 0x222222)this.scene.add(gridHelper)// 初始化射线投射器(用于点击检测)this.raycaster = new THREE.Raycaster()// 监听窗口大小变化window.addEventListener('resize', this.handleResize)// 开始动画循环this.animate()},// 加载3D模型loadModel() {const dracoLoader = new DRACOLoader()dracoLoader.setDecoderPath('/draco/')// 创建GLTF加载器const loader = new GLTFLoader()loader.setDRACOLoader(dracoLoader)loader.load('/models/tree.glb',(gltf) => {// 处理加载完成的模型const model = gltf.scene// 调整模型大小和位置model.scale.set(0.01, 0.01, 0.01)model.position.set(0, 0, 0)// 将模型添加到场景this.scene.add(model)// 收集所有可点击的对象model.traverse((child) => {if (child.isMesh) {objects.push(child)}})},(xhr) => {},(error) => {// 加载错误console.error('模型加载错误:', error)})},// 处理点击事件handleClick(event) {if (!this.$refs.canvasContainer) return// 计算鼠标在标准化设备坐标中的位置 (-1 到 1)const rect = this.$refs.canvasContainer.getBoundingClientRect()const mouse = new THREE.Vector2(((event.clientX - rect.left) / rect.width) * 2 - 1,-((event.clientY - rect.top) / rect.height) * 2 + 1)// 更新射线投射器this.raycaster.setFromCamera(mouse, this.camera)// 检查射线与哪些对象相交const intersects = this.raycaster.intersectObjects(this.objects)// 处理相交结果if (intersects.length > 0) {const selected = intersects[0].object// 获取世界坐标const worldCoords = intersects[0].point// 将世界坐标转换为屏幕坐标const screenCoords = this.worldToScreen(worldCoords)// console.log('选中对象:', selected, screenCoords);const noNeedClickList = ['Plane033_1', 'Plane001_2', 'Plane033', 'Plane001_1', 'Plane022_1', 'Plane_1', 'Plane001_3']if (noNeedClickList.includes(selected.name)) {this.$emit('modelClick', null, { x: 0, y: 0 })} else {this.$emit('modelClick', selected, screenCoords)}} else {this.selectedObject = nullthis.$emit('modelClick', null, { x: 0, y: 0 })}},// 动画循环animate() {requestAnimationFrame(this.animate)if (this.controls) {this.controls.update()}if (this.renderer && this.scene && this.camera) {this.renderer.render(this.scene, this.camera)}},// 将世界坐标转换为屏幕坐标worldToScreen(position) {const vector = position.clone()// 将三维场景中的坐标转换为屏幕坐标vector.project(camera)return {x: Math.round(((vector.x + 1) * window.innerWidth) / 2),y: Math.round(((1 - vector.y) * window.innerHeight) / 2),}},},
}
</script><style lang="scss" scoped>
.bgbox {width: 100%;height: 100%;
}
.threebox {width: 100%;height: 100%;
}
</style>
http://www.xdnf.cn/news/1368865.html

相关文章:

  • ubuntu20.04 终端安装claude
  • 事件驱动架构详解
  • .gitignore 文件相关使用配置
  • 服务器数据恢复—热备盘上线失败如何恢复数据?
  • Ansible 自动化运维工具:介绍与完整部署(RHEL 9)
  • 如何基于阿里云OpenSearch LLM搭建智能客服平台
  • 亚马逊类目合规风暴:高压清洗机品类整顿背后的运营重构与风险防御
  • 零基础构建MCP服务器TypeScriptPython双语言实战指南
  • 零基础也能照做的WordPress网站安全漏洞修复 + 高级优化保姆级教程。
  • 【JavaEE】了解volatile和wait、notify(三)
  • 算法题打卡力扣第209题:长度最小的子数组(mid)
  • 【强化学习】区分理解: 时序差分(TD)、蒙特卡洛(MC)、动态规划(DP)
  • THM El Bandito
  • 使用C++与Qt6,在windows上打造MacOS风格桌面应用窗口
  • SELinux
  • Mac测试端口连接的几种方式
  • 【制作100个Unity游戏】从零开始构建类《月圆之夜》《杀戮尖塔》的卡牌游戏(附带项目源码)
  • CSS 结构伪类选择器
  • C语言开发入门教程:从环境搭建到第一个程序
  • 【lucene】SpanNotQuery 存在的意义
  • 国产化Excel开发组件Spire.XLS教程:Python 读取 CSV 文件,从基础到进阶指南
  • 一文看懂@Bean注解的原理
  • 【C++】用哈希表封装实现unordered_set和unordered_map
  • Ubuntu 操作系统
  • 自动化测试概念与 Web 自动化实战(基于 Selenium)
  • Tensor常见操作
  • pycharm 远程连接服务器报错
  • Java基础第二课:hello word
  • 160.在 Vue3 中用 OpenLayers 解决国内 OpenStreetMap 地图加载不出来的问题
  • 从行业智能体到一站式开发平台,移动云推动AI智能体规模化落地