记一次canvas绘制地图

1,906 阅读1分钟

最近在学线性代数想树立几何直观于是试试踩地图的坑:
有些边界线没有绘制,请勿上纲上线。
数据来源于阿里的datav, 请自行下载,link.js内部为向量数据参考当前各式即可。

image.png 支持鼠标滚轮缩放,拖拽移动(请忽略小问题,位移拖拽bug,如有解决方法请在下方留言)。

image.png 废话少说直接上代码。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        html,body{
            width: 100%;
            height: 100%;
            padding: 0;
            margin: 0;
        }
    </style>
    <script src="../rxjs.umd.js"></script>
    <script src="link.js"></script>
    <script>
        let dip = 3;
        let startX = 0;
        let startY = 0;
        let endX = 0;
        let endY = 0;
        function draw() {
            const canvas = document.getElementById('canvas');
            let windowGlobalSize = getWindowSize();
            canvas.width = windowGlobalSize.width;
            canvas.height = windowGlobalSize.height;
            setTimeout(() => {
                setRect(canvas);
            }, 1000);

            window.addEventListener('resize', () => {
                setTimeout(() => {
                    let windowGlobalSize = getWindowSize();
                    canvas.width = windowGlobalSize.width;
                    canvas.height = windowGlobalSize.height;
                    setRect(canvas);
                }, 1000);
            })
            window.addEventListener('mousewheel', (event) => {
                if (event.wheelDelta > 0) {
                    dip++;
                    let windowGlobalSize = getWindowSize();
                    canvas.width = windowGlobalSize.width;
                    canvas.height = windowGlobalSize.height;
                    setRect(canvas);
                } else {
                    if (dip > 1) {
                        dip--;
                    }
                    let windowGlobalSize = getWindowSize();
                    canvas.width = windowGlobalSize.width;
                    canvas.height = windowGlobalSize.height;
                    setRect(canvas);
                }
            });

            const mouseDown = rxjs.fromEvent(canvas, "mousedown");
            const mouseUp = rxjs.fromEvent(document, "mouseup");
            const mouseMove = rxjs.fromEvent(document, "mousemove");

            let isMove = false;
            mouseDown
                .pipe(
                    rxjs.map((e) => {
                        return {
                            x: e.pageX,
                            y: e.pageY
                        };
                    })
                ).subscribe(e => {
                    if (!isMove) {
                        startX = e.x;
                        startY = e.y;
                        isMove = true;
                    }
            })

            mouseUp
                .pipe(
                    rxjs.map((e) => {
                        return {
                            x: e.pageX,
                            y: e.pageY
                        };
                    })
                ).subscribe(e => {
                startX = e.x - endX;
                startY = e.y - endY;
            })

            mouseDown
                .pipe(
                    rxjs.switchMap(e => {
                        return mouseMove.pipe(rxjs.takeUntil(mouseUp))
                    }),
                    rxjs.map((e) => {
                        return {
                            x: e.pageX,
                            y: e.pageY
                        };
                    })
                )
                .subscribe(e => {
                    endY = e.y - startY
                    endX = e.x - startX
                    let windowGlobalSize = getWindowSize();
                    canvas.width = windowGlobalSize.width;
                    canvas.height = windowGlobalSize.height;
                    setRect(canvas);
                })
        }

        function setRect(canvas) {

            // 当前画布宽度
            const width = canvas.width;
            // 当前画布高度
            const height = canvas.height;
            // 当前X轴原点处于Y轴位置
            const x = height / 2;
            // 当前Y轴原点处于X轴位置
            const y = width / 2;
            const ctx = canvas.getContext('2d');
            ctx.fillStyle = 'rgba(0,0,0,.3)'
            ctx.fillRect(0, 0, width, height);
            setColY(ctx, x, width, height);
            setRowX(ctx, y, width, height);
            setXY(ctx, x, y, width, height);
            const arrX = []
            const arrY = []
            map.features.forEach(item => {
                let coordinates = item.geometry.coordinates;
                coordinates.forEach(data => {
                    data.forEach(data1 => {
                        if (data1.length > 2) {
                            data1.forEach((data2, i) => {
                                arrX.push(data2[0]);
                                arrY.push(data2[1]);
                            });
                        }
                    });
                });
            });
            const left = Math.min(...arrX);
            const right = Math.max(...arrX);
            const top = Math.min(...arrY);
            const bottom = Math.max(...arrY);
            let movex = (y - ((right + left) / 2));
            let moveY = (x - ((top + bottom) / 2));
            map.features.forEach(item => {
                let coordinates = item.geometry.coordinates;
                coordinates.forEach(data => {
                    data.forEach(data1 => {
                        if (data1.length > 2) {
                            ctx.beginPath();
                            data1.forEach((data2, i) => {
                                let mapX = data2[0] + movex;
                                let mapY = data2[1] + moveY;
                                const valX = mapX - y;
                                const valY = mapY - x;
                                if (valX) {
                                    mapX += (valX * dip)
                                } else {
                                    mapX -= (valX * dip)
                                }
                                if (valY) {
                                    mapY += (valY * dip)
                                } else {
                                    mapY -= (valY * dip)
                                }
                                if (mapY < x) {
                                    mapY = x + (x - mapY)
                                } else {
                                    mapY = x - (mapY - x)
                                }
                                if (i === 0) {
                                    ctx.moveTo(mapX + endX, mapY + endY);
                                }
                                ctx.lineTo(mapX + endX, mapY + endY);
                            });
                            ctx.stroke();
                        }
                    });
                });
            });

        }



        // 绘制X轴刻度线
        function setColY(ctx, x, w, h) {
            /**
             * 当前从那个位置开始画Y轴刻度线
             * @param colYPositive {number} 向正半轴开始绘制
             * @param colYMinus {number} 向负半轴开始绘制
             */
            const obj = {
                colYPositive: x,
                colYMinus: x
            };
            ctx.strokeStyle = 'rgb(213,208,208)'
            ctx.beginPath();
            while (obj.colYPositive > 0) {
                obj.colYPositive = obj.colYPositive - 30;

                ctx.moveTo(0, obj.colYPositive);
                ctx.lineTo(0, obj.colYPositive);
                ctx.lineTo(w, obj.colYPositive);
            }
            while (obj.colYMinus <= h) {
                obj.colYMinus = obj.colYMinus + 30;

                ctx.moveTo(0, obj.colYMinus);
                ctx.lineTo(0, obj.colYMinus);
                ctx.lineTo(w, obj.colYMinus);
            }
            ctx.stroke();

        }

        // 绘制X轴单元格
        function setRowX(ctx, y, w, h) {
            // 当前从那个位置开始画Y轴刻度线
            /**
             * 当前从那个位置开始画Y轴刻度线
             * @param colYPositive {number} 向正半轴开始绘制
             * @param colYMinus {number} 向负半轴开始绘制
             */
            const obj = {
                rowXPositive: y,
                rowXMinus: y
            };
            ctx.strokeStyle = 'rgb(213,208,208)'
            ctx.beginPath();
            while (obj.rowXPositive > 0) {
                obj.rowXPositive = obj.rowXPositive - 30;

                ctx.moveTo(obj.rowXPositive, 0);
                ctx.lineTo(obj.rowXPositive, 0);
                ctx.lineTo(obj.rowXPositive, h);
            }
            while (obj.rowXMinus <= w) {
                obj.rowXMinus = obj.rowXMinus + 30;

                ctx.moveTo(obj.rowXMinus, 0);
                ctx.lineTo(obj.rowXMinus, 0);
                ctx.lineTo(obj.rowXMinus, h);
            }
            ctx.stroke();
        }

        function setXY(ctx, x, y, w, h) {
            ctx.strokeStyle = 'rgb(0,0,0)'
            ctx.beginPath();
            ctx.moveTo(0, x);
            ctx.lineTo(0, x);
            ctx.lineTo(w, x);
            ctx.stroke();
            ctx.strokeStyle = 'rgb(0,0,0)'
            ctx.beginPath();
            ctx.moveTo(y, 0);
            ctx.lineTo(y, 0);
            ctx.lineTo(y, h);
            ctx.stroke();
        }

        function getWindowSize() {
            const width = document.documentElement.clientWidth;
            const height = document.documentElement.clientHeight;
            return {
                width,
                height
            }
        }
    </script>
</head>

<body onload="draw()">
<canvas id="canvas" width="150" height="150"></canvas>
</body>
</html>