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、创建雨…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...