【canvas】canvas绘制园区地图

4,139 阅读3分钟

背景

业务需求,园区无人车移动端需要加载地图,个人当时给了4个方案

  • 复用我司现有的地图(但是是找第三方开发的,且没有源码,无语)
  • 三维地图的方案,俯视视角,babylonJs(这个之前web端做过,移动端比较重,且web端只有车道线,building之类都没有,且页面端需要就是一个二维平面就可以了,没必要三维)
  • 二维地图,直接加载图片。(这个方法最方便,但是需要涉及多重坐标的转换)
  • 二维地图,根据地图信息,一笔一划都自己绘制。(想想这个应该挺麻烦的,但是自己绘制灵活性大一些)

ps: 因为是园区地图,所以没法用百度、高德等API。

技术选择

除了剔除1,其他三个都可以考虑。
babylonjs的方案之前做过,只要固定俯视视角,性能未测,building因为没有数据,未画
目前在调研canvas加载地图图片的需求。

基本要求

  • 地图缩放+移动
  • 图标位于地图中间,表示起点(同打车APP,移动的是地图,而不是起点)
  • 坐标转换(canvas坐标、Img坐标、空间坐标)

其中坐标Img坐标和空间坐标的对应关系是服务端告知的。前端需要处理canvas坐标和Img坐标。

代码实现

html

画个canvas就行


	<div style="width:800px;height:800px;border: red solid 1px">
		<canvas id="canvas" width="800" height="800"></canvas>
	</div>

js

先看一个canvas的核心函数

context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
参数描述
img规定要使用的图像、画布或视频。
sx可选。开始剪切的 x 坐标位置。
sy可选。开始剪切的 y 坐标位置。
swidth可选。被剪切图像的宽度。
sheight可选。被剪切图像的高度。
x在画布上放置图像的 x 坐标位置。
y在画布上放置图像的 y 坐标位置。
width可选。要使用的图像的宽度。(伸展或缩小图像)
height可选。要使用的图像的高度。(伸展或缩小图像)

简单的说,参数2-5是展示的地图信息,参数6-9是窗口展示信息。
展示的地图信息肯定要是全的,x,y是移动后的左上角,width/height需要考虑scale


context.drawImage(img, 0, 0, img.width, img.height, imgX, imgY, img.width * imgScale, img.height * imgScale);

那就很好理解了,只要计算移动、缩放后的imgX, imgY, imgScale就行。

移动


canvas.onmousedown = function (event) {
    var pos = windowToCanvas(canvas, event.clientX, event.clientY);
    let xx = (event.clientX - imgX) / imgScale
    let yy = (event.clientY - imgY) / imgScale
    console.log('pos in img:', xx, yy) // 针对的是img.width, img.height,
    canvas.onmousemove = function (event) {
        canvas.style.cursor = "move";
        var pos1 = windowToCanvas(canvas, event.clientX, event.clientY);
        var x = pos1.x - pos.x;
        var y = pos1.y - pos.y;
        pos = pos1;
        imgX += x;
        imgY += y;
        drawImage();
    }
    canvas.onmouseup = function () {
        canvas.onmousemove = null;
        canvas.onmouseup = null;
        canvas.style.cursor = "default";
    }
}

缩放


var flag = 1
canvas.onmousewheel = canvas.onwheel = function (event) {
    var pos = windowToCanvas(canvas, event.clientX, event.clientY);
    event.wheelDelta = event.wheelDelta ? event.wheelDelta : (event.deltaY * (-40));
    if (flag) {
        flag = 0
        if (event.wheelDelta > 0) {
            imgScale *= 1.2;
            // imgX = imgX * 1.2 - pos.x;
            // imgY = imgY * 1.2 - pos.y;
        } else {
            imgScale *= 0.8;
            // imgX = imgX * 0.8 + pos.x * 0.8;
            // imgY = imgY * 0.8 + pos.y * 0.8;
        }
        drawImage();
        setTimeout(() => {
            flag = 1
        }, 500)
    }
    event.preventDefault(); // 禁止缩放
}

细节

在缩放的时候,浏览器会随着缩放。即不仅仅是canvas内Img的缩放。所以时候需要加上event.preventDefault();阻止浏览器默认事件。

坐标转换

canvas坐标转换成img坐标。
假设canvas坐标为(x,y), 不一定是用户点击,比如我们的小绿点(表示当前位置,它是不动的)
这个时候去拿到img左上角在canvas中的坐标是(imgX,imgY),对应的img坐标是(0,0)


let iX = (x - imgX) / imgScale
let iY = (y - imgY) / imgScale
console.log('img axis:', iX, iY)

image.png

这样子基本满足需求了
然后发现到移动端没法用
把mouse相关事件改成touch事件就可以了。
明天更新一下,源码一起放上来