cesium 实战系列之图标

📅 2026/8/14 9:39:27
cesium 实战系列之图标
cesium 图标我们都知道viewer.entities.add添加个图片、文字啥的都是很简单的网上随便一搜有都是那么我今天就弄点不一样的。canvas 标识牌我们普通的标识牌只能单纯的放图片/文字然而很多时候需要放的内容不仅仅是单纯的图片/文字比如我要看设备id、设备状态、设备名称、设备编号、不同设备状态设置不同的背景等等那这种就需要用到canvas来把所需要的内容画在画布上然后生成图片再绑定到标识牌上。效果如下代码实现async function rankImg(station) { var bgImg station.status 已连接 ? static/img/control/normal.png : static/img/control/warning.png; let canvas document.createElement(canvas); let ctx canvas.getContext(2d); canvas.width bgImg.width; canvas.height bgImg.height; ctx.drawImage(bgImg, 0, 0); ctx.font bold 24px PangMenZhengDao; ctx.fillStyle station.status 已连接 ? #69FF80 : #FF0000; ctx.textAlign center; ctx.textBaseline middle; ctx.fillText(station.deviceId || station.id, 25, 18); ctx.font 12px Source Han Sans CN; ctx.fillStyle station.status 已连接 ? #69FF80 : #FF0000; ctx.textAlign center; ctx.textBaseline middle; ctx.fillText(station.status 已连接 ? 已连接 : 未连接, 25, 40); ctx.font 12px Source Han Sans CN; ctx.fillStyle #fff; ctx.textAlign center; ctx.textBaseline middle; ctx.fillText(station.jammmc, 88, 14); ctx.font bold 22px PangMenZhengDao; ctx.fillStyle #fff; ctx.textAlign center; ctx.textBaseline middle; ctx.fillText(station.sbbh, 85, 35); return canvas.toDataURL(image/png); } async function createSingleMark(stations) { stations.forEach(async(station) { var textValue station.jammmc; var lng parseFloat(station.lon); var lat parseFloat(station.lat); const imgData await rankImg(station); var entityId hot_point_ station.id; // 先删除已存在的实体避免重复添加 const existingEntity viewer.entities.getById(entityId); if (existingEntity) { viewer.entities.remove(existingEntity); } var bilload viewer.entities.add({ id: entityId, name: textValue, position: Cesium.Cartesian3.fromDegrees(lng, lat, 0), billboard: { image: imgData, scale: 1, horizontalOrigin: Cesium.HorizontalOrigin.CENTER, verticalOrigin: Cesium.VerticalOrigin.CENTER, }, }); }) } createSingleMark([ { id:1, sbbh:12, jammmc:地面干扰设备, status:已连接, lon:116.85701689886864, lat:40.46263853921053 }, { id:2, sbbh:13, jammmc:地面干扰设备2, status:未连接, lon:116.81701689886864, lat:40.46263853921053 } ])扩散光圈图标先看效果扩散光圈实现方法先准备一个光圈图片然后利用cesium 的 eclipse 的长半轴、短半轴属性semiMajorAxis、semiMinorAxis实现扩散效果。光圈图片如下代码实现var r { minR: 300, maxR: 800, R: 100 } function changeR() { r.R r.R 2.5 if (r.R r.maxR) { r.R r.minR } let result r.R return result } function changeR2() { let result r.R return result } //扩散圆圈 function createPoint() { viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(120.1804,28.6626, 100), ellipse: { semiMajorAxis: new Cesium.CallbackProperty(changeR, false), semiMinorAxis: new Cesium.CallbackProperty(changeR2, false), material: new Cesium.ImageMaterialProperty({ image: static/img/hot_point.png, repeat: new Cesium.Cartesian2(1.0, 1.0), transparent: true, }), height: 100, // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND //如果有地形需要开启贴地 } }) } createPoint()动态光圈先看效果动态光圈实现思路通过html创建div实例然后通过append绑定在页面上之后再利用css animation 实现动画效果然后通过cesium监测实时更新图标位置的方法将图标绑定在地图上。具体代码//闪烁圆点 showTipsImageBg({ htmlstr: div style div classhtmlsymbol_gpsdot div classhtmlsymbol_gpsdot2 div classhtmlsymbol_gpsdot3div classhtmlsymbol_reallocationtipxxxxxxxxxxx大街/div/div /div /div /div }, 119.0789, 28.5227, 0); var infoTipsArray []; // 开启监听器--无论对当前地球做的任何操作都会监听到 let postRender viewer.scene.postRender.addEventListener(() { infoTipsArray.forEach(function (item) { var scratch new Cesium.Cartesian2(); var pos Cesium.Cartesian3.fromDegrees(item.lng, item.lat, item.height); var canvasPosition viewer.scene.cartesianToCanvasCoordinates(pos, scratch); if (canvasPosition) { var bubbledom item.dom; bubbledom.css({ top: canvasPosition.y - (item.ishalf ? bubbledom.outerHeight() / 2 : (bubbledom.outerHeight() - 10)), left: canvasPosition.x - bubbledom.outerWidth() / 2 $(#cesiumContainer).offset().left }); } }); }) function showTipsImageBg(config, lng, lat, height, ishalf) { var bubbledom $(div classinfotips2 style (config.color ? (background-color: config.color ;) : ) div classbubblecsscont config.htmlstr /div/div); var pos Cesium.Cartesian3.fromDegrees(lng, lat, height); var scratch new Cesium.Cartesian2(); var canvasPosition viewer.scene.cartesianToCanvasCoordinates(pos, scratch); if (Cesium.defined(canvasPosition)) { $(#content).append(bubbledom); bubbledom.css({ top: canvasPosition.y - (ishalf ? bubbledom.outerHeight() / 2 : (bubbledom.outerHeight() - 10)), left: canvasPosition.x - bubbledom.outerWidth() / 2 $(#cesiumContainer).offset().left }); } infoTipsArray.push({ dom: bubbledom, lng: lng, lat: lat, height: height, ishalf: ishalf ? true : false }); }style langscss .htmlsymbol_cont{ position: relative; width: 100%; } /*模拟百度地图发光圆圈效果*/ .htmlsymbol_gpsdot { animation: htmlsymbol_gpssploosh 2.5s cubic-bezier(0.165, 0.84, 0.44, 1); } .htmlsymbol_gpsdot2 { animation: htmlsymbol_gpssploosh2 2.5s cubic-bezier(0.165, 0.84, 0.44, 1); } .htmlsymbol_gpsdot3 { animation: htmlsymbol_gpssploosh3 2.5s cubic-bezier(0.165, 0.84, 0.44, 1); } .htmlsymbol_gpsdot,.htmlsymbol_gpsdot2,.htmlsymbol_gpsdot3{ height: 12px; width: 12px; font-size: 12px; color: #fff; line-height: 20px; text-align: center; /*opacity: 0.99;*/ border-radius: 100%; position: absolute; z-index: 10; animation-iteration-count:infinite; background: transparent; } keyframes htmlsymbol_gpssploosh { 0% { box-shadow: 0 0 0 0px rgba(2,220,57, 0.7); background: rgba(2,220,57, 0.7); } 100% { box-shadow: 0 0 0 40px rgba(2,220,57, 0); background: rgba(2,220,57, 0); } } keyframes htmlsymbol_gpssploosh2 { 0% { box-shadow: 0 0 0 0px rgba(2,220,57, 0.7); background: rgba(2,220,57, 0.7); } 100% { box-shadow: 0 0 0 25px rgba(2,220,57, 0); background: rgba(2,220,57, 0.3); } } keyframes htmlsymbol_gpssploosh3 { 0% { box-shadow: 0 0 0 0px rgba(2,220,57, 0.7); background: rgba(2,220,57, 0.7); } 100% { box-shadow: 0 0 0 10px rgba(2,220,57, 0); background: rgba(2,220,57, 1); } } .htmlsymbol_reallocationtip{ position:absolute; bottom: -25px; border-radius: 4px; left:-25px; width: 70px; height:20px; line-height: 1.5em; /*background-color: rgba(0,0,0,0.1);*/ font-size: 12px; } /stylehtml仿标识牌图标既然可以通过html创建div的方式创建图标那我们都知道cesium有标识牌图标那是否可以通过html做一个标识牌图标呢于是他来了。效果代码实现其中showTipsImageBg 就是上述代码中的方法。showTipsImageBg({ htmlstr: div styleposition: relative;width:332px;height:222px;background:url(static/img/tip1.png) top center no-repeat;background-size:contain;text-align:left; div styleposition: absolute;top: 25px;height: 123px;left: 27px;right: 30px; div stylefloat:left;width:100px;height:100%;background:url(static/img/gugong.jpg) center center / cover no-repeat;/div div stylefloat:right;width:150px;height:100%; div stylefont-size:15px;font-weight:bold;xxxxxxx/div div stylefont-size:12px;color: #b7b7b7;简介xxxxxxxxxxxxxxxxxxxxxxxxxxx。/div div stylefont-size:12px;color: #b7b7b7;级别AAAAA级/div /div /div /div }, 119.5, 28.2, 0);echarts 图表图标那既然html都可以那图表是不是也可以呢于是乎就有了图表图标效果图表代码实现//图表 showTipsImageBg({ htmlstr: div stylepointer-events:auto;position: relative;width:332px;height:222px;background:url(static/img/tip1.png) top center no-repeat;background-size:contain;text-align:left; div idhtmltipChart styleposition: absolute;top: 25px;height: 123px;left: 27px;right: 30px;/div /div }, 119.8402, 28.4453, 0); // 图表渲染 var chartrender_2 function (domid) { var datalist [60, 220, 1053, 826]; var dom document.getElementById(domid); let myChart echarts.init(dom); var option { tooltip: { trigger: item, formatter: function (params) { // console.log(params); var vl params.value;; if (params.seriesName ! realdata) { vl 200 - vl; } return params.name : vl ; } }, // color:color, grid: { left: 30, right: 0, top: 20, bottom: 20, containLabel: false }, xAxis: { type: category, show: true, axisTick: { show: false }, axisLine: { show: false }, data: [丽水市, 杭州市, 松阳市, xxxx], axisLabel: { show: true, interval: 0, color: function (value, index) { // if(index 0) { // return red; // } // if(index 1) { // return #ff8447; // } // if(index 2) { // return #ffcc00; // } // return rgb(18,205,12) return #fff; }, fontSize: 10, } }, yAxis: { type: value, name: , show: true, axisTick: { show: false }, axisLine: { show: false }, splitLine: { lineStyle: { color: #282B33 } }, axisLabel: { show: true, color: function (value, index) { // if(index 0) { // return red; // } // if(index 1) { // return #ff8447; // } // if(index 2) { // return #ffcc00; // } // return rgb(18,205,12) return #fff; }, fontSize: 12, // fontWeight: bold } }, series: [ { name: realdata, type: bar, barWidth: 10, itemStyle: { normal: { color: new echarts.graphic.LinearGradient(1, 0, 0, 1, [{ offset: 0, color: #2A6BCD }, { offset: 1, color: #34F6F8 }]), barBorderRadius: [5, 5, 5, 5] } }, label: { normal: { position: top, show: true, color: white, formatter: function (params) { return params.value; }, } }, data: datalist } ] }; myChart.setOption(option); }; chartrender_2(htmltipChart);视频图标那既然图表都可以那视频是否可以呢于是乎。。。效果视频图标代码实现//视频播放 showTipsImageBg({ htmlstr: div stylepointer-events:auto;width:320px;height:200px;background:url(static/img/tip3.png);background-size:100% 100%;text-align:left; div idhtmltipChart styleposition: absolute;top: 25px;height: 170px;left: 23px;right: 23px; video srcstatic/img/360.mp4 stylewidth:100%;height:100%;object-fit:fill; controls autoplay/video /div /div }, 120.1352, 28.2061, 0);那既然视频都可以那自己随便做一个动图是不是也可以于是乎。。。雷达扫描模拟图标效果雷达扫描代码实现//雷达 showTipsImageBg({ htmlstr: div styleposition: relative;width:300px;height:300px;border:1px dashed rgba(255,255,255,.3);border-radius:50%; div idhtmlradar styleposition: absolute;width:100%;height:100%;canvas idhtmlradarcanvas width300px height300px/canvas/div /div }, 119.2302, 28.2093, 0, true); //绘制扇形雷达相关方法 var CFG { initDeg: 60,//扇形的初始角度 perDeg: 1//每次旋转角度 }; var can document.getElementById(htmlradarcanvas); var ctx can.getContext(2d); var deg 0;//记录已旋转角度 var raf window.requestAnimationFrame(loop); function loop() { deg (deg CFG.perDeg); can.height can.height; drawRadar(deg); raf window.requestAnimationFrame(loop); } function drawRadar(iDeg) { gradientFun(iDeg); } //渐变填充----保证渐变效果可根据实际性能进行设置线性渐变需要随时计算偏移不稳定 function gradientFun(iDeg) { // var grd ctx.createLinearGradient(175,100,can.width,150); // grd.addColorStop(0,rgba(0,255,0,0)); // grd.addColorStop(1,rgba(0,255,0,1)); var startopa 0.1;//初始的透明度 var endopa 0.9;//结束的透明度 var rad_step 1;//渲染间隔度数-角度 var sum_step (CFG.initDeg / rad_step);//总共渲染次数 var opa_step (endopa - startopa) / sum_step;//透明度步进 for (var i 0; i sum_step; i) { ctx.fillStyle rgba(0,150,150, (startopa opa_step * i) ); // console.log(ctx.fillStyle); ctx.beginPath(); ctx.moveTo(150, 150); ctx.arc(150, 150, 150, (-(CFG.initDeg * (1 - i / sum_step)) iDeg) / 180 * Math.PI, (-(CFG.initDeg * (1 - (i 1) / sum_step)) iDeg) / 180 * Math.PI); ctx.fill(); ctx.closePath(); } }