cesium canvas广告牌

在有些业务中,对场景中的广告牌样式要求比较高,需要动态显示一些数据,这个时候,我们可以通过将复杂背景样式制作成图片,通过canvas绘制图片和动态数据,从而达到比较好的显示效果。

1 CanvasMarker 类封装

CanvasMarker.js

/*
 * @Description:
 * @Author: maizi
 * @Date: 2022-05-27 11:36:22
 * @LastEditTime: 2024-07-23 15:34:06
 * @LastEditors: maizi
 */
const merge = require('deepmerge')

const defaultStyle = {
  scale: 0.2,
  text: '0.0'
}
const monitorPanel =  require('@/assets/img/monitorPanel.png')

class CanvasMarker {
  constructor(viewer, coords, options = {}) {
    this.viewer = viewer;
    this.coords = coords;
    this.options = options;
    this.props = this.options.props;
    this.baseHeight = this.coords[2] || 10;
    this.style = merge(defaultStyle, this.options.style || {});

    this.entity = null;
    this.canvas = null
    this.init();
  }

  init() {
    let img =  new Image();
    img.src = monitorPanel;
    img.onload = ()=> {
      this.picture = img;
      this.canvas = this.createCanvas()
    }
    this.entity = new Cesium.Entity({
      type: "canvas_point",
      props: this.props,
      position:Cesium.Cartesian3.fromDegrees(this.coords[0], this.coords[1], this.baseHeight),
      billboard: {
        image: new Cesium.CallbackProperty(() => {
          return this.canvas.toDataURL("image/png");
        }),
        scale:new Cesium.CallbackProperty(() => {
          return this.style.scale
        }),
        color: new Cesium.Color(1, 1, 1),
        disableDepthTestDistance: Number.POSITIVE_INFINITY,
      },
    });
  }

  createCanvas() {
    let canvas = null;
    if (this.canvas) {
      canvas = this.canvas
    } else {
      canvas = document.createElement("canvas");
      canvas.width = 512;
      canvas.height = 329;
    }
    let ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(this.picture, 0, 0);
    ctx.fillStyle = "rgb(255, 255, 255)",
    ctx.font = "80px sans-serif",
    ctx.textBaseline = "middle",
    ctx.fillText("温度:", 50, canvas.height / 2);
    ctx.fillText(this.style.text, 270, canvas.height / 2);
    ctx.fillText("℃", 380, canvas.height / 2);

    // // 导出canvas为图片
    // var dataURL = canvas.toDataURL("image/png");
    // var link = document.createElement('a');
    // link.download = 'canvas-image.png';
    // link.href = dataURL;
    // link.click();
    return canvas;
  }
  setSelect(enabled) {
    if (enabled) {
      this.addPoint()
    } else {
      this.removePoint()
    }
  }

  addPoint() {
    this.point = new Cesium.Entity({
      position: Cesium.Cartesian3.fromDegrees(this.coords[0], this.coords[1],  this.baseHeight),
      point: {
        color: Cesium.Color.DARKBLUE.withAlpha(.4),
        pixelSize: 6,
        outlineColor: Cesium.Color.YELLOW.withAlpha(.8),
        outlineWidth: 4,
        disableDepthTestDistance: Number.POSITIVE_INFINITY
      }     
    }); 
    this.viewer.entities.add(this.point)
  }

  removePoint() {
    if (this.point) {
      this.viewer.entities.remove(this.point)
      this.point = null
    }
  }

  updateStyle(style) {
    this.style = merge(this.style, style || {});
    this.createCanvas()
  }
}

export {
  CanvasMarker
}

2 完整示例代码

MapWorks.js

import GUI from 'lil-gui'; 
// 初始视图定位在中国
import { CanvasMarker } from './CanvasMarker'

Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(90, -20, 110, 90);
//天地图key
const key = '39673271636382067f0b0937ab9a9677'

let viewer = null;
let eventHandler = null;
let canvasLayer = null
let canvasList = []
let selectGraphic = null
let gui = null

function initMap(container) {
  viewer = new Cesium.Viewer(container, {
    animation: false,
    baseLayerPicker: false,
    fullscreenButton: false,
    geocoder: false,
    homeButton: false,
    infoBox: false,
    sceneModePicker: false,
    selectionIndicator: false,
    timeline: false,
    navigationHelpButton: false, 
    scene3DOnly: true,
    orderIndependentTranslucency: false,
    contextOptions: {
      webgl: {
        alpha: true
      }
    }
  })
  viewer._cesiumWidget._creditContainer.style.display = 'none'
  viewer.scene.fxaa = true
  viewer.scene.postProcessStages.fxaa.enabled = true
  if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
    // 判断是否支持图像渲染像素化处理
    viewer.resolutionScale = window.devicePixelRatio
  }
  // 移除默认影像
  removeAll()
  // 地形深度测试
  viewer.scene.globe.depthTestAgainstTerrain = true
  // 背景色
  viewer.scene.globe.baseColor = new Cesium.Color(0.0, 0.0, 0.0, 0)
  // 太阳光照
  viewer.scene.globe.enableLighting = true;

  // 初始化图层
  initLayer()

  // 鼠标事件
  initClickEvent()

  //调试
  window.viewer = viewer
}

function initClickEvent() {
  eventHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  initLeftClickEvent()
  initMouseMoveEvent()
}

function initLeftClickEvent() {
  eventHandler.setInputAction((e) => {
    if (selectGraphic) {
      selectGraphic.setSelect(false)
      selectGraphic = null
    }
    if (gui) {
      gui.destroy()
    }
    let pickedObj = viewer.scene.pick(e.position);
    if (pickedObj && pickedObj.id) {
      if (pickedObj.id.type === 'canvas_point') {
        selectGraphic = getGraphicById(pickedObj.id.id)
        if (selectGraphic) {
          selectGraphic.setSelect(true)
          initGui()
        }
      }
    }
  },Cesium.ScreenSpaceEventType.LEFT_CLICK)
}

function initMouseMoveEvent() {
  eventHandler.setInputAction((e) => {
    const pickedObj = viewer.scene.pick(e.endPosition);
    if (pickedObj && pickedObj.id) {
      if (pickedObj.id.type === 'canvas_point') {
        // 改变鼠标状态
        viewer._element.style.cursor = "";
        document.body.style.cursor = "pointer";
      } else {
        viewer._element.style.cursor = "";
        document.body.style.cursor = "default";
      }
    } else {
      viewer._element.style.cursor = "";
      document.body.style.cursor = "default";
    }
  },Cesium.ScreenSpaceEventType.MOUSE_MOVE)
}

function getGraphicById(id) {
  let graphic = null
  for (let i = 0; i < canvasList.length; i++) {
    if (canvasList[i].entity.id === id) {
      graphic = canvasList[i]
      break
    } 
  }
  return graphic
}
function initGui() {
  let params = {
    ...selectGraphic.style
  }
  gui = new GUI()
  let layerFolder = gui.title('参数设置')
  layerFolder.add(params, 'scale', 0.1, 2).step(0.1).onChange(function (value) {
    selectGraphic.updateStyle(params)
  })
}

function addTdtLayer(options) {
  let url = `https://t{s}.tianditu.gov.cn/DataServer?T=${options.type}&x={x}&y={y}&l={z}&tk=${key}`
  const layerProvider = new Cesium.UrlTemplateImageryProvider({
    url: url,
    subdomains: ['0','1','2','3','4','5','6','7'],
    tilingScheme: new Cesium.WebMercatorTilingScheme(),
    maximumLevel: 18
  });
  viewer.imageryLayers.addImageryProvider(layerProvider);
}

function initLayer() {
  addTdtLayer({
    type: 'img_w'
  })
  addTdtLayer({
    type: 'cia_w'
  })
  canvasLayer = new Cesium.CustomDataSource('canvasMarker')
  viewer.dataSources.add(canvasLayer)
 
}

function loadCanvasMarker(points) {
  points.forEach(item => {
    const canvasMarker = new CanvasMarker(viewer, item)
    canvasList.push(canvasMarker)
    canvasLayer.entities.add(canvasMarker.entity)
  });
  viewer.flyTo(canvasLayer)
  updateText()
}

function updateText() {
  setInterval(()=>{
    canvasList.forEach((item) => {
      const text = Math.floor(30 * Math.random())
      item.updateStyle({
        text: text
      })
    })
  }, 2000)
}


function removeAll() {
  viewer.imageryLayers.removeAll();
}

function destroy() {
  viewer.entities.removeAll();
  viewer.imageryLayers.removeAll();
  viewer.destroy();
}

export {
  initMap,
  loadCanvasMarker,
  destroy
}

CanvasMarker.vue

<!--
 * @Description: 
 * @Author: maizi
 * @Date: 2023-04-07 17:03:50
 * @LastEditTime: 2023-04-11 18:07:29
 * @LastEditors: maizi
-->

<template>
  <div id="container">
  </div>
</template>

<script>
import * as MapWorks from './js/MapWorks'
export default {
  name: 'CanvasMarker',
  mounted() {
    this.init();
  },
  methods:{
    init(){
      let container = document.getElementById("container");
      MapWorks.initMap(container)
      //创建告警点
      let points = [
        [104.074822, 30.659807, 60],
        [104.076822, 30.653807, 60],
        [104.075822, 30.652807, 60],
        [104.072822, 30.654807, 60]
      ];
      MapWorks.loadCanvasMarker(points)
    }
  },

  beforeDestroy(){
    //实例被销毁前调用,页面关闭、路由跳转、v-if和改变key值
    MapWorks.destroy();
  }
}
</script>

<style lang="scss" scoped>
#container{
  width: 100%;
  height: 100%;
  background: rgba(7, 12, 19, 1);
  overflow: hidden;
  background-size: 40px 40px, 40px 40px;
  background-image: linear-gradient(hsla(0, 0%, 100%, 0.05) 1px, transparent 0), linear-gradient(90deg, hsla(0, 0%, 100%, 0.05) 1px, transparent 0);
}


</style>

3 运行效果

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Cesium中创建自定义广告牌,您可以按照以下步骤进行操作: 1. 首先,您需要在Cesium中加载必要的库和资源。您可以从Cesium官方网站下载Cesium库,并将其包含在您的项目中。 2. 创建一个Canvas元素,用于绘制广告牌的内容。您可以使用HTML5 Canvas API绘制图形、文本等。 3. 使用Cesium的Entity API创建一个实体对象,将广告牌与地球上的位置相关联。您可以指定广告牌的位置、旋转角度、大小等属性。 4. 使用Cesium的BillboardVisualizer类创建一个广告牌可视化对象,并将其与实体对象关联起来。这将处理广告牌的渲染和更新。 5. 在渲染循环中,更新广告牌的内容和属性。您可以根据需要更改广告牌的位置、颜色、文本等。 下面是一个简单的示例代码,展示了如何在Cesium中创建自定义广告牌: ```javascript // 创建Canvas元素 var canvas = document.createElement('canvas'); canvas.width = 128; canvas.height = 64; var context2D = canvas.getContext('2d'); // 在Canvas绘制内容 context2D.fillStyle = 'red'; context2D.fillRect(0, 0, canvas.width, canvas.height); context2D.font = 'bold 24px Arial'; context2D.fillStyle = 'white'; context2D.fillText('Custom Billboard', 10, 40); // 创建实体对象 var entity = new Cesium.Entity({ position: Cesium.Cartesian3.fromDegrees(0, 0, 0), }); // 创建广告牌可视化对象 var billboardVisualizer = new Cesium.BillboardVisualizer(scene, entityCollection); billboardVisualizer.update(entity); // 初始化广告牌 // 在渲染循环中更新广告牌 scene.postRender.addEventListener(function() { context2D.fillText('Time: ' + Date.now(), 10, 60); // 更新时间 billboardVisualizer.update(entity); // 更新广告牌 }); ``` 这只是一个简单示例,您可以根据需要进行自定义和扩展。您可以在Canvas绘制任何内容,并使用Cesium的其他功能来控制广告牌的属性和行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值