three.js 缓动算法.easing(渐入相机动画)

效果:淡入,靠近物体

代码:

<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(),transparent: true,//开启透明计算opacity: 0.0//完全透明});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,opacity: equipment_A.material.opacity//完全透明}).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,opacity: 1.0},3000).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();equipment_A.material.opacity = obj.opacity;}).onComplete(obj => {equipment_A.material.transparent = false;}).start().easing(TWEEN.Easing.Sinusoidal.InOut); //使用二次缓动函数;/**easing()语法格式* // easing函数:缓动算法(运动效果)// easing类型:定义缓动算法起作用地方tween.easing(TWEEN.Easing.easing函数.easing类型); // easing类型  In , Out , InOutLinear:默认效果可以不设置,可以理解为没有加速过程直接进入匀速状态,或者说没有减速过程,直接刹车Quadratic:二次方的缓动(t^2)Cubic:三次方的缓动(t^3)Quartic:四次方的缓动(t^4)Quintic:五次方的缓动(t^5)Sinusoidal:正弦曲线的缓动(sin(t))Exponential:指数曲线的缓动(2^t)启动非常慢,后面快Circular:圆形曲线的缓动(sqrt(1-t^2))会有弹性衰减往复运动感Elastic:指数衰减的正弦曲线缓动;TWEEN.Easing.Elastic.inout 会有弹性衰减往复运动感Back:超过范围的三次方缓动((s+1)*t^3 – s*t^2)会有弹性衰减往复运动感Bounce:指数衰减的反弹缓动。会有弹性衰减往复运动感*  */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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/243002.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HBuilder X中uView UI框架的安装及使用

开发工具: HBuilder X 在最上方的工具中点击 插件安装 ——> 安装新插件 ——> 前往插件市场安装 在作者排行榜中找到 uView UI 选择该版本 然后点击下载并导入HBuilder X 然后选择你想导入的项目 在项目根目录中的main.js中&#xff0c;引入并使用uView的JS库&#xff…

CTFhub-bak文件

CTFhub-Web-信息泄露-备份文件下载-bak文件 题目信息 解题过程 看到提示说和index.php有关&#xff0c;在url后面加index.php.bak&#xff0c;跳转到http://challenge-7a4da2076cfabae6.sandbox.ctfhub.com:10800/index.php.bak网址&#xff0c;即&#xff1a; 跳转到下载页…

菜鸟关于做前、后端的整理(html、js),以及疑问

涉及到后端的接口py&#xff0c;前端html和js 这三部分就按照如下格式放到server项目主路径下&#xff0c;这样后端机可以作为一个前端server main.pystaticmain.jsmain.htmlhtml 首先是html要设定网页的显示 <!DOCTYPE html> <html> <head><title>…

循环神经网络的变体模型-LSTM、GRU

一.LSTM&#xff08;长短时记忆网络&#xff09; 1.1基本介绍 长短时记忆网络&#xff08;Long Short-Term Memory&#xff0c;LSTM&#xff09;是一种深度学习模型&#xff0c;属于循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;的一种变体。…

Wincoot消除拉式图里outlier的多种策略

在蛋白质中,氨基酸残基之间由肽键相连。 由于肽键的平面性,每个残基的构象都可以用两个扭转角来描述。而空间位阻的关系又导致这两个扭转角只能取有限的值,并用拉式图(Ramachandran plot)来表述它们的允许区域。 拉式图可被用来描述蛋白质结构模型的总体质量。 对于确定…

万物简单AIoT 端云一体实战案例学习 之 快速开始

学物联网,来万物简单IoT物联网!! 下图是本案的3步导学,每个步骤中实现的功能请参考图中的说明。 1、简介 物联网具有场景多且复杂、链路长且开发门槛高等特点,让很多想学习或正在学习物联网的学生或开发者有点不知所措,甚至直接就放弃了。    万物简单AIoT物联网教育…

(2)(2.1) Andruav Android Cellular(一)

文章目录 前言 1 Andruav 是什么&#xff1f; 2 Andruav入门 3 Andruav FPV 4 Andruav GCS App​​​​​​​ 前言 Andruav 是一个基于安卓的互联系统&#xff0c;它将安卓手机作为公司计算机&#xff0c;为你的无人机和遥控车增添先进功能。 1 Andruav 是什么&#xff…

接口自动化测试框架设计

文章目录 接口测试的定义接口测试的意义接口测试的测试用例设计接口测试的测试用例设计方法postman主要功能请求体分类JSON数据类型postman内置参数postman变量全局变量环境变量 postman断言JSON提取器正则表达式提取器Cookie提取器postman加密接口签名 接口自动化测试基础getp…

【5G 接口协议】N2接口协议NGAP(NG Application Protocol)介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

Miracast手机高清投屏到电视(免费)

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Miracast概述 Miracast是一种无线显示标准&#xff0c;它允许支持Miracast的设备之间通过Wi-Fi直接共享音频和视频内容&#xff0c;实现屏幕镜像或扩展显示。这意味着你可以…

【项目日记(三)】内存池的整体框架设计

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你做项目   &#x1f51d;&#x1f51d; 开发环境: Visual Studio 2022 项目日…

k8s的对外服务---ingress

service的作用体现在两个方面&#xff1a; 集群内部&#xff1a;不断追踪pod的变化。他会更新endpoint中的pod对象&#xff0c;基于pod的IP地址不断变化的一种服务发现机制。 集群外部&#xff1a;类似负载均衡器&#xff0c;把流量IP端口&#xff0c;不涉及转发url(http、htt…

如何本地部署虚VideoReTalking

环境&#xff1a; Win10专业版 VideoReTalking 问题描述&#xff1a; 如何本地部署虚VideoReTalking 解决方案&#xff1a; VideoReTalking是一个强大的开源AI对嘴型工具&#xff0c;它是我目前使用过的AI对嘴型工具中效果最好的一个&#xff01;它是由西安电子科技大学、…

医学图像的数据增强技术 --- 切割-拼接数据增强(CS-DA)

医学图像的新型数据增强技术 CS-DA 核心思想自然图像和医学图像之间的关键差异CS-DA 步骤确定增强后的数据数量 代码复现 CS-DA 核心思想 论文链接&#xff1a;https://arxiv.org/ftp/arxiv/papers/2210/2210.09099.pdf 大多数用于医学分割的数据增强技术最初是在自然图像上开…

H5嵌入小程序适配方案

时间过去了两个多月&#xff0c;2024已经到来&#xff0c;又老了一岁。头发也掉了好多。在这两个月时间里都忙着写页面&#xff0c;感觉时间过去得很快。没有以前那么轻松了。也不是遇到了什么难点技术&#xff0c;而是接手了一个很烂得项目。能有多烂&#xff0c;一个页面发起…

Vue2移动端项目使用$router.go(-1)不生效问题记录

目录 1、this.$router.go(-1) 改成 this.$router.back() 2、存储 from.path&#xff0c;使用 this.$router.push 3、hash模式中使用h5新增的onhashchange事件做hack处理 4、this.$router.go(-1) 之前添加一个 replace 方法 问题背景 &#xff1a; 在 Vue2 的一个移动端开发…

tag 标签

tag 标签 在使用 Git 版本控制的过程中&#xff0c;会产生大量的版本。如果我们想对某些重要版本进行记录&#xff0c;就可以给仓库历史中的某一个commit 打上标签&#xff0c;用于标识。 在本章中&#xff0c;我们将会学习如何列出已有的标签、如何创建和删除新的标签、以及…

【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径

作者推荐 视频算法专题 本文涉及知识点 动态规划汇总 广度优先搜索 状态压缩 LeetCode847 访问所有节点的最短路径 存在一个由 n 个节点组成的无向连通图&#xff0c;图中的节点按从 0 到 n - 1 编号。 给你一个数组 graph 表示这个图。其中&#xff0c;graph[i] 是一个列…

数学建模学习笔记||层次分析法

评价类问题 解决评价类问题首先需要想到一下三个问题 我们评价的目标是什么我们为了达到这个目标有哪几种可行方案评价的准则或者说指标是什么 对于以上三个问题&#xff0c;我们可以根据题目中的背景材料&#xff0c;常识以及网上收集到的参考资料进行结合&#xff0c;从而筛…

问题:Feem无法发送信息OR无法连接(手机端无法发给电脑端)

目录 前言 问题分析 资源、链接 其他问题 前言 需要在小米手机、华为平板、Dell电脑之间传输文件&#xff0c;试过安装破解的华为电脑管家、小米的MIUI文件传输等&#xff0c;均无果。&#xff08;小米“远程管理”ftp传输倒是可以&#xff0c;但速度太慢了&#xff0c;且…