拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CesiumJS 自定义 Widget 开发实战:如何扩展 Viewer 的 3 种界面组件

CesiumJS 自定义 Widget 开发实战如何扩展 Viewer 的 3 种界面组件【免费下载链接】cesiumAn open-source JavaScript library for world-class 3D globes and maps :earth_americas:项目地址: https://gitcode.com/GitHub_Trending/ce/cesium场景锚点你在做一个 GIS 监控平台客户突然提需求时间轴上方要加个轨迹回放控件右侧再叠一层数据源切换器。CesiumJS 的 Viewer 自带的 Animation、Timeline、HomeButton、SceneModePicker 都是现成的可一旦业务逻辑塞不进那几个盒子就卡住了。好消息是CesiumJS 的 Widget 体系完全可扩展。官方 Timeline、Geocoder 这些组件全部是同一套HTML 模板 ViewModel Knockout 绑定的三层结构。这篇文章把这套机制拆开讲清楚你该照着哪条路径写自己的组件。机制解剖Widget 体系的三层结构先看一眼官方 API 文档的目录所有 Widget 类的命名规律一目了然整个体系可以拆成三层。最底层是CesiumWidgetpackages/engine/Source/Widget/它只干一件事持有 Scene 和 canvas用requestAnimationFrame跑渲染循环销毁时清理全部资源。中间层是 packages/widgets/Source/ 下的一堆独立 widget每个目录一个组件Timeline、Geocoder、InfoBox、BaseLayerPicker。最上层是Viewerpackages/widgets/Source/Viewer/Viewer.js它按 options 把 widgets 装配进工具栏决定谁显示、谁隐藏。把 CesiumWidget 类比成汽车底盘每个 widget 是座椅模块Viewer 是决定装哪几个座椅的车身。模块不用知道底盘型号车身也不用关心座椅内部怎么缝线。组件内部不直接操作 DOM而是把状态挂成 ES5 属性 Knockout observable。核心胶水是一个只有两行有效逻辑的工具函数// subscribeAndEvaluate先用当前值触发一次回调再订阅后续变化 function subscribeAndEvaluate(owner, property, callback) { callback(owner[property]); return knockout.getObservable(owner, property).subscribe(callback); }它就在 packages/widgets/Source/subscribeAndEvaluate.jsViewer 里全屏按钮、VR 按钮的状态同步全是靠它。可复用模式模式一ViewModel 加 observable 属性替代直接操作 DOM一句话定义所有可变状态放进一个普通对象UI 和 Scene 都绑定同一个 observable 属性谁变了都自动跟随。const viewModel { visible: true, toggle() { this.visible !this.visible; }, }; ko.applyBindings(viewModel, container); const sub subscribeAndEvaluate(viewModel, visible, (v) { layer.show v; // 同步到 Scene 侧 }); // destroy 时调用 sub.dispose()什么时候用组件有任何开关、数值、文本类状态时。什么时候别用一次性动作如点击后飞一次不需要 observable直接写事件回调即可别为不存在的需求引入响应式。模式二Command 模式给 UI 操作加前置校验一句话定义把回调函数包成 Command自带canExecute门控和beforeExecute/afterExecute事件。const command createCommand(() { /* 真正执行 */ }); command.beforeExecute.addEventListener((info) { info.cancel true; // 拦截执行 }); command.canExecute false; // 灰态执行时抛错源码在 packages/widgets/Source/createCommand.js。需要多步操作、可能被外部拦截、要有禁用但不删除状态时用单纯一键动作用它属于杀鸡用牛刀。模式三事件订阅替代组件间直接引用一句话定义widget 之间不 import 对方只共同订阅 Scene / Clock 的公开事件比如scene.postRender、clock.onTick用EventHelper统一登记监听器。销毁时调一次eventHelper.removeAll()所有订阅一起释放。这招专治我改了 A 组件B 组件悄悄坏了的循环依赖。Viewer 的 inspector、性能监控等 mixin见 packages/widgets/Source/Viewer/都是这个路子每个 mixin 文件只管一类能力互相不知道对方存在。工程落地把新组件放进项目新组件的目录结构建议长这样widget 的 JS 和 CSS 同目录放依赖只指向 enginesrc/ ├── widgets/ │ ├── LayerSwitcher/ │ │ ├── LayerSwitcher.js │ │ └── LayerSwitcher.css │ └── PlaybackControl/ ├── viewModels/ │ └── playbackViewModel.js └── main.js模块划分原则有三条。第一main.js负责创建 Viewer 并挂载你的 widget它知道所有组件组件之间互不知道。第二widget 只 importcesium/engine的Event、JulianDate、destroyObject这类核心模块禁止 import 其他 widget 目录下的文件。第三CSS 类名全部加cesium-xxx-前缀做命名空间隔离避免污染全局。部署效果上你拿到的是一个可交互的三维地球界面控件悬浮在画面之上与 Scene 渲染互不阻塞避坑速查现象根因解法参考路径改了属性UI 不刷新observable 未注册或没 applyBindings用subscribeAndEvaluate或ko.applyBindingspackages/widgets/Source/subscribeAndEvaluate.js反复切换视图内存持续增长Knockout 订阅与事件监听未释放destroy 时sub.dispose()EventHelper 统一 removeAllpackages/engine/Source/Core/EventHelper.js⚠️ 按钮禁用后仍能执行canExecute没管好用createCommand统一管门控状态packages/widgets/Source/createCommand.js组件样式被全局 CSS 打穿类名冲突、无命名空间所有类名加cesium-xxx-前缀packages/widgets/Source/widgets.cssdestroy 后渲染循环还在跑requestAnimationFrame未取消统一走widget.destroy()别手动摘 canvaspackages/engine/Source/Widget/CesiumWidget.js组合拳建议写新组件前先把 ViewModel 骨架和 destroy 函数写出来功能可以后填资源释放路径不能后补。再给每个 widget 目录配一个 编码规范 要求的 options 参数校验这套东西就能长期扛住需求膨胀。延伸阅读packages/widgets/Source/官方全部 widget 源码每个目录就是一个完整组件样板Documentation/Contributors/CodingGuide/README.md编码规范命名与注释约定packages/sandcastle/Sandcastle 示例库大量组件交互的实际用法参考【免费下载链接】cesiumAn open-source JavaScript library for world-class 3D globes and maps :earth_americas:项目地址: https://gitcode.com/GitHub_Trending/ce/cesium创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门