需求:这个需求是个刚需啊!在一个地铁场景里展示逃生路线,这个路线肯定是要有指示箭头的,为了画这个箭头,我花了不少于十几个小时,总算做出来了,但始终有点问题。我对这个箭头的要求是,无论场景拉近还是拉远,这个箭头不能太大,也不能太小看不清,形状不能变化,否则就不像箭头了。
使用到了 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 = Utils(); let msg = Msg(); this._isDrawing = false; this._path = []; this._lines =this._arrows =this.color = '#00F300'; this._depthTest = truethis._hide = this._lastRefreshTime = Date().getTime(); let _side = 0; let viewerContainerId = '#threeCanvas'; let viewerContainer = $(viewerContainerId)[0]; let objects; let camera; let turn; let scene; let canvasTexture; let material; this.config = (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 } canvasTexture = _canvasDraw.drawArrow(THREE,renderer,300,100); //箭头 material = MeshLineMaterial({ useMap: ,map: canvasTexture,color: THREE.Color(_self.color),opacity: 1 THREE.Vector2($(viewerContainerId).width(),$(viewerContainerId).height()),lineWidth: 50new THREE.Vector2(1,1),transparent: }); } this.start = () { if (!._isDrawing) { ; viewerContainer.addEventListener('click''mouseup'this.stop = if (; viewerContainer.removeEventListener('click''mouseup' mousedown(params) { this._mousedownPosition = mouseup(params) { this._mouseupPosition = ray(e) { turn.unFocusButton(); let raycaster = createRaycaster(e.clientX,e.clientY); let objs = []; objects.all.map(object => { if (object.material.visible) { objs.push(object); } }); let intersects = raycaster.intersectObjects(objs); if (intersects.length > 0) { let point = intersects[0].point; let distance = utils.distance(this._mousedownPosition.x,this._mousedownPosition.y,1)">this._mousedownPosition.z,1)">this._mouseupPosition.x,1)">this._mouseupPosition.y,1)">._mouseupPosition.z); if (distance < 5) { _self._path.push({ x: point.x,y: point.y + 50if (_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); } } } } 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 = THREE.Raycaster(camera.position,ray); return raycaster; } this.refresh = new Date().getTime() - this._lastRefreshTime > 200) { Date().getTime(); ) { let distance = utils.distance(this._oldCameraPos.x,1)">this._oldCameraPos.y,1)">._oldCameraPos.z,camera.position.x,camera.position.y,camera.position.z); let ratio = 1; this._oldDistance != 0) { ratio = Math.abs((this._oldDistance - distance) / ._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; 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 = LineGeometry(); geometry.setPositions(positions); geometry.setColors([ parseInt(_self.color.substr(1,2),16) / 256 ]); let matLine = LineMaterial({ color: in world units with size attenuation,pixels otherwise dashed: $(viewerContainerId).width()) }); let line = Line2(geometry,matLine); line.computeLineDistances(); line.scale.set(50,50,50); scene.add(line); _self._lines.push(line); } drawArrow(point1,point2) { var meshLine = _self.createArrowLine(point1,point2); var mesh = THREE.Mesh(meshLine.geometry,material); mesh.scale.set(50,1)">); scene.add(mesh); _self._arrows.push(mesh); } refreshArrow(point1,arrow) { meshLine.geometry; arrow.material = material; } this.createArrowLine = (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.z); console.log("d=",d); let sc = 0.035var 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); let meshLine = MeshLine(); meshLine.setGeometry(arrowLinePoints); meshLine; } this.setDepthTest = (bl) { (bl) { _self._depthTest = ; this._lines.map(line => { line.material.depthTest = ; line.material.side = 0; }); this._arrows.map(arrow => { arrow.material.depthTest = ; arrow.material.side = 0; }); } else { _self._depthTest = ; line.material.side = THREE.DoubleSide; }); ; arrow.material.side = THREE.DoubleSide; }); } } this.getPath = return ._path; } this.hide = scene.remove(line)); scene.remove(arrow)); ; } this.show = scene.add(line)); scene.add(arrow)); this.isShow = return !._hide; } this.create = (path,color) { _self.color = color; _self._path = path; material = }); ) { let point1 = _self._path[i]; let point2 = _self._path[i + 1]; drawLine(point1,point2); drawArrow(point1,point2); } } } this.getDepthTest = _self._depthTest; } * * 撤销 */ this.undo = () { 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 }
CanvasDraw.js:
* * canvas绘图 let CanvasDraw = () { * * 画文本和气泡 this.drawText = (THREE,text,width) { let canvas = document.createElement("canvas"); let ctx = canvas.getContext('2d'); canvas.width = width * 2; canvas.height = width * 2; this.drawBubble(ctx,width - 10,width - 65,width,45,6,"#00c864"); 设置文字 ctx.fillStyle = "#ffffff"; ctx.font = '32px 宋体'; ctx.fillText(text,width - 10 + 12,width - 65 + 34); let canvasTexture = THREE.CanvasTexture(canvas); canvasTexture.magFilter = THREE.NearestFilter; canvasTexture.minFilter = THREE.NearestFilter; let maxAnisotropy = renderer.capabilities.getMaxAnisotropy(); canvasTexture.anisotropy = maxAnisotropy; canvasTexture; } * * 画箭头 this.drawArrow = ); canvas.width = width; canvas.height = height; ctx.save(); ctx.translate(0,0this.drawRoundRectPath(ctx,height,0); ctx.fillStyle = "#ffff00"; ctx.fill(); this.drawArrowBorder(ctx,2,4,100,96,1)">); ctx.fillStyle = "#ffffff"; ctx.fill(); ctx.restore(); let canvasTexture = * * 画气泡 this.drawBubble = (ctx,x,radius,fillColor) { ctx.save(); ctx.translate(x,y); .drawRoundRectPath(ctx,radius); ctx.fillStyle = fillColor || "#000"; ctx.fill(); this.drawTriangle(ctx,20,40,10,65); ctx.fillStyle = fillColor || "#000"; ctx.fill(); ctx.restore(); } * * 画三角形 this.drawTriangle = * * 画箭头边框 this.drawArrowBorder = * * 画圆角矩形 this.drawRoundRectPath = 从右下角顺时针绘制,弧度从0到1/2PI ctx.arc(width - radius,height - radius,Math.PI / 2矩形下边线 ctx.lineTo(radius,height); 左下角圆弧,弧度从1/2PI到PI ctx.arc(radius,Math.PI); 矩形左边线 ctx.lineTo(0左上角圆弧,弧度从PI到3/2PI ctx.arc(radius,Math.PI,Math.PI * 3 / 2上边线 ctx.lineTo(width - radius,1)">右上角圆弧 ctx.arc(width - radius,Math.PI * 3 / 2,Math.PI * 2右边线 ctx.lineTo(width,height - radius); ctx.closePath(); } * * 画圆 this.drawCircle = height; ctx.save(); ctx.beginPath(0); ctx.arc(width / 2,height / 2,2 * Math.PI); ctx.closePath(); ctx.fillStyle = fillColor || "#000"; ctx.fill(); ctx.restore(); let texture = THREE.CanvasTexture(canvas); texture.needsUpdate = ; texture.magFilter = THREE.NearestFilter; texture.minFilter = renderer.capabilities.getMaxAnisotropy(); texture.anisotropy = texture; } } CanvasDraw.prototype.constructor = CanvasDraw; export { CanvasDraw }
show.js中的部分代码:
let drawPath; 绘制线路 drawPath = DrawPath(); drawPath.config( objects,camera,scene,turn ); $("#rightContainer").show(); $("#line-start").on("click",1)"> (event) { drawPath.start(); }); $("#line-stop").on("click",1)"> (event) { drawPath.stop(); }); $("#line-undo").on("click",1)"> (event) { drawPath.undo(); }); $("#line-show").on("click",1)"> (event) { drawPath.refresh(); }); let depthTest = ; $("#line-depthTest").on("click",1)"> (event) { (depthTest) { drawPath.setDepthTest(); depthTest = ; } { drawPath.setDepthTest(; } }); setInterval(() => { drawPath && drawPath.refresh(); },100);
效果图:
还是有点问题:
虽然这个效果图中,场景拉近,箭头有点大,但是最大大小还是做了控制的,就是这个形状有点问题,可能是视角的问题。
我期望的效果应该是这样的,就是无论从什么角度看,箭头不要变形:
原文地址:https://www.cnblogs.com/s0611163
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。