怎么使用three.js 绘制三维带箭头线
发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,今天就跟大家聊聊有关怎么使用three.js 绘制三维带箭头线,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。需求:这个需求是个刚需啊!在一个地
千家信息网最后更新 2026年11月01日怎么使用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安全错误
数据库的锁怎样保障安全
百单网互联网科技有限公司
喔噻互联网科技有限公司
湖北九亚互联网络科技有限公司
江苏根荣互联网科技
金融科技创新监管互联网金融
奇酷互联网科技公司
长亮科技 互联网金融
富国互联网科技创新基金
中国的互联网科技企业的崛起
茂名市互联网科技产业园
中化能源互联网科技招聘
佳云科技和互联网广告
健康食品互联网创新科技园项目
洛阳共同富裕互联网科技有限公司
众安科技工业互联网合作
上海创道互联网络科技有限公司
山东大美朱楼互联网科技有限公司
科技互联网经济论坛
悟空问答互联网科技
现代互联网科技公司
手磨咖啡
互联网科技工作委员会毛军
成都互联网科技公司
菲比寻常互联网科技
国外互联网科技网站
深圳卡尔迅互联网科技有限公司怎么样
广州优美互联网科技
浙南科技城互联网大厦建设工程
华夏恒生科技与互联网科技
绵阳真晨互联网科技有限公司