You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Leaflet平移/缩放后Canvas绘制内容消失的技术问题

解决React中Leaflet+D3 Canvas路径在地图平移/缩放后消失的问题

问题分析

你的代码在地图初始化时能正常渲染GeoJSON路径,但平移或缩放后Canvas内容消失,核心原因是:

  • Leaflet的Canvas图层在地图视图变更(平移、缩放结束)时会自动清空画布
  • 原代码仅在地图首次就绪时执行一次绘制,没有监听视图变化事件触发重绘

解决方案

通过以下步骤修复:

  • 提取绘制逻辑为独立函数,便于重复调用
  • 监听Leaflet的moveend事件(平移/缩放操作结束时触发),触发重绘
  • 优化DOM元素获取方式,避免重复选择
  • 添加组件卸载时的事件清理,防止内存泄漏

修改后的完整代码

import React, { useEffect, useState } from 'react'
import 'leaflet/dist/leaflet.css'
import * as d3 from 'd3'
var L = null

const source = require('./data/states-us.geojson')

const AppCanvas = () => {
    const [map, setMap] = useState(null)
    const [geoData, setGeoData] = useState(null)
    const [canvasContext, setCanvasContext] = useState(null)
    const [pathGenerator, setPathGenerator] = useState(null)

    // 提取绘制逻辑为独立函数
    const drawGeoData = () => {
        if (!canvasContext || !pathGenerator || !geoData) return
        
        // 清空画布后重绘
        canvasContext.clearRect(0, 0, canvasContext.canvas.width, canvasContext.canvas.height)
        canvasContext.beginPath()
        pathGenerator(geoData)
        canvasContext.stroke()
    }

    useEffect(() => {
        if (!map) {
            L = require('leaflet')
            const origin = [37.8, -96.9]
            const initialZoom = 4

            // 创建地图实例
            const leafletMap = L.map('map')
                .setView(origin, initialZoom)
                .addLayer(
                    new L.TileLayer('http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png')
                )

            leafletMap.whenReady(() => {
                // 创建Leaflet Canvas图层并添加到地图
                const canvasLayer = L.canvas().addTo(leafletMap)
                const canvas = canvasLayer.getElement()
                const context = canvas.getContext('2d')
                setCanvasContext(context)

                // 定义D3投影(绑定Leaflet的坐标转换)
                const projection = d3.geoTransform({
                    point: function (x, y) {
                        const point = leafletMap.latLngToLayerPoint(new L.LatLng(y, x))
                        this.stream.point(point.x, point.y)
                    },
                })

                // 创建D3路径生成器
                const path = d3.geoPath().projection(projection).context(context)
                setPathGenerator(path)

                // 加载GeoJSON数据
                d3.json(source).then((data) => {
                    setGeoData(data)
                })

                setMap(leafletMap)
            })
        }

        // 监听地图视图变化事件,触发重绘
        if (map) {
            map.on('moveend', drawGeoData)
        }

        // 组件卸载时移除事件监听,清理资源
        return () => {
            if (map) {
                map.off('moveend', drawGeoData)
            }
        }
    }, [map, geoData, canvasContext, pathGenerator])

    // 当数据或路径生成器就绪时执行初始绘制
    useEffect(() => {
        drawGeoData()
    }, [geoData, canvasContext, pathGenerator])

    return (
        <div>
            <div
                style={{
                    position: 'absolute',
                    top: 0,
                    bottom: 0,
                    left: 0,
                    right: 0,
                    zIndex: 0,
                }}
                id="map"
            ></div>
        </div>
    )
}

export default AppCanvas

关键修改说明

  1. 独立绘制函数:drawGeoData负责清空画布并重新绘制路径,确保每次视图变化后都能重新渲染
  2. 事件监听:通过map.on('moveend', drawGeoData)监听平移/缩放结束事件,触发重绘
  3. 状态管理:将地图实例、GeoJSON数据、Canvas上下文、路径生成器存入React状态,确保依赖变化时触发更新
  4. 资源清理:在useEffect的返回函数中移除moveend事件监听,避免组件卸载后内存泄漏
  5. Canvas获取优化:直接从Leaflet的Canvas图层实例获取元素,比通过d3选择DOM更可靠

内容的提问来源于stack exchange,提问作者Alexandre Palo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 05:55:31