学习目标掌握添加动画图标的实现方法理解相关API的使用能够独立完成类似功能开发 核心概念向地图添加动画图标。 完 整 代 码代码示例constmapnewmaplibregl.Map({container:map,style:https://demotiles.maplibre.org/style.json,});constsize200;constpulsingDot{width:size,height:size,data:newUint8Array(size*size*4),onAdd(){constcanvasdocument.createElement(canvas);canvas.widththis.width;canvas.heightthis.height;this.contextcanvas.getContext(2d);},render(){constduration1000;constt(performance.now()%duration)/duration;constradius(size/2)*0.3;constouterRadius(size/2)*0.7*tradius;constcontextthis.context;context.clearRect(0,0,this.width,this.height);context.beginPath();context.arc(this.width/2,this.height/2,outerRadius,0,Math.PI*2);context.fillStylergba(255, 200, 200,${1-t});context.fill();context.beginPath();context.arc(this.width/2,this.height/2,radius,0,Math.PI*2);context.fillStylergba(255, 100, 100, 1);context.strokeStylewhite;context.lineWidth24*(1-t);context.fill();context.stroke();this.datacontext.getImageData(0,0,this.width,this.height).data;map.triggerRepaint();returntrue;},};map.on(load,(){map.addImage(pulsing-dot,pulsingDot,{pixelRatio:2});map.addSource(points,{type:geojson,data:{type:FeatureCollection,features:[{type:Feature,geometry:{type:Point,coordinates:[0,0]}}],},});map.addLayer({id:points,type:symbol,source:points,layout:{icon-image:pulsing-dot},});});代码示例!DOCTYPEhtmlhtmllangzh-CNheadtitle向地图添动态图标/titlemetapropertyog:descriptioncontent向地图添加使用 Canvas API 在运行时生成的动画图标。/metapropertyog:createdcontent2025-06-25/metacharsetutf-8/metanameviewportcontentwidthdevice-width, initial-scale1/linkrelstylesheethrefhttps://unpkg.com/maplibre-gl5.24.0/dist/maplibre-gl.css/scriptsrchttps://unpkg.com/maplibre-gl5.24.0/dist/maplibre-gl.js/scriptstylebody{margin:0;padding:0;}html, body, #map{height:100%;}/style/headbodydividmap/divscriptconstmapnewmaplibregl.Map({container:map,style:https://demotiles.maplibre.org/style.json,});constsize200;constpulsingDot{width:size,height:size,data:newUint8Array(size*size*4),onAdd(){constcanvasdocument.createElement(canvas);canvas.widththis.width;canvas.heightthis.height;this.contextcanvas.getContext(2d);},render(){constduration1000;constt(performance.now()%duration)/duration;constradius(size/2)*0.3;constouterRadius(size/2)*0.7*tradius;constcontextthis.context;context.clearRect(0,0,this.width,this.height);context.beginPath();context.arc(this.width/2,this.height/2,outerRadius,0,Math.PI*2);context.fillStylergba(255, 200, 200,${1-t});context.fill();context.beginPath();context.arc(this.width/2,this.height/2,radius,0,Math.PI*2);context.fillStylergba(255, 100, 100, 1);context.strokeStylewhite;context.lineWidth24*(1-t);context.fill();context.stroke();this.datacontext.getImageData(0,0,this.width,this.height).data;map.triggerRepaint();returntrue;},};map.on(load,(){map.addImage(pulsing-dot,pulsingDot,{pixelRatio:2});map.addSource(points,{type:geojson,data:{type:FeatureCollection,features:[{type:Feature,geometry:{type:Point,coordinates:[0,0]}}],},});map.addLayer({id:points,type:symbol,source:points,layout:{icon-image:pulsing-dot},});});/script/body/html 代码解析初始化地图使用new maplibregl.Map()创建地图实例配置基本参数。本示例的核心特色是展示如何使用StyleImageInterface接口创建动态动画图标。关键配置项container: 地图容器的 DOM 元素 IDstyle: 使用 MapLibre 官方样式https://demotiles.maplibre.org/style.jsonStyleImageInterface 接口实现constpulsingDot{width:size,height:size,data:newUint8Array(size*size*4),onAdd(){constcanvasdocument.createElement(canvas);canvas.widththis.width;canvas.heightthis.height;this.contextcanvas.getContext(2d);},render(){constduration1000;constt(performance.now()%duration)/duration;// 绘制外圈脉冲效果constradius(size/2)*0.3;constouterRadius(size/2)*0.7*tradius;// 更新图像数据并触发重绘this.datacontext.getImageData(0,0,this.width,this.height).data;map.triggerRepaint();returntrue;},};添加动画图标到地图map.on(load,(){map.addImage(pulsing-dot,pulsingDot,{pixelRatio:2});map.addSource(points,{type:geojson,data:{type:FeatureCollection,features:[...]}});map.addLayer({id:points,type:symbol,source:points,layout:{icon-image:pulsing-dot}});});⚙️ 参数说明参数类型必填默认值说明containerstring是-地图容器元素的 IDstylestring/object是-地图样式 URL 或内联样式对象StyleImageInterface 属性属性类型必填说明widthnumber是图像宽度像素heightnumber是图像高度像素dataUint8Array是像素数据RGBA 格式onAddfunction是图层添加时调用初始化 Canvasrenderfunction是每帧调用返回 true 表示图像已更新 效果说明运行代码后地图上会在坐标[0, 0]处显示一个脉冲动画图标内圈: 固定大小的红色圆点带白色描边外圈: 脉冲扩散效果从中心向外逐渐扩大并淡出动画周期: 1 秒完成一次脉冲循环交互功能: 支持鼠标拖拽、滚轮缩放等标准交互动画原理:render()方法每帧被调用使用performance.now()计算动画进度动态计算外圈半径和透明度通过map.triggerRepaint()触发地图重绘 常 见 问 题Q1: StyleImageInterface 是什么A:这是一个接口允许开发者创建动态生成的图像。通过实现onAdd()和render()方法可以在运行时生成动画图标。Q2: 为什么需要返回 trueA:render()方法返回true告诉地图图像已更新需要重新渲染。返回false则跳过重绘。Q3: 性能影响如何A:每帧都会调用render()和triggerRepaint()对于复杂动画可能影响性能。建议优化渲染逻辑或降低动画帧率。Q4: 可以创建多个动画图标吗A:可以。为每个动画图标定义不同的 ID或者使用相同的图像对象创建多个图层。 练习任务基础练习修改动画周期和颜色创建不同的脉冲效果进阶挑战实现多个不同位置的脉冲图标每个有不同的动画周期拓展思考如何实现图标沿路径移动的动画 最佳实践性能优化: 避免在render()中进行复杂计算考虑预计算或缓存内存管理: 对于临时图像使用后及时清理像素比例: 使用pixelRatio参数适配高分辨率屏幕动画控制: 提供启动/停止动画的机制测试验证: 在不同设备上测试动画性能降级方案: 为不支持 Canvas 的环境提供备用方案 延伸阅读Map API文档MapLibre GL JS 官方文档[下一课预告]将继续学习地图图层的基础知识本文是MapLibre GL JS实践课程系列的一部分欢迎关注收藏