怎么使用three.js 绘制三维带箭头线
发表于:2025-11-07 作者:千家信息网编辑
千家信息网最后更新 2025年11月07日,今天就跟大家聊聊有关怎么使用three.js 绘制三维带箭头线,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。需求:这个需求是个刚需啊!在一个地
千家信息网最后更新 2025年11月07日怎么使用three.js 绘制三维带箭头线
今天就跟大家聊聊有关怎么使用three.js 绘制三维带箭头线,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
需求:这个需求是个刚需啊!在一个地铁场景里展示逃生路线,这个路线肯定是要有指示箭头的,为了画这个箭头,我花了不少于十几个小时,总算做出来了,但始终有点问题。我对这个箭头的要求是,无论场景拉近还是拉远,这个箭头不能太大,也不能太小看不清,形状不能变化,否则就不像箭头了。
使用到了 three.js 的 Line2.js 和一个开源库MeshLine.js
部分代码:
DrawPath.js:
/** * 绘制路线 */import * as THREE from '../build/three.module.js';import { MeshLine, MeshLineMaterial, MeshLineRaycast } from '../js.my/MeshLine.js';import { Line2 } from '../js/lines/Line2.js';import { LineMaterial } from '../js/lines/LineMaterial.js';import { LineGeometry } from '../js/lines/LineGeometry.js';import { GeometryUtils } from '../js/utils/GeometryUtils.js';import { CanvasDraw } from '../js.my/CanvasDraw.js';import { Utils } from '../js.my/Utils.js';import { Msg } from '../js.my/Msg.js';let DrawPath = function () { let _self = this; let _canvasDraw = new CanvasDraw(); let utils = new Utils(); let msg = new Msg(); this._isDrawing = false; this._path = []; this._lines = []; this._arrows = []; let _depthTest = true; let _side = 0; let viewerContainerId = '#cc'; let viewerContainer = $(viewerContainerId)[0]; let objects; let camera; let turn; let scene; this.config = function (objects_, camera_, scene_, turn_) { objects = objects_; camera = camera_; turn = turn_; scene = scene_; this._oldDistance = 1; this._oldCameraPos = { x: camera.position.x, y: camera.position.y, z: camera.position.z } } this.start = function () { if (!this._isDrawing) { this._isDrawing = true; viewerContainer.addEventListener('click', ray); viewerContainer.addEventListener('mousedown', mousedown); viewerContainer.addEventListener('mouseup', mouseup); } msg.show("请点击地面画线"); } this.stop = function () { if (this._isDrawing) { this._isDrawing = false; viewerContainer.removeEventListener('click', ray); viewerContainer.addEventListener('mousedown', mousedown); viewerContainer.addEventListener('mouseup', mouseup); } msg.show("停止画线"); } function mousedown(params) { this._mousedownPosition = { x: camera.position.x, y: camera.position.y, z: camera.position.z } } function mouseup(params) { this._mouseupPosition = { x: camera.position.x, y: camera.position.y, z: camera.position.z } } function ray(e) { turn.unFocusButton(); let raycaster = createRaycaster(e.clientX, e.clientY); let intersects = raycaster.intersectObjects(objects.all); if (intersects.length > 0) { let point = intersects[0].point; let distance = utils.distance(this._mousedownPosition.x, this._mousedownPosition.y, this._mousedownPosition.z, this._mouseupPosition.x, this._mouseupPosition.y, this._mouseupPosition.z); if (distance < 5) { _self._path.push({ x: point.x, y: point.y + 50, z: point.z }); if (_self._path.length > 1) { let point1 = _self._path[_self._path.length - 2]; let point2 = _self._path[_self._path.length - 1]; drawLine(point1, point2); drawArrow(point1, point2); } } } } function createRaycaster(clientX, clientY) { let x = (clientX / $(viewerContainerId).width()) * 2 - 1; let y = -(clientY / $(viewerContainerId).height()) * 2 + 1; let standardVector = new THREE.Vector3(x, y, 0.5); let worldVector = standardVector.unproject(camera); let ray = worldVector.sub(camera.position).normalize(); let raycaster = new THREE.Raycaster(camera.position, ray); return raycaster; } this.refresh = function () { if (_self._path.length > 1) { let distance = utils.distance(this._oldCameraPos.x, this._oldCameraPos.y, this._oldCameraPos.z, camera.position.x, camera.position.y, camera.position.z); let ratio = 1; if (this._oldDistance != 0) { ratio = Math.abs((this._oldDistance - distance) / this._oldDistance) } if (distance > 5 && ratio > 0.1) { console.log("======== DrawPath 刷新 ====================================================") for (let i = 0; i < _self._path.length - 1; i++) { let arrow = _self._arrows[i]; let point1 = _self._path[i]; let point2 = _self._path[i + 1]; refreshArrow(point1, point2, arrow); } this._oldDistance = distance; this._oldCameraPos = { x: camera.position.x, y: camera.position.y, z: camera.position.z } } } } function drawLine(point1, point2) { const positions = []; positions.push(point1.x / 50, point1.y / 50, point1.z / 50); positions.push(point2.x / 50, point2.y / 50, point2.z / 50); let geometry = new LineGeometry(); geometry.setPositions(positions); let matLine = new LineMaterial({ color: 0x009900, linewidth: 0.003, // in world units with size attenuation, pixels otherwise dashed: true, depthTest: _depthTest, side: _side }); let line = new Line2(geometry, matLine); line.computeLineDistances(); line.scale.set(50, 50, 50); scene.add(line); _self._lines.push(line); } function drawArrow(point1, point2) { let arrowLine = _self.createArrowLine(point1, point2); var meshLine = arrowLine.meshLine; let canvasTexture = _canvasDraw.drawArrow(THREE, renderer, 300, 100); //箭头 var material = new MeshLineMaterial({ useMap: true, map: canvasTexture, color: new THREE.Color(0x00f300), opacity: 1, resolution: new THREE.Vector2($(viewerContainerId).width(), $(viewerContainerId).height()), lineWidth: arrowLine.lineWidth, depthTest: _depthTest, side: _side, repeat: new THREE.Vector2(1, 1), transparent: true, sizeAttenuation: 1 }); var mesh = new THREE.Mesh(meshLine.geometry, material); mesh.scale.set(50, 50, 50); scene.add(mesh); _self._arrows.push(mesh); } function refreshArrow(point1, point2, arrow) { let arrowLine = _self.createArrowLine(point1, point2); var meshLine = arrowLine.meshLine; let canvasTexture = _canvasDraw.drawArrow(THREE, renderer, 300, 100); //箭头 var material = new MeshLineMaterial({ useMap: true, map: canvasTexture, color: new THREE.Color(0x00f300), opacity: 1, resolution: new THREE.Vector2($(viewerContainerId).width(), $(viewerContainerId).height()), lineWidth: arrowLine.lineWidth, depthTest: _depthTest, side: _side, repeat: new THREE.Vector2(1, 1), transparent: true, sizeAttenuation: 1 }); arrow.geometry = meshLine.geometry; arrow.material = material; } this.createArrowLine = function (point1, point2) { let centerPoint = { x: (point1.x + point2.x) / 2, y: (point1.y + point2.y) / 2, z: (point1.z + point2.z) / 2 }; let distance = utils.distance(point1.x, point1.y, point1.z, point2.x, point2.y, point2.z); var startPos = { x: (point1.x + point2.x) / 2 / 50, y: (point1.y + point2.y) / 2 / 50, z: (point1.z + point2.z) / 2 / 50 } let d = utils.distance(centerPoint.x, centerPoint.y, centerPoint.z, camera.position.x, camera.position.y, camera.position.z); if (d < 2000) d = 2000; if (d > 10000) d = 10000; let lineWidth = 100 * d / 4000; //console.log("d=", d); let sc = 0.035; var endPos = { x: startPos.x + (point2.x - point1.x) * sc * d / distance / 50, y: startPos.y + (point2.y - point1.y) * sc * d / distance / 50, z: startPos.z + (point2.z - point1.z) * sc * d / distance / 50 } var arrowLinePoints = []; arrowLinePoints.push(startPos.x, startPos.y, startPos.z); arrowLinePoints.push(endPos.x, endPos.y, endPos.z); var meshLine = new MeshLine(); meshLine.setGeometry(arrowLinePoints); return { meshLine: meshLine, lineWidth: lineWidth }; } this.setDepthTest = function (bl) { if (bl) { _depthTest = true; this._lines.map(line => { line.material.depthTest = true; line.material.side = 0; }); this._arrows.map(arrow => { arrow.material.depthTest = true; arrow.material.side = 0; }); } else { _depthTest = false; this._lines.map(line => { line.material.depthTest = false; line.material.side = THREE.DoubleSide; }); this._arrows.map(arrow => { arrow.material.depthTest = false; arrow.material.side = THREE.DoubleSide; }); } } /** * 撤销 */ this.undo = function () { scene.remove(this._lines[this._lines.length - 1]); scene.remove(this._arrows[this._arrows.length - 1]); _self._path.splice(this._path.length - 1, 1); _self._lines.splice(this._lines.length - 1, 1); _self._arrows.splice(this._arrows.length - 1, 1); }}DrawPath.prototype.constructor = DrawPath;export { DrawPath }show.js中的部分代码:
let drawPath; //绘制线路 drawPath = new DrawPath(); drawPath.config( objects, camera, scene, turn ); $("#rightContainer").show(); $("#line-start").on("click", function (event) { drawPath.start(); }); $("#line-stop").on("click", function (event) { drawPath.stop(); }); $("#line-undo").on("click", function (event) { drawPath.undo(); }); $("#line-show").on("click", function (event) { drawPath.refresh(); }); let depthTest = true; $("#line-depthTest").on("click", function (event) { if (depthTest) { drawPath.setDepthTest(false); depthTest = false; } else { drawPath.setDepthTest(true); depthTest = true; } });setInterval(() => { drawPath && drawPath.refresh();}, 100);效果图:
还是有点问题:
虽然这个效果图中,场景拉近,箭头有点大,但是最大大小还是做了控制的,就是这个形状有点问题,可能是视角的问题。
我期望的效果应该是这样的,就是无论从什么角度看,箭头不要变形:
看完上述内容,你们对怎么使用three.js 绘制三维带箭头线有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。
箭头
问题
内容
场景
效果
路线
还是
三维
代码
就是
形状
效果图
部分
需求
最大
地铁
地面
大小
小时
指示
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
sci数据库技术
无代码软件开发成熟项目
良好的网络安全维护
姜堰区环保网络技术诚信服务
tbc可以服务器
戴尔服务器一按开机就不启动
酷凌软件开发
如何在数据库中导出文件
信息网络安全培训的重点
新乡市荣耀网络技术
网络安全隔离质检产品抽查
宿州医疗软件开发定制
网络安全教育 总结报告
查询数据库的版本oracle
美团外卖用什么软件开发语言
DNS服务器地址可以共用吗
jsp查询数据库语句
网络技术渗透
关系数据库的基本知识
战双帕弥什b站属于什么服务器
三级考试网络技术题
数据库网络工程师招聘
数据库引擎优化
nba教练数据库
汉武大帝下载软件开发
网络安全防范与技术
网络安全教育 总结报告
网络技术员技能
浪潮服务器怎么重置管理密码
张家界口碑好的软件开发