ThreeJS-3D教学三:平移缩放+物体沿轨迹运动

我们在项目中会有一些这样的需求,我们可视化一个场景,需要俯视、平移、缩放,方便观察场景中的数据或者模型,之所以把这个案例拿出来
1、这是个很实用的需求,我相信很多人会用到
2、我自己认为在实际案例中我们可以学习相关知识点更易吸收些
为了丰富本篇文章知识点,我还加入了一个物体沿轨迹运动的场景,下面代码会介绍到,回到之前的问题three中可以利用对 OrbitControls 的设置很轻松的实现相关场景,代码如下:

controls = new OrbitControls( camera, renderer.domElement );// 移动端控制平移 缩放// controls.touches = {//   ONE: THREE.TOUCH.PAN,//   TWO: THREE.TOUCH.DOLLY_PAN// };// PC 左平移,右旋转 滚轮放大缩小   默认是右平移controls.mouseButtons = {LEFT: THREE.MOUSE.PAN,MIDDLE: THREE.MOUSE.DOLLY,RIGHT: THREE.MOUSE.ROTATE};// 设置最大最小视距controls.minDistance = 20;controls.maxDistance = 1000;controls.autoRotate = false;controls.enableRotate = false;controls.enablePan = true;

需要注意的是移动端和PC是不同的配置,minDistance和maxDistance按实际需求设置即可,到这一步只是对controls的操作,已经有能力实现效果了,但是camera的设置也是不可或缺的,即camera必须是俯视的

先上下整体效果图
在这里插入图片描述
看下代码:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>body {width: 100%;height: 100%;}* {margin: 0;padding: 0;}.label {font-size: 20px;color: #000;font-weight: 700;}</style>
</head>
<body>
<div id="container"></div>
<script type="importmap">{"imports": {"three": "../three-155/build/three.module.js","three/addons/": "../three-155/examples/jsm/"}}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
let stats, labelRenderer, curve, movingObjects;
let camera, scene, renderer, mesBox, target, controls;
const group = new THREE.Group();
let progress = 0; // 物体运动时在运动路径的初始位置,范围0~1
const velocity = 0.0005; // 影响运动速率的一个值,范围0~1,需要和渲染频率结合计算才能得到真正的速率
let widthImg = 200;
let heightImg = 200;
init();
initHelp();
initLight();
axesHelperWord();
animate();
// 添加一个物体
mesBox = addGeometries();
// 添加平面
addPlane();
// 添加路径
makeCurve();
// 添加一个运动的物体
movingObjects = addMovingObjects();window.addEventListener('mousemove', mousemoveFun);function addPlane() {// 创建一个平面 PlaneGeometry(width, height, widthSegments, heightSegments)const planeGeometry = new THREE.PlaneGeometry(widthImg, heightImg, 1, 1);// 创建 Lambert 材质:会对场景中的光源作出反应,但表现为暗淡,而不光亮。const planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});const plane = new THREE.Mesh(planeGeometry, planeMaterial);// 以自身中心为旋转轴,绕 x 轴顺时针旋转 45 度plane.rotation.x = -0.5 * Math.PI;plane.position.set(0, -4, 0);scene.add(plane);
}function addGeometries() {let img = new THREE.TextureLoader().load('../materials/img/view4.jpg');img.repeat.x = img.repeat.y = 5;const geometry = new THREE.BoxGeometry( 10, 10, 10 );let material = new THREE.MeshPhongMaterial({map: img,flatShading: true,side: THREE.DoubleSide,transparent: 1});const mesh = new THREE.Mesh( geometry, material );mesh.position.x = 0;mesh.position.y = 0;mesh.position.z = -20;scene.add( mesh );return mesh;
}function addMovingObjects() {let img = new THREE.TextureLoader().load('../materials/img/view3.jpg');const geometry = new THREE.BoxGeometry( 10, 10, 10 );const material = new THREE.MeshPhongMaterial({map: img,flatShading: true,side: THREE.DoubleSide,transparent: 1});const mesh = new THREE.Mesh( geometry, material );scene.add( mesh );return mesh;
}function makeCurve() {// 创建一个环形路径curve = new THREE.CatmullRomCurve3([new THREE.Vector3(80, 0, 80),new THREE.Vector3(80, 0, -80),new THREE.Vector3(-80, 0, -80),new THREE.Vector3(-80, 0, 80)]);curve.curveType = "catmullrom";curve.closed = true;//设置是否闭环curve.tension = 0; //设置线的张力,0为无弧度折线// 为曲线添加材质在场景中显示出来,不显示也不会影响运动轨迹,相当于一个Helperconst points = curve.getPoints(20);const geometry = new THREE.BufferGeometry().setFromPoints(points);const material = new THREE.LineBasicMaterial({color: '#f00',linewidth: 1});// Create the final object to add to the sceneconst curveObject = new THREE.Line(geometry, material);curveObject.position.y = 1;curveObject.visible = false;scene.add(curveObject);
}function mousemoveFun() {let width = widthImg;let height = heightImg;let newPo;let newPoHeight;const prevPos = camera.position.clone();if (camera.position['x'] > width / 2) {newPo = width / 2 - 0.01;camera.position.set(newPo, prevPos.y, prevPos.z);controls.target = new THREE.Vector3(newPo, 0, prevPos.z);controls.enablePan = false;} else if (camera.position['x'] < -width / 2) {newPo = -width / 2 + 0.01;camera.position.set(newPo, prevPos.y, prevPos.z);controls.target = new THREE.Vector3(newPo, 0, prevPos.z);controls.enablePan = false;} else if (camera.position['z'] > height / 2) {// 因为我们的坐标系 y轴在上 所以平移时 height 即是 znewPoHeight = height / 2 - 0.01;camera.position.set(prevPos.x, prevPos.y, newPoHeight);controls.target = new THREE.Vector3(prevPos.x, 0, newPoHeight);controls.enablePan = false;} else if (camera.position['z'] < -height / 2) {newPoHeight = -height / 2 + 0.01;camera.position.set(prevPos.x, prevPos.y, newPoHeight);controls.target = new THREE.Vector3(prevPos.x, 0, newPoHeight);controls.enablePan = false;} else {controls.enablePan = true;}
}function init() {camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 10, 2000 );camera.up.set(0, 1, 0);camera.position.set(0, 100, 0);camera.lookAt(0, 0, 0);scene = new THREE.Scene();// scene.background = new THREE.Color( '#ccc' );renderer = new THREE.WebGLRenderer( { antialias: true } );renderer.setPixelRatio( window.devicePixelRatio );renderer.setSize( window.innerWidth, window.innerHeight );document.body.appendChild( renderer.domElement );labelRenderer = new CSS2DRenderer();labelRenderer.setSize( window.innerWidth, window.innerHeight );labelRenderer.domElement.style.position = 'absolute';labelRenderer.domElement.style.top = '0px';labelRenderer.domElement.style.pointerEvents = 'none';document.getElementById( 'container' ).appendChild( labelRenderer.domElement );controls = new OrbitControls( camera, renderer.domElement );// 移动端控制平移 缩放// controls.touches = {//   ONE: THREE.TOUCH.PAN,//   TWO: THREE.TOUCH.DOLLY_PAN// };// PC 左平移,右旋转 滚轮放大缩小   默认是右平移controls.mouseButtons = {LEFT: THREE.MOUSE.PAN,MIDDLE: THREE.MOUSE.DOLLY,RIGHT: THREE.MOUSE.ROTATE};// 设置最大最小视距controls.minDistance = 20;controls.maxDistance = 1000;controls.autoRotate = false;controls.enableRotate = false;controls.enablePan = true;window.addEventListener( 'resize', onWindowResize );stats = new Stats();stats.setMode(1); // 0: fps, 1: msdocument.body.appendChild( stats.dom );scene.add( group );
}function initLight() {let spotLight;spotLight = new THREE.SpotLight( 0xffffff, 500 );spotLight.name = 'Spot Light';spotLight.angle = Math.PI / 5;spotLight.penumbra = 0.1;spotLight.position.set( 0, 5, 10 );spotLight.castShadow = true;spotLight.shadow.camera.near = 2;spotLight.shadow.camera.far = 100;spotLight.shadow.mapSize.width = 1024;spotLight.shadow.mapSize.height = 1024;scene.add( spotLight );const AmbientLight = new THREE.AmbientLight(0xCCCCCC, 2);scene.add( AmbientLight );
}function initHelp() {// const size = 100;// const divisions = 5;// const gridHelper = new THREE.GridHelper( size, divisions );// scene.add( gridHelper );// The X axis is red. The Y axis is green. The Z axis is blue.const axesHelper = new THREE.AxesHelper( 100 );scene.add( axesHelper );
}function axesHelperWord() {let xP = addWord('X轴');let yP = addWord('Y轴');let zP = addWord('Z轴');xP.position.set(50, 0, 0);yP.position.set(0, 50, 0);zP.position.set(0, 0, 50);
}function addWord(word) {let name = `<span>${word}</span>`;let moonDiv = document.createElement( 'div' );moonDiv.className = 'label';// moonDiv.textContent = 'Moon';// moonDiv.style.marginTop = '-1em';moonDiv.innerHTML = name;const label = new CSS2DObject( moonDiv );group.add( label );return label;
}function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize( window.innerWidth, window.innerHeight );
}function animate() {requestAnimationFrame( animate );// mesh绕自身中心的 y 轴自转if (mesBox) {mesBox.rotation.y += 0.01;}if (curve && movingObjects) {if (progress <= 1 - velocity) {// 获取样条曲线指定点坐标const point = curve.getPointAt(progress);movingObjects.position.set(point.x, point.y, point.z);progress += velocity;} else {progress = 0;}}stats.update();controls.update();labelRenderer.render( scene, camera );renderer.render( scene, camera );
}
</script>
</body>
</html>

这里是全部代码,可以看出 three用的是 155版本,大家从官网上下载到本地改下路径即可。图片url你可以换成本地图片,项目就能跑起来了。

中间立方体在做了一个环绕自身y轴旋转,外面的立方体通过获取curve上的坐标点,沿着curve的轨迹运动。

这里有一些新的知识点:

new THREE.TextureLoader().load(‘…/materials/img/view3.jpg’);
TextureLoader是three加载图片的方法,我们还可以这样用
let loader = new THREE.TextureLoader();
loader.load(‘url’, (img) => {
// img 这个img就是加载完成后的图片
});

这时就一个疑问了,同步还是异步,我们来看源码
在这里插入图片描述
其实还是异步,拿到图片后我们发现material有一个map参数,可以接收图片作为材质,这样material就可以做的很丰富了,side是指物体的正反两面,THREE.DoubleSide即两面都要渲染,如果我们只看外面,不要设置这里,这样也能节省一些性能

 let material = new THREE.MeshPhongMaterial({map: img,flatShading: true,side: THREE.DoubleSide,transparent: 1});

最后一个知识点,即物体沿着轨迹运动,我们把这一行代码
curveObject.visible = false; 改为 true 大家就可以看到这个轨迹了。
逻辑代码中已经写的很详细了,大家也可以思考下还有哪些方法可以作为curve使用。
如果感觉有用,点个赞不过分吧!!!

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

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

相关文章

[NOIP2011 提高组] 选择客栈

[NOIP2011 提高组] 选择客栈 题目描述 丽江河边有 n n n 家很有特色的客栈&#xff0c;客栈按照其位置顺序从 1 1 1 到 n n n 编号。每家客栈都按照某一种色调进行装饰&#xff08;总共 k k k 种&#xff0c;用整数 0 ∼ k − 1 0 \sim k-1 0∼k−1 表示&#xff09;&am…

利用EasyX绘制国旗

所谓国庆&#xff0c;举国同庆 今天我就来分享一下利用图形库来制作一个比例版缩小的五星红旗&#xff08;尽量精确了&#xff09; 需要利用到前边的知识note2基本图形的绘制 进入正题 五星红旗的长和高之比为三比二 创建一个长为960像素&#xff0c;宽为640像素的窗体 更…

Vue3.0跨端Web SDK访问微信小程序云储存,文件上传路径不存在/文件受损无法显示问题(已解决)

整理需求&#xff1a; 需要vue3.0作为pc端的后台管理来连接微信小程序客户端需要Web SDK的引入&#xff0c;实现vue3.0接入云开发环境需要以云环境作为线上服务器&#xff0c;将vue3.0上传的本地文件通过云环境进入云储存&#xff0c;并将文件在云端生成云端快捷访问路径及htt…

排序---P1781 宇宙总统

思路&#xff1a; 当我们要对这些超大数进行比较排序时&#xff0c;如果我们用int或long基本数据类型时&#xff0c;会超出能承载的范围&#xff0c;因此我们选择用引用数据类型&#xff1a;BigDecimal或BigInteger。 区别在于基本数据类型直接比较大小&#xff0c;而是调用这…

【吞噬星空】连播两集,尼赫鲁对徐欣动手,罗峰修分身强势复仇

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析吞噬星空资讯。 吞噬星空动画第四季定档之后&#xff0c;官方真的是太宠粉了&#xff0c;每天都会公布全新预告情报&#xff0c;无论是外星人物角色&#xff0c;亦或者宇宙星球建模&#xff0c;那都是相当的炸裂。如今更…

python操作.xlsx文件

from openpyxl import load_workbook from openpyxl.styles import Font,colors, Alignment from openpyxl.styles import Border, Side #打开已经存在的Excel workbook load_workbook(filenameC:\\Users\\yh\\Documents\\测试.xlsx) #创建表&#xff08;sheet&#xff09;,插…

ubuntu下源码编译方式安装opencv

基础条件 ubuntu 20.04 opencv 3.4.3 opencv 源码编译的安装步骤 第一步&#xff0c; 首先clone源码 git clone https://github.com/opencv/opencv.git第二步&#xff0c;依赖包&#xff0c;执行下面的命令 sudo apt-get install build-essential sudo apt-get install cmak…

基于Springboot实现餐厅点餐系统演示【项目源码+论文说明】分享

基于Springboot实现餐厅点餐系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff…

【新的小主机】向日葵远程控制ubuntu

向日葵远程控制ubuntu 一、简介二、问题及解决方法2.1 向日葵远程连接Ubuntu22主机黑屏&#xff1f;2.2 Ubuntu如何向日葵开机自启&#xff1f;2.3 无显示器情况下&#xff0c;windows远程桌面连接Ubuntu? 三、美化桌面3.1 安装/解压3.2 设置 四、安装docker容器及部署微服务4…

【考研数学】高等数学第七模块 —— 曲线积分与曲面积分 | 3. 对面积的曲面积分(第一类曲面积分)

文章目录 二、曲面积分2.1 对面积的曲面积分&#xff08;第一类曲面积分&#xff09;2.1.1 问题引入 —— 曲面的质量2.1.2 对面积的曲面积分定义及性质2.1.3 对面积的曲面积分的计算法 写在最后 二、曲面积分 2.1 对面积的曲面积分&#xff08;第一类曲面积分&#xff09; 2…

LIMS实验室信息管理系统源码 基于计算机的数据处理技术、数据存储技术、网络传输技术、自动化仪器分析技术于一体

LIMS 是一个集现代化管理思想与基于计算机的数据处理技术、数据存储技术、网络传输技术、自动化仪器分析技术于一体&#xff0c;以实验室业务和管理工作为核心&#xff0c;遵循实验室管理国际规范&#xff0c;实现对实验室全方位管理的信息管理系统。 LIMS将样品管理、数据管理…

基于51单片机NEC协议红外遥控发送接收仿真设计( proteus仿真+程序+原理图+报告+讲解视频)

基于51单片机NEC协议红外遥控发送接收仿真设计 讲解视频1.主要功能&#xff1a;2.仿真3. 程序代码4. 原理图5. 设计报告6. 设计资料内容清单&&下载链接 基于51单片机NEC协议红外遥控发送接收仿真设计 51单片机红外发送接收仿真设计( proteus仿真程序原理图报告讲解视频…

阿里云RDS关系型数据库详细介绍_多版本数据库说明

阿里云RDS关系型数据库大全&#xff0c;关系型数据库包括MySQL版、PolarDB、PostgreSQL、SQL Server和MariaDB等&#xff0c;NoSQL数据库如Redis、Tair、Lindorm和MongoDB&#xff0c;阿里云百科分享阿里云RDS关系型数据库大全&#xff1a; 目录 阿里云RDS关系型数据库大全 …

德国云安全协作软件提供商【Rencore】完成800万美元融资

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;总部位于德国慕尼黑的云安全协作软件提供商Rencore今日宣布已完成800万美元融资。 本轮融资由UVC Partners领投。 该公司打算利用这笔资金进一步投资于其云协作治理产品的增长。 Rencore由Matthi…

小谈设计模式(12)—迪米特法则

小谈设计模式&#xff08;12&#xff09;—迪米特法则 专栏介绍专栏地址专栏介绍 迪米特法则核心思想这里的“朋友”指当前对象本身以参数形式传入当前对象的对象当前对象的成员变量直接引用的对象目标 Java程序实现程序分析 总结 专栏介绍 专栏地址 link 专栏介绍 主要对目…

EasyEdge 智能边缘控制台通过sdk发布应用

离线部署SDK生成 模型部署完成后会出现下载SDK的按钮&#xff0c;点击按钮下载SDK并保存好SDK。 进入EasyDL官网的技术文档 安装智能边缘控制台 跟着教程&#xff0c;完成安装&#xff1a;点此链接 树莓派4b是Linux arm64的架构&#xff0c;点击对应的链接进行下载。 下载完成…

OCI 发布了容器运行时和镜像规范!

7 月 19 日是开放容器计划Open Container Initiative&#xff08;OCI&#xff09;的一个重要里程碑&#xff0c;OCI 发布了容器运行时和镜像规范的 1.0 版本&#xff0c;而 Docker 在这过去两年中一直充当着推动和引领的核心角色。 我们的目标是为社区、客户以及更广泛的容器行…

PHP 反序列化漏洞:身份标识

文章目录 参考环境访问修饰符访问修饰符PHP 与访问修饰符 手写身份标识身份标识定义身份标识控制字符 NUL在 PHP 中如何表示空字符&#xff1f; 通过空字符尝试构建包含非公共属性对象的序列化文本 空字符的传输控制字符的不可打印性结论另辟蹊径URL 字符编码将非 ASCII 字符文…

【进阶C语言】自定义类型

本节内容大致目录如下&#xff1a; 1.结构体 2.位段 3.枚举 4.联合&#xff08;共用体&#xff09; 以上都是C语言中的自定义类型&#xff0c;可以根据我们的需要去定义。 一、结构体 一些基础知识在初阶C语言的时候已经介绍过&#xff0c;在这里粗略概括&#xff1b;重…

wxWidgets(1):在Ubuntu 环境中搭建wxWidgets 库环境,安装库和CodeBlocks的IDE,可以运行demo界面了,继续学习中

1&#xff0c;选择使用 wxWidgets 框架 选择这个主要是因为完全的开源&#xff0c;不想折腾 Qt的库&#xff0c;而且打包的文件比较大。 网络上面有很多的对比&#xff0c;而且使用QT的人比较多。 但是我觉得wxwidgets 更加偏向 c 语法本身&#xff0c;也有助学习C。 没有太多…