如何用L.canvas在Leaflet中绘制波动扩散的动态圆形
实现Leaflet动态水波扩散圆形(基于L.canvas)
下面直接提供可运行的示例代码,并拆解关键逻辑:
完整可运行示例
<!DOCTYPE html> <html> <head> <title>Leaflet Water Wave Animation</title> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <style> #map { height: 600px; width: 100%; } </style> </head> <body> <div id="map"></div> <script> // 初始化地图 const map = L.map('map').setView([39.9042, 116.4074], 13); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); // 自定义Canvas图层实现水波动画 class WaveLayer extends L.Canvas { constructor(options) { super(options); this.waves = []; // 存储所有活跃水波的状态 } // 添加新水波:参数为圆心经纬度、初始半径、最大半径、动画持续时间 addWave(latlng, startRadius = 0, maxRadius = 200, duration = 3000) { const wave = { latlng: latlng, startTime: Date.now(), startRadius, maxRadius, duration }; this.waves.push(wave); this.redraw(); // 触发首次重绘 } // 核心重绘逻辑 _drawLayer() { if (this.waves.length === 0) return; const ctx = this._ctx; const now = Date.now(); // 清空画布准备重绘 ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); // 遍历并更新所有水波,过滤掉已结束的波 this.waves = this.waves.filter(wave => { const elapsed = now - wave.startTime; if (elapsed > wave.duration) return false; // 计算当前进度:0到1的数值 const progress = elapsed / wave.duration; // 半径随进度从初始值增长到最大值 const currentRadius = wave.startRadius + (wave.maxRadius - wave.startRadius) * progress; // 透明度随进度从1衰减到0 const opacity = 1 - progress; // 把经纬度转成Canvas画布上的像素坐标 const point = this._map.latLngToContainerPoint(wave.latlng); // 绘制水波圆形 ctx.beginPath(); ctx.arc(point.x, point.y, currentRadius, 0, Math.PI * 2); ctx.fillStyle = `rgba(0, 150, 255, ${opacity})`; ctx.fill(); ctx.strokeStyle = `rgba(0, 150, 255, ${opacity})`; ctx.lineWidth = 2; ctx.stroke(); return true; // 保留未结束的波 }); // 请求下一帧动画,形成连续效果 requestAnimationFrame(() => this.redraw()); } } // 创建图层并添加到地图 const waveLayer = new WaveLayer(); waveLayer.addTo(map); // 示例:每2秒在地图中心生成一个新水波(可自行替换为指定经纬度) setInterval(() => { waveLayer.addWave(map.getCenter(), 0, 300, 4000); }, 2000); </script> </body> </html>
关键逻辑说明
- 自定义Canvas图层:继承Leaflet的
L.Canvas类,重写_drawLayer实现自定义绘制逻辑,这是Leaflet中用Canvas做自定义可视化的标准方式。 - 帧动画驱动:用
requestAnimationFrame替代setInterval,保证动画与浏览器刷新帧率同步,避免卡顿。 - 状态管理:用数组存储所有活跃水波的时间、半径、坐标等状态,每帧更新后自动移除已结束的波,避免内存浪费。
- 坐标转换:通过
latLngToContainerPoint把经纬度转换成画布像素坐标,确保水波精准落在地图指定位置。
实用优化建议
- 自定义样式:扩展
addWave方法,支持传入颜色、线宽、填充/描边开关等参数,适配不同视觉需求。 - 范围控制:如果需要限制水波在指定区域内显示,可以在添加水波前判断圆心是否在目标范围内,或者用Canvas的
clip()方法裁剪绘制区域。 - 性能控制:同时生成大量水波时,限制最大活跃波数量;地图缩放/拖拽时暂时暂停动画,减少不必要的计算。
内容的提问来源于stack exchange,提问作者Redamancy-qian
相关产品推荐
相关产品推荐

