news 2026/6/13 14:44:53

MapLibre GL JS第47课:添加动画图标

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MapLibre GL JS第47课:添加动画图标

📌 学习目标

  • 掌握添加动画图标的实现方法
  • 理解相关API的使用
  • 能够独立完成类似功能开发

🎯 核心概念

向地图添加动画图标。

💻 完 整 代 码

代码示例

constmap=newmaplibregl.Map({container:"map",style:"https://demotiles.maplibre.org/style.json",});constsize=200;constpulsingDot={width:size,height:size,data:newUint8Array(size*size*4),onAdd(){constcanvas=document.createElement("canvas");canvas.width=this.width;canvas.height=this.height;this.context=canvas.getContext("2d");},render(){constduration=1000;constt=(performance.now()%duration)/duration;constradius=(size/2)*0.3;constouterRadius=(size/2)*0.7*t+radius;constcontext=this.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.fillStyle=`rgba(255, 200, 200,${1-t})`;context.fill();context.beginPath();context.arc(this.width/2,this.height/2,radius,0,Math.PI*2);context.fillStyle="rgba(255, 100, 100, 1)";context.strokeStyle="white";context.lineWidth=2+4*(1-t);context.fill();context.stroke();this.data=context.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"},});});

代码示例

<!DOCTYPEhtml><htmllang="zh-CN"><head><title>向地图添动态图标</title><metaproperty="og:description"content="向地图添加使用 Canvas API 在运行时生成的动画图标。"/><metaproperty="og:created"content="2025-06-25"/><metacharset="utf-8"/><metaname="viewport"content="width=device-width, initial-scale=1"/><linkrel="stylesheet"href="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css"/><scriptsrc="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js"></script><style>body{margin:0;padding:0;}html, body, #map{height:100%;}</style></head><body><divid="map"></div><script>constmap=newmaplibregl.Map({container:"map",style:"https://demotiles.maplibre.org/style.json",});constsize=200;constpulsingDot={width:size,height:size,data:newUint8Array(size*size*4),onAdd(){constcanvas=document.createElement("canvas");canvas.width=this.width;canvas.height=this.height;this.context=canvas.getContext("2d");},render(){constduration=1000;constt=(performance.now()%duration)/duration;constradius=(size/2)*0.3;constouterRadius=(size/2)*0.7*t+radius;constcontext=this.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.fillStyle=`rgba(255, 200, 200,${1-t})`;context.fill();context.beginPath();context.arc(this.width/2,this.height/2,radius,0,Math.PI*2);context.fillStyle="rgba(255, 100, 100, 1)";context.strokeStyle="white";context.lineWidth=2+4*(1-t);context.fill();context.stroke();this.data=context.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 元素 ID
  • style: 使用 MapLibre 官方样式https://demotiles.maplibre.org/style.json

StyleImageInterface 接口实现

constpulsingDot={width:size,height:size,data:newUint8Array(size*size*4),onAdd(){constcanvas=document.createElement("canvas");canvas.width=this.width;canvas.height=this.height;this.context=canvas.getContext("2d");},render(){constduration=1000;constt=(performance.now()%duration)/duration;// 绘制外圈脉冲效果constradius=(size/2)*0.3;constouterRadius=(size/2)*0.7*t+radius;// 更新图像数据并触发重绘this.data=context.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-地图容器元素的 ID
stylestring/object-地图样式 URL 或内联样式对象

StyleImageInterface 属性

属性类型必填说明
widthnumber图像宽度(像素)
heightnumber图像高度(像素)
dataUint8Array像素数据,RGBA 格式
onAddfunction图层添加时调用,初始化 Canvas
renderfunction每帧调用,返回 true 表示图像已更新

🎨 效果说明

运行代码后,地图上会在坐标[0, 0]处显示一个脉冲动画图标:

  • 内圈: 固定大小的红色圆点,带白色描边
  • 外圈: 脉冲扩散效果,从中心向外逐渐扩大并淡出
  • 动画周期: 1 秒完成一次脉冲循环
  • 交互功能: 支持鼠标拖拽、滚轮缩放等标准交互

动画原理:

  1. render()方法每帧被调用
  2. 使用performance.now()计算动画进度
  3. 动态计算外圈半径和透明度
  4. 通过map.triggerRepaint()触发地图重绘

💡 常 见 问 题

Q1: StyleImageInterface 是什么?
A:这是一个接口,允许开发者创建动态生成的图像。通过实现onAdd()render()方法,可以在运行时生成动画图标。

Q2: 为什么需要返回 true?
A:render()方法返回true告诉地图图像已更新,需要重新渲染。返回false则跳过重绘。

Q3: 性能影响如何?
A:每帧都会调用render()triggerRepaint(),对于复杂动画可能影响性能。建议优化渲染逻辑或降低动画帧率。

Q4: 可以创建多个动画图标吗?
A:可以。为每个动画图标定义不同的 ID,或者使用相同的图像对象创建多个图层。

📝 练习任务

  1. 基础练习:修改动画周期和颜色,创建不同的脉冲效果
  2. 进阶挑战:实现多个不同位置的脉冲图标,每个有不同的动画周期
  3. 拓展思考:如何实现图标沿路径移动的动画?

🌟 最佳实践

  1. 性能优化: 避免在render()中进行复杂计算,考虑预计算或缓存
  2. 内存管理: 对于临时图像,使用后及时清理
  3. 像素比例: 使用pixelRatio参数适配高分辨率屏幕
  4. 动画控制: 提供启动/停止动画的机制
  5. 测试验证: 在不同设备上测试动画性能
  6. 降级方案: 为不支持 Canvas 的环境提供备用方案

🔗 延伸阅读

  • Map API文档

  • MapLibre GL JS 官方文档

  • [下一课预告]:将继续学习地图图层的基础知识


本文是MapLibre GL JS实践课程系列的一部分,欢迎关注收藏

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/13 14:41:54

ARM9中断与EIM实战:MC9328MXL AITC配置与外部总线接口设计

1. 项目概述与核心价值在嵌入式系统开发&#xff0c;尤其是基于ARM9这类经典架构的深度定制中&#xff0c;中断控制器和外部总线接口是两个绕不开的核心硬件模块。它们一个负责处理内部的“紧急呼叫”&#xff0c;一个负责管理外部的“数据通道”&#xff0c;共同构成了系统实时…

作者头像 李华
网站建设 2026/6/13 14:41:52

MC68EZ328引导模式与B-Record加载全解析:从硬件触发到实战调试

1. 项目概述与引导模式核心价值在嵌入式系统开发的早期阶段&#xff0c;尤其是硬件调试和固件烧录环节&#xff0c;我们常常面临一个“先有鸡还是先有蛋”的困境&#xff1a;系统上电后&#xff0c;CPU需要执行存储在非易失性存储器&#xff08;如Flash&#xff09;中的程序来初…

作者头像 李华
网站建设 2026/6/13 14:33:56

单细胞虚拟敲除 scTenifoldKnk 【翰佰尔生物培训实战课程全总结】

在单细胞转录组研究中&#xff0c;探究基因功能、解析调控通路、筛选潜在靶点是高频研究方向。传统基因敲除实验虽结果直观&#xff0c;但存在周期长、成本高、通量低、部分基因敲除致死等诸多痛点&#xff0c;极大限制了大规模候选基因筛选工作。而虚拟敲除技术的出现&#xf…

作者头像 李华
网站建设 2026/6/13 14:33:52

西安交通大学LaTeX学位论文模板:告别格式困扰的终极解决方案

西安交通大学LaTeX学位论文模板&#xff1a;告别格式困扰的终极解决方案 【免费下载链接】XJTU-thesis 西安交通大学学位论文模板&#xff08;LaTeX&#xff09;&#xff08;适用硕士、博士学位&#xff09;An official LaTeX template for Xian Jiaotong University degree th…

作者头像 李华