CesiumJS 封装 - 初始化与配置

一、定义基础参数

首先我们使用 ts 定义 初始化的基本参数,大致涵盖创建 cesium 的相关参数,代码如下:

interface CesiumOptions { 
  container: HTMLElement; //容器
  animation?: boolean; //是否创建动画小器件,左下角仪表
  baseLayerPicker?: boolean; //是否显示图层选择器
  fullscreenButton?: boolean; //是否显示全屏按钮
  vrButton?: boolean; // 用于切换 VR 模式的单个按钮小部件。
  geocoder?: boolean; // 是否显示geocoder小器件,右上角查询按钮
  homeButton?: boolean; //是否显示Home按钮
  infoBox?: boolean; //是否显示信息框
  sceneModePicker?: boolean; //是否显示3D/2D选择器
  selectionIndicator?: boolean; //是否显示选取指示器组件
  timeline?: boolean; //是否显示时间轴
  navigationHelpButton?: boolean; //是否显示右上角的帮助按钮
  showCompass?: boolean; // 是否显示指南针
  showFrameRate?: boolean; // 是否显示帧率
  tiandituConfig?: {
    token: string;
    mapServer?: boolean; // 影像:是否使用天地图服务
    national?: boolean;// 是否使用国界线
  }; // 天地图配置
  arcGisConfig?: boolean; // ArcGis配置
  amapConfig?: {
    mapServer?: boolean; // 影像:是否使用高德地图服务
    annotation?: boolean;// 是否使用注记
  };
  tencentConfig?:{
    mapServer?: boolean; // 影像:是否使用腾讯服务
    annotation?: boolean;// 是否使用注记
    vector?: number;//  是否使用矢量图层  1 表示默认,4 表示黑色矢量图层
  };
  skyBox?: [string, string, string, string, string, string]; // 是否显示天空盒
  customMouseControl?: MouseControllerOptions; // 自定义鼠标控制选项
}

详细参数介绍

参数名

类型/默认值

说明

container

HTMLElement

必填,Cesium 渲染容器,DOM 元素

animation

boolean

是否显示左下角动画仪表(默认为 true

baseLayerPicker

boolean

是否显示图层选择器(默认为 true

fullscreenButton

boolean

是否显示全屏按钮(默认为 true

vrButton

boolean

是否显示 VR 模式切换按钮(默认为 false

geocoder

boolean

是否显示右上角地理编码查询按钮(默认为 true

homeButton

boolean

是否显示 Home(回到初始视角)按钮(默认为 true

infoBox

boolean

是否显示实体信息框(点击实体后弹出)(默认为 true

sceneModePicker

boolean

是否显示 3D/2D/哥伦布模式切换按钮(默认为 true

selectionIndicator

boolean

是否显示选中实体的高亮指示器(默认为 true

timeline

boolean

是否显示时间轴(默认为 true

navigationHelpButton

boolean

是否显示右上角帮助按钮(默认为 true

showCompass

boolean

是否显示指南针(默认为 true

showFrameRate

boolean

是否显示帧率(FPS)(默认为 false

tiandituConfig

object

天地图服务配置(可选)

├─ token

string

天地图 Token(必填)

├─ mapServer

boolean

是否使用天地图影像服务(默认 false

└─ national

boolean

是否使用国界线图层(默认 false

arcGisConfig

boolean

是否启用 ArcGIS 地图服务(默认 false

amapConfig

object

高德地图服务配置(可选)

├─ mapServer

boolean

是否使用高德影像服务(默认 false

└─ annotation

boolean

是否使用高德注记图层(默认 false

tencentConfig

object

腾讯地图服务配置(可选)

├─ mapServer

boolean

是否使用腾讯影像服务(默认 false

├─ annotation

boolean

是否使用腾讯注记图层(默认 false

└─ vector

number

矢量图层类型:1 默认矢量,4 黑色矢量(默认 1

skyBox

[string, string, string, string, string, string]

天空盒贴图路径(6 张图)

customMouseControl

MouseControllerOptions

自定义鼠标控制选项(可选)

二、使用类开始封装基础功能

(一) 初始化代码

import * as Cesium from 'cesium';
import TOKEN from './token'; // 特殊注意点1
import EventEmitter from '../common/emitter'; // 特殊注意点2

class CesiumManager extends EventEmitter{
  protected viewer: Cesium.Viewer; // viewer实例 
  private camera: Cesium.Camera; // 相机实例
  private container: HTMLElement; // 容器
  private tdtImgMap: Cesium.UrlTemplateImageryProvider | null = null; // 天地图影像
  private tdtIboMap: Cesium.UrlTemplateImageryProvider | null = null; // 天地图矢量
  private arcgisImgMap: Cesium.ArcGisMapServerImageryProvider | null = null; // arcgis影像 
  private amapImgMap: Cesium.UrlTemplateImageryProvider | null = null; // 高德影像
  private amapNoteMap: Cesium.UrlTemplateImageryProvider | null = null; // 高德矢量
  private tencentImgMap: Cesium.UrlTemplateImageryProvider | null = null; // 腾讯影像
  private tencentNoteMap: Cesium.UrlTemplateImageryProvider | null = null; // 腾讯矢量
  private tencentVectorMap: Cesium.UrlTemplateImageryProvider | null = null; // 腾讯矢量
  private sseHandler: Cesium.ScreenSpaceEventHandler; // 鼠标事件
  constructor(options: CesiumOptions) {
    super() // 初始化事件总线
    Cesium.Ion.defaultAccessToken = TOKEN // 设置Cesium ion的token
    const { skyBox = DEFAULT_SKY_BOX } = options 
    this.container = options.container
    this.viewer = new Cesium.Viewer(this.container, {
      animation: options.animation || false, //是否创建动画小器件,左下角仪表
      baseLayerPicker: options.baseLayerPicker || false, //是否显示图层选择器
      fullscreenButton: options.fullscreenButton || false, //是否显示全屏按钮
      vrButton: options.vrButton || false, // 用于切换 VR 模式的单个按钮小部件。
      geocoder: options.geocoder || false, // 是否显示geocoder小器件,右上角查询按钮
      homeButton: options.homeButton || false, //是否显示Home按钮
      infoBox: options.infoBox || false, //是否显示信息框
      sceneModePicker: options.sceneModePicker || false, //是否显示3D/2D选择器
      selectionIndicator: options.selectionIndicator || false, //是否显示选取指示器组件
      timeline: options.timeline || false, //是否显示时间轴
      navigationHelpButton: options.navigationHelpButton || false, //是否显示右上角的帮助按钮
      navigationInstructionsInitiallyVisible: false,
      scene3DOnly: true, //如果设置为true,则所有几何图形以3D模式绘制以节约GPU资源
      shouldAnimate: false, // 初始化是否开始动画
      clockViewModel: undefined, // 一个视图模型,它为用户界面提供 Clock
      skyBox: new Cesium.SkyBox({
        sources: {
          positiveX: skyBox[0],
          negativeX: skyBox[1],
          positiveY: skyBox[2],
          negativeY: skyBox[3],
          positiveZ: skyBox[4],
          negativeZ: skyBox[5]
        }
      }), //用于渲染星空的SkyBox对象
      skyAtmosphere: new Cesium.SkyAtmosphere(), // 围绕提供的椭球体边缘绘制的大气
      fullscreenElement: document.body, //全屏时渲染的HTML元素,
      useDefaultRenderLoop: true, //如果需要控制渲染循环,则设为true
      targetFrameRate: undefined, //使用默认render loop时的帧率
      showRenderLoopErrors: false, //如果设为true,将在一个HTML面板中显示错误信息
      automaticallyTrackDataSourceClocks: true, //自动追踪最近添加的数据源的时钟设置
      sceneMode: Cesium.SceneMode.SCENE3D, //初始场景模式
      mapProjection: new Cesium.WebMercatorProjection(), //地图投影体系
      globe: undefined, // 在场景中渲染的地球仪,包括其地形 ( Globe#terrainProvider ) 和图像图层 ( Globe#imageryLayers )
      orderIndependentTranslucency: true,
      dataSources: new Cesium.DataSourceCollection(), //需要进行可视化的数据源的集合
      projectionPicker: undefined, //ProjectionPicker 是用于在透视和正交投影之间切换的单按钮小部件。
      useBrowserRecommendedResolution: false, //如果设置为true,则使用浏览器推荐的分辨率
      contextOptions: {
        // 支持html2cavas截图
        webgl: {
          preserveDrawingBuffer: true, // 设置为 true 来启用
          alpha: false,
          depth: true,
          stencil: false,
          antialias: true,
          premultipliedAlpha: true,
          failIfMajorPerformanceCaveat: false
        }
      }
    })
    // 阻止右键菜单
    this.container.addEventListener('contextmenu', function (event) {
      event.preventDefault();
    }, false);
    // 去掉cesium底部的logo
    this.container.querySelector('.cesium-viewer-bottom')?.remove()
    // 根据配置开启帧率
    if (options.showFrameRate) {
      this.viewer.scene.debugShowFramesPerSecond = true
    }
    this.viewer.resolutionScale = window.devicePixelRatio; // 屏幕分辨率
    // 鼠标滚轮缩放
    this.viewer.scene.screenSpaceCameraController.zoomEventTypes = [
      Cesium.CameraEventType.WHEEL,
      Cesium.CameraEventType.PINCH
    ];
    // 鼠标滚轮倾斜
    this.viewer.scene.screenSpaceCameraController.tiltEventTypes = [
      Cesium.CameraEventType.PINCH,
      Cesium.CameraEventType.RIGHT_DRAG
    ];
    this.viewer.camera.maximumZoomFactor = 1 // 最大缩放比例 默认为1
    // 大气层显示
    if (this.viewer && this.viewer.scene && this.viewer.scene.skyAtmosphere) {
      this.viewer.scene.skyAtmosphere.show = true
    }
    // 开启地形深度检测
    this.viewer.scene.globe.depthTestAgainstTerrain = true
    this.viewer.scene.fog.enabled = true; // 开启雾
    this.viewer.shadows = true // 启用场景中的阴影
    this.viewer.terrainShadows = Cesium.ShadowMode.ENABLED // 启用地形阴影
    // 启用软阴影和PCF
    this.viewer.shadowMap.softShadows = true;
    this.viewer.shadowMap.size = 4096;
    // 鼠标操作惯性控制
    //关闭旋转惯性
    this.viewer.scene.screenSpaceCameraController.inertiaSpin = 0
    //关闭平移惯性
    this.viewer.scene.screenSpaceCameraController.inertiaTranslate = 0
    //关闭缩放惯性
    this.viewer.scene.screenSpaceCameraController.inertiaZoom = 0
    // 初始化鼠标事件处理程序
    this.sseHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    // 确保 Cesium 的时间系统正常更新
    this.viewer.clock.shouldAnimate = true;
    this.viewer.clock.multiplier = 1.0; // 设置时间倍速
    // 启用高质量纹理过滤
    this.viewer.scene.globe.maximumScreenSpaceError = 1.0 // 降低地形细节误差
    this.viewer.scene.globe.tileCacheSize = 1000 // 增加瓦片缓存
    // 优化模型渲染
    this.viewer.scene.logarithmicDepthBuffer = true // 启用对数深度缓冲
    this.viewer.scene.highDynamicRange = true // 启用高动态范围 
    // 展示指南针
    if (options?.showCompass) {
      this.dealCompass()
    }
  }
}

注意点:

1. cesium token 请自行前往 Cesium ion 获取

const TOKEN = 'xxxxxxx'

export default TOKEN

2. 主体类需要使用事件广播所以需要集成事件处理库

pnpm add mitt
import mitt from 'mitt';

class EventEmitter {
  emitter: any;
  constructor() {
    this.emitter = mitt();
  }

  on(eventName: string, handler: Function) {
    this.emitter.on(eventName, handler);
  }

  off(eventName: string, handler: Function) {
    this.emitter.off(eventName, handler);
  }

  emit(eventName: string, event?: any) {
    this.emitter.emit(eventName, event);
  }

  removeAllListeners() {
    this.emitter.all.clear()
  }
}
export default EventEmitter;

(二) 功能方法说明

1. 定位到中国

class CesiumManager extends EventEmitter{
  ...
  constructor(options: CesiumOptions) {
    ...
    this.localChina()
  }
  // 定位到中国
  private localChina() {
    this.viewer.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(103.84, 31.15, 20000000),
      orientation: {
        heading: Cesium.Math.toRadians(0), // 方向
        pitch: Cesium.Math.toRadians(-90.0), // 倾斜角度
        roll: Cesium.Math.toRadians(0)
      }
    })
  }
}

2. 添加指南针

pnpm add cesium-navigation-es6
import CesiumNavigation from 'cesium-navigation-es6';

class CesiumManager extends EventEmitter{
  ...
  constructor(options: CesiumOptions) {
    ...
    // 添加指南针
    if (options?.showCompass) {
      this.dealCompass()
    }
  }
  // 启用指南针
  private dealCompass() {
    const options: CompassOptions = {
      defaultResetView: new Cesium.Cartographic(Cesium.Math.toRadians(103.84), Cesium.Math.toRadians(31.15), 20000000),
      enableCompass: true,
      enableZoomControls: true,
      enableDistanceLegend: true,
      enableCompassOuterRing: true
    }
    //自定义指南针图的SVG,可以去阿里图库下载
    //options.compassOuterRingSvg = ``;
    // @ts-ignore
    new CesiumNavigation(this.viewer, options)
  }
}

3. 添加地图影像(支持天地图/arcgis/高德/腾讯)

......
class CesiumManager extends EventEmitter{
  ...
  constructor(options: CesiumOptions) {
    ...
    // 添加指南针
    if (options?.tiandituConfig) {
      this.dealTDTMapServer(options?.tiandituConfig)
    }
    if (options?.arcGisConfig) {
      this.dealArcGisMapServer()
    }
    if (options?.amapConfig) {
      this.dealAMapMapServer(options?.amapConfig)
    }
    if (options?.tencentConfig) {
      this.dealTencentMapServer(options?.tencentConfig)
    }
  }
  // 处理天地图影像
  private dealTDTMapServer(config: CesiumOptions['tiandituConfig']):void {
    const tdtUrl = 'https://t{s}.tianditu.gov.cn/';
    const subdomains = ['0', '1', '2', '3', '4', '5', '6', '7'];
    if (config?.mapServer) {
      this.tdtImgMap = new Cesium.UrlTemplateImageryProvider({
        url: tdtUrl + 'DataServer?T=img_w&x={x}&y={y}&l={z}&tk=' + config?.token,
        subdomains: subdomains,
        tilingScheme : new Cesium.WebMercatorTilingScheme(),
        maximumLevel : 18
      });
      this.viewer.imageryLayers.addImageryProvider(this.tdtImgMap); 
    }
    if (config?.national) {
      this.tdtIboMap = new Cesium.UrlTemplateImageryProvider({
        url: tdtUrl + 'DataServer?T=ibo_w&x={x}&y={y}&l={z}&tk=' + config?.token,
        subdomains: subdomains,
        tilingScheme : new Cesium.WebMercatorTilingScheme(),
        maximumLevel : 10
      });
      this.viewer.imageryLayers.addImageryProvider(this.tdtIboMap);   
    }
  }
  // ArcGis地图服务
  private async dealArcGisMapServer():Promise<void> {
    this.arcgisImgMap = await Cesium.ArcGisMapServerImageryProvider.fromUrl(
      'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer',
    )
    this.viewer.imageryLayers.addImageryProvider(this.arcgisImgMap)
  }
  // 高德地图服务
  private dealAMapMapServer(config: CesiumOptions['amapConfig']):void {
    if (config?.mapServer) {
      this.amapImgMap = new Cesium.UrlTemplateImageryProvider({
        url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
        minimumLevel: 3,
        maximumLevel: 18,
        tilingScheme: new AmapCorrection()
      })
      this.viewer.imageryLayers.addImageryProvider(this.amapImgMap);   
    }
    if (config?.annotation) {
      this.amapNoteMap = new Cesium.UrlTemplateImageryProvider({
        url: "http://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8",
        minimumLevel: 3,
        maximumLevel: 18,
        tilingScheme: new AmapCorrection()
      })
      this.viewer.imageryLayers.addImageryProvider(this.amapNoteMap);   
    }
  }
  // 腾讯地图服务
  private dealTencentMapServer(config: CesiumOptions['tencentConfig']): void {
    if (config?.mapServer) {
      this.tencentImgMap = new Cesium.UrlTemplateImageryProvider({ 
         // 影像图层
         url: "https://p2.map.gtimg.com/sateTiles/{z}/{sx}/{sy}/{x}_{reverseY}.jpg?version=400",
         // 自定义标签,调整瓦片的 X 和 Y 坐标
         customTags: {
           // SX 坐标映射,利用 X 坐标计算出实际的 SX 值
           sx: function (imageryProvider: any, x: number, y: any, level: any) {
             console.info('imageryProvider: ', imageryProvider, y, level)
             return x >> 4;  // 这里的>>4是对X坐标的处理方式
           },
           // SY 坐标映射,利用 Y 坐标和缩放级别计算出实际的 SY 值
           sy: function (imageryProvider: any, x: any, y: number, level: number) {
            console.info('imageryProvider: ', imageryProvider, x)
             return ((1 << level) - y) >> 4;  // 对 Y 坐标进行逆向计算
           }
         },
         tilingScheme: new AmapCorrection()
       })
      this.viewer.imageryLayers.addImageryProvider(this.tencentImgMap);   
    }
    if (config?.vector) {
      this.tencentVectorMap = new Cesium.UrlTemplateImageryProvider({ 
        url: `https://rt3.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=${config?.vector ?? 1}&version=297`,
        // 自定义标签,调整瓦片的 X 和 Y 坐标
        customTags: {
          // SX 坐标映射,利用 X 坐标计算出实际的 SX 值
          sx: function (imageryProvider: any, x: number, y: any, level: any) {
            console.info('imageryProvider: ', imageryProvider, y, level)
            return x >> 4;  // 这里的>>4是对X坐标的处理方式
          },
          // SY 坐标映射,利用 Y 坐标和缩放级别计算出实际的 SY 值
          sy: function (imageryProvider: any, x: any, y: number, level: number) {
           console.info('imageryProvider: ', imageryProvider, x)
            return ((1 << level) - y) >> 4;  // 对 Y 坐标进行逆向计算
          }
        },
        tilingScheme: new AmapCorrection()
      })
      this.viewer.imageryLayers.addImageryProvider(this.tencentVectorMap);   
    }
    if (config?.annotation) {
      this.tencentNoteMap = new Cesium.UrlTemplateImageryProvider({ 
        url: "https://rt3.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=3&scene=0",
        // 自定义标签,调整瓦片的 X 和 Y 坐标
        customTags: {
          // SX 坐标映射,利用 X 坐标计算出实际的 SX 值
          sx: function (imageryProvider: any, x: number, y: any, level: any) {
            console.info('imageryProvider: ', imageryProvider, y, level)
            return x >> 4;  // 这里的>>4是对X坐标的处理方式
          },
          // SY 坐标映射,利用 Y 坐标和缩放级别计算出实际的 SY 值
          sy: function (imageryProvider: any, x: any, y: number, level: number) {
            console.info('imageryProvider: ', imageryProvider, x)
            return ((1 << level) - y) >> 4;  // 对 Y 坐标进行逆向计算
          }
        },
        tilingScheme: new AmapCorrection()
      })
      this.viewer.imageryLayers.addImageryProvider(this.tencentNoteMap);   
    }
  }
}

4. 初始化基础事件

import CesiumNavigation from 'cesium-navigation-es6';

class CesiumManager extends EventEmitter{
  ...
  constructor(options: CesiumOptions) {
    ...
    this.initEvent()
  }
  // 处理事件
  private initEvent(): void {
    if (!this.viewer) return
    // 1、-----------屏幕空间事件(鼠标和键盘输入相关的事件)
    this.sseHandler.setInputAction((event: any)=> {
      const pickPosition = this.viewer.scene.camera.pickEllipsoid(event.position, this.viewer.scene.globe.ellipsoid)
      
      // 检查pickPosition是否有效,避免undefined值导致的标准化错误
      if (pickPosition && 
          typeof pickPosition.x === 'number' && 
          typeof pickPosition.y === 'number' && 
          typeof pickPosition.z === 'number' &&
          !isNaN(pickPosition.x) && 
          !isNaN(pickPosition.y) && 
          !isNaN(pickPosition.z)) {
        
        const cartesian3 = new Cesium.Cartesian3(pickPosition.x, pickPosition.y, pickPosition.z)
        const cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian3)
         
        if (cartographic) {
          const lat = Cesium.Math.toDegrees(cartographic.latitude)
          const lng = Cesium.Math.toDegrees(cartographic.longitude)
          // 获取被点击的实体
          const pick = this.viewer.scene.pickPosition(event.position)
          const pickEd = this.viewer.scene.pick(event.position)
         
          this.emit('cesium:pick', {
            screen: event.position,
            lngLat: [lng, lat],
            object: pickEd && pick ? pickEd : null,
            pick
          })
        }
      } else {
        // 当点击位置无效时,仍然发送事件但不包含坐标信息
        const pickEd = this.viewer.scene.pick(event.position)
        const pick = this.viewer.scene.pickPosition(event.position)
        
        this.emit('cesium:pick', {
          screen: event.position,
          lngLat: null, // 无效坐标时设为null
          object: pickEd && pick ? pickEd : null,
          pick
        })
      }
      
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
    //  2、-----------时钟事件(时钟相关事件)
    this.viewer.clock.onTick.addEventListener((clock: Cesium.Clock)=> {
      this.emit('cesium:clock', clock)
    });
    // 3、-----------相机事件(相机相关事件)
    this.viewer.camera.changed.addEventListener((zoom: number) => {
      this.emit('cesium:camera:changed', zoom)
    });
    //  4、-----------场景事件(场景相关事件)
    this.viewer.scene.preRender.addEventListener(() => {
      this.emit('cesium:preRender')
    });

  }
}

5. 全部清除(有遗漏后续补充)

import CesiumNavigation from 'cesium-navigation-es6';

class CesiumManager extends EventEmitter{
  ...
  constructor(options: CesiumOptions) {
    ...
  }
  // 全部清除
  allClear(): void {
    if (!this.viewer || this.viewer.isDestroyed()) return; // 添加状态检查

    // 1. 先移除所有事件监听
    this.viewer.clock.onTick.removeEventListener((clock: any)=> {
      this.emit('cesium:clock', clock)
    });
    this.viewer.camera.changed.removeEventListener((zoom: number) => {
      this.emit('cesium:camera', zoom)
    });
    this.viewer.scene.preRender.removeEventListener(() => {
      this.emit('cesium:preRender')
    });

    this.sseHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.sseHandler.destroy();

    this.viewer.entities.removeAll();
    if (this.viewer.scene.primitives) {
      this.viewer.scene.primitives.removeAll();
    }
    this.viewer.imageryLayers.removeAll()
    this.viewer.dataSources.removeAll();
  
    // 3. 清理视频资源
    const videoList = this.container.querySelectorAll('video');
    videoList.forEach((item: HTMLVideoElement) => {
      item.pause();
      item.remove();
    });
    // 4. 最后销毁 viewer
    this.viewer.destroy();
    this.viewer = null as any; // 清空引用
    window.removeEventListener('resize', () => {
      rainSnow.resize(window.innerWidth, window.innerHeight);
    })
  }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值