一、定义基础参数
首先我们使用 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 |
| 必填,Cesium 渲染容器,DOM 元素 |
| animation |
| 是否显示左下角动画仪表(默认为 |
| baseLayerPicker |
| 是否显示图层选择器(默认为 |
| fullscreenButton |
| 是否显示全屏按钮(默认为 |
| vrButton |
| 是否显示 VR 模式切换按钮(默认为 |
| geocoder |
| 是否显示右上角地理编码查询按钮(默认为 |
| homeButton |
| 是否显示 Home(回到初始视角)按钮(默认为 |
| infoBox |
| 是否显示实体信息框(点击实体后弹出)(默认为 |
| sceneModePicker |
| 是否显示 3D/2D/哥伦布模式切换按钮(默认为 |
| selectionIndicator |
| 是否显示选中实体的高亮指示器(默认为 |
| timeline |
| 是否显示时间轴(默认为 |
| navigationHelpButton |
| 是否显示右上角帮助按钮(默认为 |
| showCompass |
| 是否显示指南针(默认为 |
| showFrameRate |
| 是否显示帧率(FPS)(默认为 |
| tiandituConfig |
| 天地图服务配置(可选) |
| ├─ token |
| 天地图 Token(必填) |
| ├─ mapServer |
| 是否使用天地图影像服务(默认 |
| └─ national |
| 是否使用国界线图层(默认 |
| arcGisConfig |
| 是否启用 ArcGIS 地图服务(默认 |
| amapConfig |
| 高德地图服务配置(可选) |
| ├─ mapServer |
| 是否使用高德影像服务(默认 |
| └─ annotation |
| 是否使用高德注记图层(默认 |
| tencentConfig |
| 腾讯地图服务配置(可选) |
| ├─ mapServer |
| 是否使用腾讯影像服务(默认 |
| ├─ annotation |
| 是否使用腾讯注记图层(默认 |
| └─ vector |
| 矢量图层类型:1 默认矢量,4 黑色矢量(默认 |
| skyBox |
| 天空盒贴图路径(6 张图) |
| customMouseControl |
| 自定义鼠标控制选项(可选) |
二、使用类开始封装基础功能
(一) 初始化代码
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);
})
}
}
1万+

被折叠的 条评论
为什么被折叠?



