canvas地图画线

856 阅读2分钟

前言

一直没有深入学习过canvas,近期接手一个地图网格的优化需求,优化前是用百度地图的鼠标绘制管理类BMapLib.DrawingManager实现鼠标绘制管理的入口,并通过实例化该类,调用open方法开启绘制状态进行绘制。但是画线交互方式是通过鼠标点击,不太流畅。因此希望通过canvas在地图上进行鼠标点击划动画线。

百度地图鼠标绘制管理类--BMapLib.DrawingManager

  1. 资源
    <!-- 百度地图 -->
    <script type="text/javascript" src="//api.map.baidu.com/api?v=3.0&ak=osOdXZRHUHOC2yMC4YB3InaCxzGVECy4"></script>
    <!-- 热力图 -->
    <script type="text/javascript" src="//api.map.baidu.com/library/Heatmap/2.0/src/Heatmap_min.js"></script>
    <!-- 鼠标绘制 -->
    <script type="text/javascript" src="//api.map.baidu.com/library/DrawingManager/1.4/src/DrawingManager_min.js"></script>
    <!-- 工具类文档: http://api.map.baidu.com/library/GeoUtils/1.2/docs/symbols/BMapLib.GeoUtils.html -->
    <script type="text/javascript" src="//api.map.baidu.com/library/GeoUtils/1.2/src/GeoUtils_min.js"></script>
    <link rel="stylesheet" href="//api.map.baidu.com/library/DrawingManager/1.4/src/DrawingManager_min.css" />
    <script>
        window.BMap = BMap;
        window.BMapLib = BMapLib;
    </script>

封装canvas画线基类

通过canvas方法画线并将线路经的坐标传给后端

挂载dom: <canvas className="map-draw-canvas" id="mapDrawCanvas"></canvas>

class MapCanvas {
    protected mapDrawCanvas = null;
    protected ctx = null;
    protected width = null;
    protected height = null;
    protected draw = false;
    protected points = [];
    protected map = null;
    constructor(props: any) {
        this.width = props.width;
        this.height = props.height;
        this.map = props.map;
        this.mapDrawCanvas = document.getElementById('mapDrawCanvas');
        this.mapDrawCanvas.width = this.width;
        this.mapDrawCanvas.height = this.height;
        this.mapDrawCanvas.style.display = 'block';
        this.ctx = this.mapDrawCanvas.getContext('2d');
    }
    private randomWithSet(length, data: Array<any>) {
        const set = new Set();
        const MAXIMUM = Math.min(length, data.length);
        const UID_COUNT = data.length;
        // eslint-disable-next-line no-constant-condition
        while (true) {
            if (set.size > MAXIMUM - 1) break;
            // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
            // @ts-ignore
            const tmp = parseInt(Math.random() * UID_COUNT, 10);
            if (set.has(tmp)) continue;
            set.add(tmp);
        }

        return Array.from(set)
            .sort(function(a: any, b: any) {
                return a - b;
            })
            .map((i: number) => data[i]);
    }
    public startDraw() {
        // 画布添加滚轮事件,放缩地图
        this.mapDrawCanvas.onmousewheel = this.scrollFunc.bind(this);
        const gridMap = document.getElementById('grid-map');
        const y = gridMap.getBoundingClientRect().top;
        this.mapDrawCanvas.onmousedown = e => {
            this.ctx.beginPath();
            this.ctx.strokeStyle = '#ff0000';
            this.ctx.beginPath();
            this.ctx.lineWidth = 1;
            this.ctx.moveTo(e.clientX + 8, e.clientY - y + 28);
            this.mapDrawCanvas.onmousemove = e => {
                // console.log(e.clientX, e.clientY);
                this.ctx.lineTo(e.clientX + 8, e.clientY - y + 28);
                this.ctx.stroke();
                this.points.push({ x: e.clientX + 8, y: e.clientY - y + 28 });
            };
            this.mapDrawCanvas.onmouseup = e => {
                this.mapDrawCanvas.onmouseup = null;
                this.mapDrawCanvas.onmousemove = null;
                this.mapDrawCanvas.onmousedown = null;
                this.draw = false;
                this.mapDrawCanvas.style.display = 'none';
                this.endDraw();
            };
        };
    }
    public endDraw() {
        if (this.points.length > 5) {
            this.drawLinePoints();
        }
        this.mapDrawCanvas.style.display = 'none';
        // 移除去鼠标滚动事件
        this.mapDrawCanvas.onmousewheel = null;
    }
    public clearCanvas() {
        this.ctx.clearRect(0, 0, this.width, this.height);
        this.points = [];
    }
    public drawLinePoints() {
        const { BMap, BMapLib } = window as any;
        if (this.map) {
            const a = this.randomWithSet(10, this.points).map(point => {
                // eslint-disable-next-line no-undef
                return this.map.pixelToPoint(point);
            });
            console.log(a);
            const bc = new BMap.Polyline(a, {
                strokeColor: '#ff0000',
                strokeWeight: 2,
                strokeOpacity: 1
            });
            bc.enableEditing();
            this.map.addOverlay(bc);
        }
    }
    public getPoints() {
        return this.points;
    }
    public showCanvas(width, height) {
        this.mapDrawCanvas.style.display = 'block';
        this.mapDrawCanvas.width = width;
        this.mapDrawCanvas.height = height;
    }
    // 鼠标滚轮事件
    private scrollFunc(e) {
        const { wheelDelta } = e;
        if (wheelDelta > 0) {
            this.map.zoomIn();
        } else {
            this.map.zoomOut();
        }
    }
    public hideCanvas() {
        this.mapDrawCanvas.style.display = 'none';
        this.mapDrawCanvas.onmousewheel = null;
    }
}
export default MapCanvas;

如何使用

// 打开画笔,开始绘制
const { width, height } = this._map;
if (!this.mapCanvas) {
    this.mapCanvas = new MapCanvas({ map: this._map, width, height });
    const ployline = getDrawingPolyline(this._map);
    if (ployline) {
        ployline.remove();
    }
} else {
    this.mapCanvas.showCanvas(width, height);
}
this.mapCanvas.startDraw();

思考

地图遮罩层并高亮选中区域,如何做?

问题

  1. canvas开启画线模式,不能缩放 解决
  • 样式穿透

参考文献

[1] api.map.baidu.com/library/Dra… [2]