展示二维室内定位
1、需求:最近在弄一个实时定位的展示模块,就是说前端通过webSocket获取到用户位置信息:实时展示在图上,还会有图文提示等
2、思路:这个有两种解决方案:1、canvas来处理。2、svg来处理
一开始不知道哪个解决方案好,就做了两个demo探探水
总体来说:如果交互并不多的话推荐canvas处理就完事了,如果交互很强的话,比如说里面很多元素需要触发各种不同的事件,推荐svg
3、解决:
3-1、canvas
html
<canvas id="myCanvas" ref="myCanvas"></canvas>
js
/*画布属性 */ let c = this.$refs.myCanvas; c.width = 800; c.height = 400; const ctx = c.getContext("2d"); /*加载图片 */ let img = new Image(); img.src = require("./assets/003.jpg"); img.onload = function () { drawImageData(0, 0, 150, 150); }; let seat = new Image(); seat.src = require("./assets/004.png"); /*拖动计算位置需要的变量 */ let beforeX = 0, beforeY = 0, afterX = 0, afterY = 0, chaX = add(beforeX, -afterX), chaY = add(beforeY, -afterY), isMousemove = false, multiple = 1; /**图片位置 */ let x = 0, y = 0, width = 150, height = 150; /**监听拖动图片 */ c.addEventListener("mousedown", (e) => { beforeX = e.offsetX; beforeY = e.offsetY; isMousemove = true; }); c.addEventListener("mouseup", (e) => { isMousemove = false; x = add(x, -chaX); y = add(y, -chaY); beforeX = e.offsetX; beforeY = e.offsetY; }); c.addEventListener("mouseleave", (e) => { isMousemove = false; }); c.addEventListener("mousemove", (e) => { if (isMousemove) { afterX = e.offsetX; afterY = e.offsetY; chaX = except(add(beforeX, -afterX), multiple); chaY = except(add(beforeY, -afterY), multiple); drawImageData(add(x, -chaX), add(y, -chaY), width, height); } }); /**定义放大缩小倍数参数 */ let initScale = 1, spacing = 0.1; /*监听滚轮放大缩小 */ c.addEventListener("wheel", (e) => { if (e.wheelDelta > 0) { initScale = add(initScale, spacing); console.log("放大", initScale); } else { initScale = add(initScale, -spacing); console.log("缩小", initScale); } dramImageByScale(initScale); initScale = 1; }); /*放大缩小 */ function dramImageByScale(scale) { multiple = ride(multiple, scale); console.log("放大倍数", multiple); ctx.scale(scale, scale); if (!isMousemove) { drawImageData(); } } let dataArr = []; getData(); function getData() { let num = 130; // let time = setInterval(() => { if (dataArr.length > 90) { // clearInterval(time); dataArr = []; num = 130; } dataArr.push({ x: num, y: 70, }); num -= 1; if (!isMousemove) { drawImageData(); } }, 100); } /*绘制数据 */ function drawImageData( dataX = x, dataY = y, dataWidth = width, dataHeight = height ) { /**地图 */ ctx.clearRect(0, 0, c.width, c.height); ctx.drawImage(img, dataX, dataY, dataWidth, dataHeight); /**位置信息数据 */ dataArr.forEach((e, i) => { /**点 */ // console.log(i); // ctx.beginPath(); // ctx.strokeStyle = "red"; // ctx.arc( // add(e.x, dataX), // add(e.y, dataY), // 2, // 0, // 2 * Math.PI // ); // ctx.stroke(); // ctx.fillStyle = "red"; // ctx.fill(); /**线 */ if (i == 0) { ctx.moveTo(add(e.x, dataX), add(e.y, dataY)); } else { ctx.lineTo(add(e.x, dataX), add(e.y, dataY)); } ctx.lineCap = "round"; ctx.strokeStyle = "red"; ctx.stroke(); if (i == dataArr.length - 1) { ctx.drawImage( seat, add(add(e.x, dataX), -5), add(add(e.y, dataY), -10), 10, 10 ); } }); ctx.beginPath(); } /*精度加减 */ function add(n1, n2) { var s1, s2, m; try { s1 = n1.toString().split(".")[1].length; } catch (e) { s1 = 0; } try { s2 = n2.toString().split(".")[1].length; } catch (e) { s2 = 0; } m = Math.pow(10, Math.max(s1, s2)); return (n1 * m + n2 * m) / m; } /*精度乘 */ function ride(arg1, arg2) { if (arg1 == null || arg2 == null) { return null; } var n1, n2; var r1, r2; // 小数位数 try { r1 = arg1.toString().split(".")[1].length; } catch (e) { r1 = 0; } try { r2 = arg2.toString().split(".")[1].length; } catch (e) { r2 = 0; } n1 = Number(arg1.toString().replace(".", "")); n2 = Number(arg2.toString().replace(".", "")); return (n1 * n2) / Math.pow(10, r1 + r2); } /*精度除 */ function except(arg1, arg2) { if (arg1 == null) { return null; } if (arg2 == null || arg2 == 0) { return null; } var n1, n2; var r1, r2; // 小数位数 try { r1 = arg1.toString().split(".")[1].length; } catch (e) { r1 = 0; } try { r2 = arg2.toString().split(".")[1].length; } catch (e) { r2 = 0; } n1 = Number(arg1.toString().replace(".", "")); n2 = Number(arg2.toString().replace(".", "")); return (n1 / n2) * Math.pow(10, r2 - r1); }
效果
3-2、svg
html
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" width="100%" height="100%" viewBox="0 0 1400 800" ref="mySvg" > <image :x="x" :y="y" :width="width + 'px'" :height="height + 'px'" xlink:href="./assets/003.jpg" preserveAspectRatio="none" ></image> <polyline :points="points" style="fill: white; stroke: red; stroke-width: 4" /> <image :x="px" :y="py" width="20px" height="20px" xlink:href="./assets/004.png" preserveAspectRatio="none" ></image> </svg>
vue data
... data() { return { x: 0, y: 0, px: 0, py: 0, width: 1200, height: 600, points: "", }; }, ...
js
const s = this.$refs.mySvg, _this = this, w = this.width, h = this.height; /*放大缩小 */ let initScale = 1, spacing = 0.1, multiple = 1; s.addEventListener("wheel", (e) => { if (e.wheelDelta < 0) { multiple = ride(multiple, add(initScale, -spacing)); _this.width = ride(w, multiple); _this.height = ride(h, multiple); } else { multiple = ride(multiple, add(initScale, spacing)); _this.width = ride(w, multiple); _this.height = ride(h, multiple); } initScale = 1; }); /*拖动 */ let beforeX = 0, beforeY = 0, afterX = 0, afterY = 0, chaX = add(beforeX, -afterX), chaY = add(beforeY, -afterY), isMousemove = false, originX = 0, originY = 0; s.addEventListener("mousedown", (e) => { beforeX = e.offsetX; beforeY = e.offsetY; originX = _this.x; originY = _this.y; isMousemove = true; }); s.addEventListener("mouseup", () => { isMousemove = false; originX = _this.x; originY = _this.y; }); s.addEventListener("mouseleave", () => { isMousemove = false; originX = _this.x; originY = _this.y; }); s.addEventListener("mousemove", (e) => { if (isMousemove) { afterX = e.offsetX; afterY = e.offsetY; chaX = add(beforeX, -afterX); chaY = add(beforeY, -afterY); setXY(add(originX, -chaX), add(originY, -chaY)); } }); let dataArr = []; getData(); function setXY(x = _this.x, y = _this.y) { _this.x = x; _this.y = y; getPoints(); } function getData() { let num = 1000; // let time = setInterval(() => { if (dataArr.length > 75) { // clearInterval(time); dataArr = []; num = 1000; } dataArr.push({ x: num, y: 285, }); num -= 10; getPoints(); }, 100); } function getPoints() { _this.points = ""; dataArr.forEach((e, i) => { _this.points += ` ${add( ride(e.x, multiple), _this.x )},${add(ride(e.y, multiple), _this.y)}`; /**设置地标位置 */ if (i == dataArr.length - 1) { _this.px = add(add(ride(e.x, multiple), _this.x), -10); _this.py = add(add(ride(e.y, multiple), _this.y), -20); } }); } /*精度加减 */ function add(n1, n2) { var s1, s2, m; try { s1 = n1.toString().split(".")[1].length; } catch (e) { s1 = 0; } try { s2 = n2.toString().split(".")[1].length; } catch (e) { s2 = 0; } m = Math.pow(10, Math.max(s1, s2)); return (n1 * m + n2 * m) / m; } /*精度乘 */ function ride(arg1, arg2) { if (arg1 == null || arg2 == null) { return null; } var n1, n2; var r1, r2; // 小数位数 try { r1 = arg1.toString().split(".")[1].length; } catch (e) { r1 = 0; } try { r2 = arg2.toString().split(".")[1].length; } catch (e) { r2 = 0; } n1 = Number(arg1.toString().replace(".", "")); n2 = Number(arg2.toString().replace(".", "")); return (n1 * n2) / Math.pow(10, r1 + r2); }
效果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」