three.js 点按钮,相机飞行靠近观察设备
效果: 点击按钮或直接点击模型都可以实现运动效果
代码:
<template><div><el-container><el-main><div class="box-card-left"><div id="threejs" style="border: 1px solid red"></div><div class="box-right"><el-button type="primary" @click="lookFor('设备A')">设备A</el-button><el-button type="primary" @click="lookFor('设备B')">设备B</el-button><el-button type="primary" @click="lookAll">整体</el-button><el-button type="primary" @click="saveImg">保存图片</el-button></div></div></el-main></el-container></div>
</template>
<script>
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import TWEEN from "@tweenjs/tween.js";export default {data() {return {scene: null,camera: null,renderer: null,mesh: null,geometry: null,group: null,material: null,clock: null,mixer: null,};},created() {},mounted() {this.name = this.$route.query.name;this.init();// 监听点击事件this.addClickEventListener();},methods: {goBack() {this.$router.go(-1);},init() {// 创建场景对象this.scene = new this.$three.Scene();this.group = new this.$three.Group();this.createMesh({x: 50, y: 50, z: 50, name: '设备A'})this.createMesh({x: -50, y: 50, z: 50, name: '设备B'})this.scene.add(this.group);const axesHelper = new this.$three.AxesHelper(150);this.scene.add(axesHelper);// 创建环境光对象const ambientLight = new this.$three.AmbientLight(0xffffff);this.scene.add(ambientLight);// 创建相机对象this.camera = new this.$three.PerspectiveCamera();this.camera.position.set(300,300,300);this.camera.lookAt(0,0,0);// 创建渲染器对象this.renderer = new this.$three.WebGLRenderer({preserveDrawingBuffer: true // 把画布内容保存为图片时,需要设置为true});this.renderer.setSize(1000,800);this.renderer.render(this.scene, this.camera);window.document.getElementById("threejs").append(this.renderer.domElement);// 创建相机空间轨道控制器对象this.controls = new OrbitControls(this.camera, this.renderer.domElement);this.controls.addEventListener("change", () => {this.renderer.render(this.scene, this.camera);console.log(' this.camera.position', this.camera.position.x, this.camera.position.y, this.camera.position.z);})},// 创建网格模型的方法createMesh(obj) {// 创建立方缓冲几何体对象const geometry = new this.$three.BoxGeometry(obj.x, obj.y, obj.z);// 创建材质对象const material = new this.$three.MeshLambertMaterial({color: this.randomColor()});const mesh = new this.$three.Mesh(geometry, material);mesh.position.set(obj.x, obj.y, obj.z);mesh.name = obj.name;if(this.group) {this.group.add(mesh);}},lookFor(name){if(this.scene && this.scene.getObjectByName(name)) {// 通过 getObjectByName() 方法获取name为设备A的模型const equipment_A = this.scene.getObjectByName(name);// 创建Vector3类型的位置对象const position = new this.$three.Vector3();// 获取设置A的世界坐标并赋值到position对象中equipment_A.getWorldPosition(position);// 向量x,y,z坐标值在position的基础上增加50,const position_scalar = position.clone().addScalar(100);// 创建TWEEN对象并调用Tween方法new TWEEN.Tween({x: this.camera.position.x,y: this.camera.position.y,z: this.camera.position.z,px: this.controls.target.x,py: this.controls.target.y,pz: this.controls.target.z,}).to({x: position_scalar.x,y: position_scalar.y,z: position_scalar.z,px: equipment_A.position.x,py: equipment_A.position.y,pz: equipment_A.position.z,}, 1000).onUpdate(obj => {// 设置相机位置this.camera.position.set(obj.x, obj.y, obj.z);// 设置控制器指向this.controls.target.set(obj.px, obj.py, obj.pz);// 更新控制器this.controls.update();}).start();this.loop();}},loop() {this.renderer.render(this.scene, this.camera);TWEEN.update();window.requestAnimationFrame(this.loop);},lookAll() {/*** 查看整体的思路:* 用包围盒 Box3, 将场景中所有的模型包裹起来,计算出 * (box3.min.x + box.max.x) / 2 = centerX* (box.min.y + box.max.y) / 2 = centerY* (box.min.z + box.max.z) / 2 = centerZ* , 计算出 centerX, centerY, centerZ 整体的中心坐标,* 为了显示包围盒的边界,可以使用Box3Helper辅助对象;* 相机的位置position要从当前位置定位到** */// 创建包围盒对象const box3 = new this.$three.Box3();// 设置包围盒中的对象const groupBox = box3.expandByObject(this.group);console.log(groupBox);const box3Helper = new this.$three.Box3Helper(box3, 0xffffff);this.scene.add(box3Helper);let max_x = groupBox.max.x;let max_y = groupBox.max.y;let max_z = groupBox.max.z;let min_x = groupBox.min.x;let min_y = groupBox.min.y;let min_z = groupBox.min.z;let center_x = (max_x + min_x) / 2;let center_y = (max_y + min_y) / 2;let center_z = (max_z + min_z) / 2;// let increment_x = Math.abs(max_x) > Math.abs(min_x) ? Math.abs(max_x) : Math.abs(min_y);let increment_y = Math.abs(max_y) > Math.abs(min_y) ? Math.abs(max_y) : Math.abs(min_y);let increment_z = Math.abs(max_z) > Math.abs(min_z) ? Math.abs(max_z) : Math.abs(min_z);new TWEEN.Tween({x: this.camera.position.x,y: this.camera.position.y,z: this.camera.position.z,px: this.controls.target.x,py: this.controls.target.y,pz: this.controls.target.z,}).to({x: center_x + increment_x * 2,y: center_y + increment_y * 2,z: center_z + increment_z * 2,px: center_x,py: center_y,pz: center_z,},1200).onUpdate(obj => {this.camera.position.set(obj.x, obj.y, obj.z);this.controls.target.set(obj.px, obj.py, obj.pz);this.controls.update();}).start();this.loop();},saveImg() {const link = document.createElement('a');const canvas = this.renderer.domElement;link.href = canvas.toDataURL('image/png');link.download = 'threejs.png';link.click();},randomColor() {const numbers = Array.from({ length: 255 }, (_, i) => i);const color = [...numbers];// 要生成min-max之间的随机数,公式为:Math.random()*(max-min+1)+minlet i = Math.floor(Math.random() * (color.length - 0 + 1) + 0);let j = Math.floor(Math.random() * (color.length - 0 + 1) + 0);let k = Math.floor(Math.random() * (color.length - 0 + 1) + 0);return new this.$three.Color("rgb(" +i +", " +j +", " +k +")");},// 在canvas画布上添加监听点击的事件addClickEventListener() {// 获取id 是 threejs 的元素;const dom = window.document.getElementById("threejs");const canvasWidth = dom.clientWidth; // 获取元素的宽度const canvasHeight = dom.clientHeight; // 获取元素的高度dom.addEventListener("click", e => {const x = e.offsetX; // 获取鼠标当前点击的点距离dom元素左上角原点 在x轴方向上的距离const y = e.offsetY;// 获取鼠标当前点击的点距离dom元素左上角原点 在y轴方向上的距离console.log(x,y);// 由于canvas画布上的坐标值与普通2d页面的坐标值是不一样的;// 在canvas画布上的坐标轴是以画布的中心点为原点,左右x轴,值 -1 ~ 1,,上下y轴,值-1 ~ 1;// 坐标需要进行坐标转换const pos_x = (x / canvasWidth) * 2 - 1; // 转换后的x坐标const pos_y = -(y / canvasHeight) * 2 + 1; // 转换后的y坐标// 创建射线投射器对象(可以在初始化方法中创建,每次点击时创建有些浪费资源)const rayCaster = new this.$three.Raycaster();// 计算射线(在点击位置创建一条射线,用来拾取模型对象)rayCaster.setFromCamera(new this.$three.Vector2(pos_x, pos_y), this.camera);const mesh_list = [];// traverse 是threejs中的递归遍历方法;找出group中的meshthis.group.traverse(obj => {if(obj.isMesh) {mesh_list.push(obj);}});// 射线交叉计算(计算出与自身射线相交的网格模型)const intersects = rayCaster.intersectObjects(mesh_list);if(intersects && intersects.length > 0) {console.log( intersects[0]);this.lookFor(intersects[0].object.name);}})}},
};
</script>
//
<style lang="less" scoped>
.box-card-left {display: flex;align-items: flex-start;flex-direction: row;width: 100%;.box-right {img {width: 500px;user-select: none;}}
}
</style>
相关文章:

three.js 点按钮,相机飞行靠近观察设备
效果: 点击按钮或直接点击模型都可以实现运动效果 代码: <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"><…...

什么情况下物理服务器会运行出错?
物理服务器,也称为裸机服务器,一般可以提供高性能计算水平和巨大的存储容量。然而,它们也难免会遇到一些问题。运行出错时,可能会导致停机和数据丢失。在这篇文章中,介绍了常见的物理服务器在一些情况下显示出错…...
配置免费的SSL
1 引言 本文介绍了如何在 Linux 环境下使用免费的 Let’s Encrypt 为你的网站配置 SSL 证书的方法,以及如何在 Nginx 服务器中启用 SSL。对于需要在自己的网站上启用 HTTPS 的用户来说非常实用。 2 SSL 简介 SSL,全称为 Secure Sockets Layer…...

(2)(2.1) Andruav Android Cellular(一)
文章目录 前言 1 Andruav 是什么? 2 Andruav入门 3 Andruav FPV 4 Andruav GCS App 前言 Andruav 是一个基于安卓的互联系统,它将安卓手机作为公司计算机,为你的无人机和遥控车增添先进功能。 1 Andruav 是什么ÿ…...
[GN] Vue3.2 快速上手 ---- 核心语法(终章)_3
文章目录 路由器工作模式命名路由to的三种写法嵌套路由路由传参query参数params参数 路由的props配置replace 和 push编程式导航重定向 总结 路由器工作模式 history模式 优点:URL更加美观,不带有#,更接近传统的网站URL。 缺点:后…...

在k8s上部署ClickHouse
概述 clickhouse的容器化部署,已经有非常成熟的生态了。在一些互联网大厂也已经得到了大规模的应用。 clickhouse作为一款数据库,其容器化的主要难点在于它是有状态的服务,因此,我们需要配置PVC。 目前业界比较流行的部署方式有…...

快速入门:使用 Gemini Embeddings 和 Elasticsearch 进行向量搜索
Gemini 是 Google DeepMind 开发的多模态大语言模型家族,作为 LaMDA 和 PaLM 2 的后继者。由 Gemini Ultra、Gemini Pro 和 Gemini Nano 组成,于 2023 年 12 月 6 日发布,定位为 OpenAI 的竞争者 GPT-4。 本教程演示如何使用 Gemini API 创建…...

【网络安全】-入门版
secure 一、基本工具1、metasploit framework ps.本着兴趣爱好,加强电脑的安全防护能力,并严格遵守法律和道德规范。一、基本工具 1、metasploit framework msf(metasploit framework)是一个开源的渗透测试框架,用于…...

Elasticsearch各种高级文档操作3
本文来记录几种Elasticsearch的文档操作 文章目录 初始化文档数据聚合查询文档概述对某个字段取最大值 max 示例对某个字段取最小值 min 示例对某个字段求和 sum 示例对某个字段取平均值 avg 示例对某个字段的值进行去重之后再取总数 示例 State 聚合查询文档概述操作实例 桶聚…...
【算法题】66. 加一
题目 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外,这个整数不会以零开头。 示例 1: 输入:…...
查看服务器资源使用情况
查看服务器资源使用情况 一、top命令二、理解IOPS三、腾讯云机器cvm四、iotop五、atop六、查看内存使用情况一、top命令 "top"命令是一个Linux系统的实用工具,用于动态监视系统的运行状态。它会实时显示系统中正在运行的进程列表,并按照CPU使用率、内存使用率等指…...

锐浪报表 Grid++Report 明细表格标题重复打印
一、问题提出 锐浪报表 GridReport,打印表格时,对于明细表格的标题,打开换页时,需要重复打印明细表格的标题,或取消打印明细表格的标题。见下表: 首页: 后续页:(无明细表…...
编程笔记 html5cssjs 048 CSS链接
编程笔记 html5&css&js 048 CSS链接 一、设置链接样式二、文本装饰三、背景色四、链接按钮五、练习小结 通过 CSS,可以用不同的方式设置链接的样式。 一、设置链接样式 链接可以使用任何 CSS 属性(例如 color、font-family、background 等&…...

Spring DI
目录 什么是依赖注入 属性注入 构造函数注入 Setter 注入 依赖注入的优势 什么是依赖注入 依赖注入是一种设计模式,它通过外部实体(通常是容器)来注入一个对象的依赖关系,而不是在对象内部创建这些依赖关系。这种方式使得对象…...

CorelDRAW Graphics Suite2024专业图形设计软件Mac/Windows版
以激情为创造动力,以目的为设计导向。“对专业的图形设计人士来说,CorelDRAW 是市面上最易于使用的软件。 我用它设计过标牌、横幅、T 恤,甚至 45 英尺长的房车图形!” 使用 CorelDRAW 全新的聚焦模式,现在可以单独编…...

如何本地部署虚拟数字克隆人 SadTalker
环境: Win10 SadTalker 问题描述: 如何本地部署虚拟数字克隆人 SadTalker 解决方案: SadTalker:学习逼真的3D运动系数,用于风格化的音频驱动的单图像说话人脸动画 单张人像图像🙎 ♂️音频dz…...
电容充电时间的计算
电容充电时间的计算公式有两种。一种用于已知V0,Vu,Vt,R和C的情况,另一种用于已知E,R和C的情况。 当已知V0,Vu,Vt,R和C时: 设 V0 为电容上的初始电压值设 Vu 为电容充满…...
MicroPython核心(1):源码获取、编译构建
本文介绍了MicroPython在主要平台进行构建的基本方法,包括如何进行版本控制、获取和构建移植的源代码、构建文档、运行测试,以及MicroPython代码库的目录结构。 使用git进行源码管理 MicroPython托管在GitHub上,并使用Git进行源码管理。在进…...
pyspark之Structured Streaming file文件案例1
# generate_file.py # 生成数据 生成500个文件,每个文件1000条数据 # 生成数据格式:eventtime name province action ()时间 用户名 省份 动作) import os import time import shutil import time FIRST_NAME [Zhao, Qian, Sun, Li, Zhou, Wu, Zheng, Wang] SEC…...

虚幻UE 特效-Niagara特效实战-雨天
回顾Niagara特效基础知识:虚幻UE 特效-Niagara特效初识 其他两篇实战:虚幻UE 特效-Niagara特效实战-火焰、烛火、虚幻UE 特效-Niagara特效实战-烟雾、喷泉 本篇笔记我们再来实战雨天,雨天主要用到了特效中的事件。 文章目录 一、雨天1、创建雨…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...