先说明一下,个人项目里去掉 Cesium 的 Logo 属于很常见的需求,官方文档其实也留了口子。但很多新手一搜"去掉版权Logo",搜到的都是直接改源码删代码那种野路子,真正规范的做法反而被淹没了。这篇就从头到尾把 Cesium 的 Logo 机制讲透,顺带把 Vue3 里集成 Cesium 的坑也一起排掉。
1. Cesium 版权 Logo 的显示逻辑与去除原理
1.1 先搞清楚 Logo 是怎么渲染出来的
Cesium 初始化的时候,会往页面容器里塞一堆 DOM 元素,Logo 就是其中之一。很多人的第一反应是"我能不能用 CSS 把它藏掉",答案是可以,但这不是最干净的做法。
Cesium 的 Logo 在源码里走的是CreditContainer这条线,它内部维护了一个CreditDisplay类,专门负责把各种版权信息渲染到屏幕上。我们平时看到的左下角那行字,本质上是 Cesium 在初始化时根据传入的creditContainer参数决定往哪里挂载:
const viewer = new Cesium.Viewer("cesiumContainer", { creditContainer: document.createElement("div") // 传一个空白容器 });一旦你手动指定了creditContainer,Cesium 就会把版权信息渲染到你指定的这个元素里,默认的左下角容器就不再生效。你只要不把这个元素挂到 DOM 上,Logo 就自然消失了。这个方案完全不需要改源码,升级 Cesium 版本也不会被覆盖,是我在正式项目里最推荐的一种做法。
1.2 为什么推荐这种方式而不是直接删源码
网上流传比较广的办法是去node_modules里找到 Cesium 的源码文件,把渲染 Logo 那几行代码删掉。这种做法在本地开发时候确实有效,但一打包、一升级,问题就来了:
node_modules是依赖目录,重新npm install之后你改的东西全没了- Cesium 升级版本时,你很难记住自己到底改过哪里,排查问题非常痛苦
- 团队其他成员拉代码后,并不会自动同步你本地的
node_modules修改
所以只要不是被逼到没办法,我都建议避开"改源码"这条路。creditContainer这个参数就是官方留给开发者的合法出口,用它在逻辑上最干净。
1.3 确保已经先配置了 Cesium Ion 的 Token
去掉 Logo 之前必须确认一件事:你的 Cesium 是不是一个合法可用的状态。如果压根没配 Token,Cesium 会频繁弹窗提示你要去申请,这种情况下即使你去掉了 Logo,后续加载影像、地形也会遇到问题。
Cesium.Ion.defaultAccessToken = "你的token";登录 Cesium Ion 官网,注册账号后创建一个 Token 就行。这一步不复杂,但确实是很多项目里"地图不显示""没有影像"这类问题的根源,我实际排查过不少都是这个原因。Token 配置好了再去处理 Logo 的事,顺序不能反。
2. Vue3 项目里集成 Cesium 的基础搭建
2.1 环境准备:Vue3 + Vite 项目的初始化
现在做 Vue3 项目,大多用的是 Vite 而不是老牌的 vue-cli,打包速度快、配置也直观。初始化命令很简单:
npm create vite@latest cesium-demo -- --template vue依赖装好后,再把 Cesium 装进来:
npm install cesium装完后不要去动node_modules里的东西。Vite 项目需要在vite.config.js里做一些配置,Cesium 才能正确打包。这里给一个我常用的配置模板:
import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import path from 'node:path'; export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': path.resolve(__dirname, './src'), 'cesium': path.resolve(__dirname, './node_modules/cesium/Source'), } }, define: { CESIUM_BASE_URL: JSON.stringify('/cesium'), }, });CESIUM_BASE_URL这个变量很关键,Cesium 运行时需要加载一堆静态资源,比如Workers、Assets、Widgets这些目录,如果你不定义它,资源请求会 404,直接导致地球出不来。
2.2 静态资源拷贝:让 Cesium 正常加载工作线程
配好了CESIUM_BASE_URL,还得保证这路径下真的存在对应的静态资源。在public目录里建一个cesium文件夹,把node_modules/cesium/Build/Cesium/下的Workers和Assets两个目录复制过去。
Vite 的public目录里的内容会原样拷贝到打包后的根路径下,所以只要CESIUM_BASE_URL配成/cesium,请求自然就能命中。
一步步手动复制比较笨,可以在vite.config.js里加一个插件自动处理,但为了新手好理解,我建议前期先手动复制一次,跑通了再考虑自动化。脑袋里要有一张图:请求/cesium/Workers/createGeometry.js时,实际文件位置是public/cesium/Workers/createGeometry.js。
2.3 页面组件的编写与 Cesium 实例化
环境配好之后,写一个最简单的 Vue3 组件来挂载地球。这一步我通常直接用一个普通的<div>作为容器,然后onMounted里启动 Viewer:
<template> <div id="cesiumContainer" class="cesium-container"></div> </template> <script setup> import { onMounted, onBeforeUnmount } from 'vue'; import * as Cesium from 'cesium'; let viewer = null; onMounted(() => { viewer = new Cesium.Viewer('cesiumContainer', { timeline: false, animation: false, baseLayerPicker: false, geocoder: false, homeButton: false, sceneModePicker: false, navigationHelpButton: false, fullscreenButton: false, infoBox: false, selectionIndicator: false, }); }); onBeforeUnmount(() => { if (viewer) { viewer.destroy(); viewer = null; } }); </script> <style scoped> .cesium-container { width: 100%; height: 100vh; position: relative; } </style>注意onBeforeUnmount里的destroy,很多新人会在路由切换后遇到地图崩溃、内存泄漏的问题,很大程度上就是没有正确销毁 Viewer。这一点后面在"3D 地球滚动崩溃"相关常见问题里我会再展开说。
2.4 初次运行可能遇到的资源加载报错
按上面的步骤走完,大概率能正常看到地球。如果页面一片灰、控制台一堆 404,优先排查顺序是:
CESIUM_BASE_URL是否被正确读取public/cesium下是否有Workers和Assets目录- 请求路径和实际文件路径是不是一一对应
我自己碰到最多的情况是路径配错,比如请求的是/Assets/xxx,但实际放到了/cesium/Assets/xxx,这种情况下哪怕只差一层目录,Cesium 也会直接罢工。
3. 去除左下角 Cesium 版权 Logo 的几种实用方案
3.1 方案一:官方推荐的 creditContainer 方式
老规矩,先推荐正路。
const creditContainer = document.createElement('div'); const viewer = new Cesium.Viewer('cesiumContainer', { creditContainer: creditContainer, });因为creditContainer是自定义的 DOM 元素,Cesium 会把 Logo 渲染到这个元素里,但这个元素并不在你页面的可见区域里,用户自然看不到。这种做法的好处:
- 没有修改任何源码,Cesium 本身的功能完全不受影响
- 升级 Cesium 版本时,这一行配置不会失效
- 后面如果想要把 Logo 显示在别的位置,也完全可以拿这个容器做文章
我单独提一句:官方文档里写的是"默认值会创建一个新的 div 并添加到 widget 容器内",这句话的意思就是你不传这个参数时,Cesium 会自动帮你创建一个。我们自己创建,就是截胡了它的这个过程。
3.2 方案二:关闭默认的 creditDisplay 展示
还有另一个配置项叫creditDisplay,它是Viewer内部服务CreditDisplay的实例。如果你想在运行时动态控制版权信息的显隐,可以通过访问viewer.creditDisplay来操作:
viewer.creditDisplay._creditsContainer.style.display = 'none';注意下划线_creditsContainer属于"私有成员",理论上框架不保证它永远存在。所有带下划线开头的属性,都是官方默认不推荐外部去动的。如果你只是为了快速验证效果,临时用用没问题,但我不推荐写进正式代码里,因为 Cesium 升级后这个属性是有可能被改掉的。
3.3 方案三:CSS 层面隐藏(不推荐但不失为一个思路)
很多文章里提到用 CSS 去隐藏:
.cesium-viewer-bottom { display: none; }这个 class 在默认情况下确实能瞄中左下角的版权区域。前面也说了能行,但从合规角度来说,这种方式属于"视觉隐藏",Cesium 的版权信息实际还在页面上渲染着。而且.cesium-viewer-bottom这个类名如果被 Cesium 内部其他模块复用,样式可能会互相影响。
我不太推荐,但把它列出来是因为你在看别人代码时可能会遇到,知道了总比一头雾水强。
3.4 方案四:基于 Cesium 源码的自定义打包
如果你确实被逼到非要彻底改源码不可,那我建议不要直接改node_modules,而是去 fork 一份 Cesium 源码,改完后自己打包引用。
具体来说,需要关注的源码位置在packages/widgets/Source/CreditDisplay/CreditDisplay.js里。这里面有一段代码负责把 Logo 加到creditContainer中,逻辑大致是创建了一个div,把 Cesium 的版权字符串塞进去。你可以自己去掉这段逻辑,再把整个项目重新打包。
这条路成本不低,但适合两种人:一是对 Cesium 二次开发有长期规划、想深入理解源码的团队;二是用的 Cesium 版本比较旧、官方可能已经不更新支持了。普通项目确实没必要搞这么重。
3.5 四种方案对比与选型建议
| 方案 | 难易程度 | 是否改源码 | 升级兼容性 | 推荐度 |
|---|---|---|---|---|
| creditContainer | 低 | 否 | 好 | 高 |
| 动态操作 creditDisplay | 低 | 否 | 中等 | 中 |
| CSS 隐藏 | 低 | 否 | 中等 | 低 |
| 源码自定义打包 | 高 | 是 | 定制 | 低 |
我在实际项目里优先用的是第一种,简单、干净、不会因为版本升级而翻车。
4. 深度玩法:修改源码彻底移出 Logo
4.1 找出生成 Logo 的具体代码位置
这一节是给那些确确实实需要改源码的人准备的。如果你用的是常规方式安装的 Cesium,那么关键文件通常在:
node_modules/cesium/Source/Widgets/CreditDisplay/CreditDisplay.js如果你用的是打包后的版本,那可能要去:
node_modules/cesium/Build/Cesium/Cesium.js那么在压缩后的Cesium.js里找字符串会比较痛苦,你会看到很多代码挤成一行或几行。建议先去搜CreditDisplay.prototype这样的关键字,定位到类定义,再从里面找logo、creditContainer、CreditLogo之类的标识。
4.2 定制打包时的注意事项
如果你选择 fork 源码自己打包,需要额外注意CreditDisplay内部不只是挂了一个 Logo,它还管理着所有数据源、图层的版权信息。如果盲目删除,可能会导致某些图层的数据源版权信息一并失效,比如加载了某些需要标明出处的数据服务,这在合规上是有风险的。
所以正确的改法不是删整个模块,而是精准地去掉"默认 Logo 渲染"的那一段,保留其他 credit 展示逻辑。具体到代码层面,通常是找到类似这样的语句:
var logo = document.createElement('div'); logo.className = 'cesium-credit-logoContainer';把这段创建和插入 DOM 的逻辑注释掉或者删除即可。但注释之前先确认:这段逻辑是否同时负责初始化一些必要的事件绑定,如果有,那就要连事件绑定一起评估。
4.3 什么场景下才真正需要改源码
我提供一个判断标准:只有当你的项目既不能用creditContainer方案,又不能接受页面上有任何 CSS hack 痕迹时,才需要考虑改源码。这种需求多见于有严格 UI 设计标准的可视化大屏项目,或者客户当场打开开发者工具检查 DOM 结构的情况。
在这类场景下,改源码是唯一能彻底"消灭"Logo 的方式,因为它是从渲染源头断掉的。
5. 实际项目里的完整集成与优化
5.1 封装一个可复用的 Cesium 地图组件
在 Vue3 项目里,Cesium 的地图不会只在一个页面用到。我通常会把它封装成组件,暴露几个常用配置项,比如是否显示 Logo、是否允许切换底图、视角初始位置等。这样在不同页面里,只需要传不同 props 就能复用同一套逻辑。
组件的大致结构长这样:
<template> <div ref="cesiumRef" class="cesium-container"></div> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue'; import * as Cesium from 'cesium'; const props = defineProps({ showLogo: { type: Boolean, default: false, }, initialView: { type: Object, default: () => ({ longitude: 116.39, latitude: 39.9, height: 10000000, }), }, }); const cesiumRef = ref(null); let viewer = null; onMounted(() => { const creditContainer = props.showLogo ? undefined : document.createElement('div'); viewer = new Cesium.Viewer(cesiumRef.value, { creditContainer, animation: false, timeline: false, baseLayerPicker: false, geocoder: false, homeButton: false, sceneModePicker: false, navigationHelpButton: false, fullscreenButton: false, }); viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees( props.initialView.longitude, props.initialView.latitude, props.initialView.height ), }); }); onBeforeUnmount(() => { viewer.destroy(); }); </script>这个组件在多个业务页面里直接引用,只需通过showLogo控制版权信息的显隐,非常方便。
5.2 影像底图与地形加载
Cesium 默认使用的是 Ion 的天地图影像,速度在国内一般。如果你的业务主要面向国内用户,我更推荐直接接入高德或天地图影像。配置方法是在 Viewer 创建时指定imageryProvider:
import { UrlTemplateImageryProvider } from 'cesium'; const imageryProvider = new UrlTemplateImageryProvider({ url: 'https://your-tile-server/{z}/{x}/{y}.png', maximumLevel: 18, }); viewer = new Cesium.Viewer('cesiumContainer', { imageryProvider, baseLayerPicker: false, });底图服务商每家提供的瓦片格式略有差异,接入前务必读一下对应文档。像天地图需要额外的 key,高德则对subdomains有要求。如果搞不清,可以先不换,等 Logo 去掉了,再慢慢调底图。
5.3 动态光照、雷达扫描这类效果的加分项
Cesium 里做动态光照,最常见的做法是调整viewer.scene.light或者给Material设置随时间变化的属性。雷达扫描效果则常用PolylineCollection配合自定义 Material 来实现动态波纹。
这些效果在概念上不难,但调试时会遇到不少麻烦。比如动态光照如果frameState的渲染循环没有正确触发,会发现灯光一直不变化;雷达波纹如果Material的czm_material定义不严谨,也会出现边界闪烁。我个人的建议是先把基础地球跑起来、Logo 处理干净,再在它上面叠加这类视觉效果,不然出了问题,你真不知道是 Cesium 底子没搭好还是效果代码的问题。
6. 常见问题排查:为什么我的 Cesium 表现异常
6.1 3D 地球滚动时崩溃
这是最近被问得比较多的一类问题。场景往往是:页面里嵌入了 Cesium 地球,然后用户滚动页面,或者页面内部有滚动容器,滚动几下浏览器标签页就卡死或者崩溃。
背后大概率是滚动事件把 Cesium 的渲染循环拖垮了。Cesium 渲染本身很吃 GPU 和 CPU,滚动会造成浏览器频繁触发重绘和合成,叠加起来就直接把页面拖崩。排查思路:
- 确认页面是否有不必要的全局滚动监听,有就针对性优化
- 查看有没有频繁调用
viewer.camera之类的操作导致每次滚动都触发重新渲染 - 用 Chrome 性能面板录制一段滚动操作,看是哪些函数占用了大部分时间
如果只是为了展示,可以考虑把 Cesium 场景放在一个固定高度且不参与页面滚动的容器里;如果要跟随页面滚动,那就要好好做一下requestAnimationFrame的节流了。
6.2 加载 3857 坐标系数据时出现偏移
这个坑是真的有点深,遇到的人也不少。3857 是 Web 墨卡托投影,本身是平面坐标,而 Cesium 默认是三维球面坐标,直接在球上叠加平面投影数据,必然会出现"飘"。
解决思路是尽量在数据源头转换成经纬度,或者通过GeoJSONDataSource加载时指定dataProjection参数,让 Cesium 知道你的数据是哪个投影来的,它内部会做转换。单纯依赖前端硬转并不现实,因为一个 GeoJSON 里可能包含大量要素,前端转的效率和精度都不理想。
6.3 Edge 浏览器下按钮无法点击
有朋友反馈 Vue3 项目在 Edge 浏览器里出现"关闭不了右上角最小化按钮"这类怪异问题。这通常是 CSS 的层级、z-index或pointer-events设置导致的。Cesium 的容器默认会创建多个层级的 DOM 和 canvas,如果外层元素不小心给他加了transform或者filter,会形成新的层叠上下文,导致原有按钮被盖住。
检查方式是打开 Edge 的开发者工具,查看按钮元素的computed样式,重点看pointer-events和z-index,再逐层往上看是否有父元素影响了层级。这类问题跟 Cesium 本身没太大关系,而是混合了 UI 框架后常见的样式冲突。
7. 从入门到精通常犯的几个理解误区
7.1 误区一:Cesium 一定要用 npm 包手动配置
其实 Cesium 也有 CDN 方式的引入,直接<script>标签一样能用。但对于 Vue3 + Vite 这种工程化项目,npm 方式更利于依赖管理和版本锁定,也能更好地利用 Vite 的打包优化。手动拷贝静态资源那一步,只是前期配置成本,后面都用得上,谈不上麻烦。
7.2 误区二:去 Logo 就等于破解或违规
Cesium 的正规版权协议中,它本身提供了creditContainer这种合法隐藏 Logo 的机制,许多商业项目也在用。只要你没有滥用 Cesium 的资源、没有抹掉其他不可删除的版权信息,使用它提供的官方配置隐藏默认 Logo 是允许的。很多人的顾虑是因为看到太多"改源码"的野路子,误以为去 Logo 一定违规,这个理解需要修正。
7.3 误区三:Vue2 经验直接搬到 Vue3 没问题
Vue2 和 Vue3 在响应式原理、组件通信、生命周期上差异很大。最典型的例子是this的引用方式完全不同,Vue3 组合式 API 里已经没有this指向组件实例的习惯用法了。如果你之前习惯在mounted里写一堆初始化逻辑,转 Vue3 后应该把这些逻辑分散到setup、onMounted等合适的位置里,不要一把梭。
Cesium 在 Vue2 项目里的集成方式,放到 Vue3 里往往也不能直接复制。组件实例的生命周期钩子名字变了,$refs 的获取时机也变了,很容易出问题。
8. 功能扩展:除了去 Logo,Cesium 还能怎么玩
8.1 绘制矩形与热力图
绘制矩形在 Cesium 里主要用viewer.entities.add配合RectangleGraphics。给定西南角和东北角的经纬度,就能直接在地图上拉出一个面。热力图则稍微复杂,通常需要先用 ECharts 生成热力图 canvas,再把 canvas 作为Material贴在 Cesium 的Rectangle上,这样就能实现"热力覆盖面"的效果。想做成动态热力,可以在前端定时更新数据、重新生成 canvas,再刷新材质。
8.2 鹰眼图与模型节点操作
鹰眼也就是小地图导航,很多 GIS 项目都会要这个。实现思路并不复杂:创建一个小型的Viewer或者Scene,和主视图保持同步,camera.changed事件触发时,把小地图的相机位置同步过去。
模型节点操作就更有趣了,加载一个 3D Tiles 或者 glTF 模型后,你可以通过viewer.scene.getPickPosition获取鼠标点击位置,再通过模型的ModelInstanceCollection获取对应节点,就能实现"点哪个零件,高亮哪个零件"这种交互。这在城市孪生方向特别常见。
8.3 天空盒与 Unity 集成
Cesium 支持自定义SkyBox,你可以换成自己团队设计的天空贴图,让整个三维场景的氛围更统一。和 Unity 集成则属于 Cesium for Unity 这条独立产品线的范畴,它把 Cesium 的地球能力整合到 Unity 渲染引擎里。很多做城市孪生、数字孪生项目的团队都在走这条路。这种场景下,版权 Logo 的处理又会变得不同,因为渲染管线不再完全由 Cesium 控制。
8.4 面试场景里 Cesium 相关的考点
Cesium 面试题核心就集中在:坐标系转换、相机视角控制、实体与图元渲染、离屏渲染、性能优化、requestRenderMode这几类。把 Cesium 官方文档里的Camera、Entity、Primitive相关 API 吃透,再对这个项目里的集成经验做一个梳理,面 Cesium 岗基本没什么大问题。毕竟项目里能踩的坑你都踩过了,代码结构也完整,这本身就是浓缩的实战经验。
9. 一些踩坑记录与个人经验
9.1 项目里的 3D 地球总是崩?先看看内存
有时候页面看着没进行什么复杂操作,但地球时不时卡死,刷新后又好了。这种情况大多数是内存泄漏。Cesium 的Viewer如果创建后没有正确销毁,每次进页面都会创建一个新实例,而且旧实例的 GPU 资源喂一直占着。时间一长,页面内存飙升,3D 场景必然崩。
我自己的习惯是给组件用完后统一记日志,在开发环境里切页面时观察window.performance.memory有没有明显上涨。这个经验能帮你早点发现泄漏,别等现场客户反馈了才追着排查。
9.2 requestRenderMode 是性能优化的核心开关
如果你的项目并不需要地球持续转动或动画,开启requestRenderMode: true会极大降低 CPU 和 GPU 消耗。它表示只有在场景发生变化时才渲染新帧,否则静态画面就是"空转"。但要注意,如果同时开了动态光照、雷达扫描这类需要持续帧更新的效果,必须手动调用viewer.scene.requestRender()来让新帧重新绘制。
这个开关理解起来容易,用起来有点考功力,因为你要清楚"什么操作会触发新渲染,什么操作不会"。我一般会针对动态数据和静态场景分开处理,做一个专门的渲染调度模块。
ES6 模块化与 Cesium 的 import 时机
Cesium 对 ES Module 的支持已经挺成熟了,但依然有同学在import * as Cesium from 'cesium'之后就报各种 undefined。多数情况是版本与引入方式不一致,或者跟 Vite 的optimizeDeps处理产生了冲突。建议优先用 npm 最新稳定版本,遇到错误时先看版本号,而不是急着去改源码。
如果你是用 CDN 方式加载的,需要注意引入顺序,确保 Cesium 的全局变量已经挂在 window 上之后再执行自己的代码。这个顺序在很多老教程里被忽略,但实际项目里恰恰最容易出问题。
小技巧:利用监听器动态控制版权 Logo
最后分享个小技巧。如果你希望页面上有个开关,用户点了就显示 Logo,再点了就隐藏 Logo,可以直接操作creditDisplay的容器样式:
function toggleLogo(visible) { const container = viewer.creditDisplay._creditsContainer; if (container) { container.style.display = visible ? 'block' : 'none'; } }这个功能放在一个不起眼的工具栏里,演示给客户看的时候特别方便。虽然前面说了私有成员有兼容风险,但用在这种"辅助功能"里,问题不大。真要进了生产环境,记得优先考虑creditContainer方案。