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、创建雨…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
