百度地图上设置挖空效果的电子围栏

更新时间: 2024-04-15 09:24:01

公司项目有个需求是要在百度地图上设置电子围栏,电子围栏很简单嘛,就是一个覆盖物就能搞定了,然而UI又在搞事情,设计的效果图中电子围栏外卖填充颜色,电子围栏内不填充颜色。
最后我还是写出了这个效果,浅浅的复盘一下:
狗狗太可爱了给他用电子围栏描个边边

我是怎么做到的呢,我使用的还是百度地图的CanvasLayer:

然后利用canvas绘图的一个特性:
绘制路径的时候,如果两个路径的方向不一致,就会出现挖空的效果,所以就让外面罩住屏幕的大框是顺时针,电子围栏的路径是逆时针。

至于怎么判断是顺时针还是逆时针,写在另一篇文章里 判断路径是顺时针还是逆时针

# 实现

实现大致就这样几个步骤:

  1. 以顺时针定义外部矩形的路径,我使用了zrender作为绘图库
  2. 计算电子围栏的路径是顺时针还是逆时针,如果是顺时针就将路径的点翻转一下成为逆时针拼接在矩形路径后

需要注意的是,我们在计算路径是否顺时针的时候,是用经纬度去计算,然后再用百度地图的api将经纬度转换为实际屏幕像素位置拼接在矩形路径后面。

zr = null

var canvasLayer = new BMap.CanvasLayer({
    update: function () {
        if (!zr) {
            zr = zrender.init(this.canvas); //初始化zrender
        }
        const canvasWidth = zr.getWidth()
        const canvasHeight = zr.getHeight()
        zr.clear() //先清空之前画的
        zr.resize()

        // 这是外部大框的路径,这个大框实际尺寸超出canvas
        let pathString = `M -10,-10 L ${canvasWidth + 10},-10 L ${canvasWidth + 10} ${canvasHeight + 10} L -10 ${canvasHeight + 10} Z `
        let pixiPos = [[117.74870031429617,38.93620960719094],[117.75249116354829,38.934132767812294],[117.74976031479795,38.929557891809644],[117.74505319392566,38.93087705835964]]

        let reversepixiPos = []

        // 将原始路径改为顺时针
        if (!isClockwise(pixiPos)) {
            pixiPos = pixiPos.reverse()
        }

        // 将经纬度转换为像素
        pixiPos = pixiPos.map(item => {
            const {x, y} = map.pointToPixel(new BMap.Point(...item))
            return [x, y]
        })

        // 翻转路径为逆时针
        reversepixiPos = pixiPos.reverse()

        // 拼接路径
        for (let i = 0; i < reversepixiPos.length; i++) {
            if (i === 0) {
                pathString += `M ${reversepixiPos[i][0]} ${pixiPos[i][1]} `
            } else {
                pathString += `L ${reversepixiPos[i][0]} ${pixiPos[i][1]} `
            }
            if (i === reversepixiPos.length - 1) {
                pathString += 'Z'
            }
            }
        }

        // 判断路径是顺时针还是逆时针,逆时针返回正,顺时针返回负
        function isClockwise(points) {
            var maxXIndex = 0;
            var maxX = points[maxXIndex][0];
            var c1,c2,c3;
            var x1,y1,x2,y2,x3,y3;
            for(var i=0;i<points.length;i++){
            if(points[i][0]>maxX){
                maxX = points[i][0]
                maxXIndex = i;
            }
            }
            if(maxXIndex==0){
                c1 = points[points.length-1];
                c2 = points[maxXIndex];
                c3 = points[maxXIndex+1];
            }else if(maxXIndex==points.length-1){
                c1 = points[maxXIndex-1];
                c2 = points[maxXIndex];
                c3 = points[0];
            }else {
                c1 = points[maxXIndex-1];
                c2 = points[maxXIndex];
                c3 = points[maxXIndex+1];
            }
            x1 = c1[0];
            y1 = c1[1];
            x2 = c2[0];
            y2 = c2[1];
            x3 = c3[0];
            y3 = c3[1];
            var s = ((x1-x3)*(y2-y3)-(x2-x3)*(y1-y3));
            return s<0;
        }

        // 使用zrender绘制路径
        var mask = zrender.path.createFromString(pathString,{
            style: {
                fill: 'rgba(0,0,0,0.5)',
                stroke: '#FF5959',
                lineWidth:6,
                lineDash:12
            }
        })

        zr.add(mask)
    },
    // 定义图层在上层
    paneName: 'floatPane'
});

map.addOverlay(canvasLayer);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97