news 2026/9/15 22:02:44

deck.gl widgets 示例应用完全指南:从零搭建带交互控件的可视化应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
deck.gl widgets 示例应用完全指南:从零搭建带交互控件的可视化应用

deck.gl widgets 示例应用完全指南:从零搭建带交互控件的可视化应用

【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl

本文基于 deck.gl 仓库中的 widgets 示例应用 展开,该示例使用 Vite 作为构建与开发服务器,通过 6 个独立 Demo 系统演示了 deck.gl widgets 模块(@deck.gl/widgets)的用法。读完本文,你将掌握如何安装依赖、启动开发服务器、构建生产产物,并理解 Zoom、Compass、Info、Popup、ContextMenu 等 20 余个内置控件的真实配置方式,以及它们如何在纯 JavaScript 与 React 两种 API 下组织使用。

示例应用概览与目录结构

该示例位于仓库的 test/apps/widgets 目录,是一个用于验证与演示 widgets 模块能力的独立 Vite 应用。整个目录包含以下文件:

文件作用
README.md应用说明与使用命令
package.json依赖声明与 npm scripts
index.html入口页,列出 6 个 Demo 的跳转链接
geospatial.ts / geospatial.html纯 JS 地理空间场景(MapView)
multiview.ts / multiview.html纯 JS 多视图分割场景(SplitterWidget)
infovis.ts / infovis.html纯 JS 信息可视化场景(Orbit/Orthographic)
react-geospatial.tsx / react-geospatial.htmlReact 地理空间场景
react-multiview.tsx / react-multiview.htmlReact 多视图分割场景
react-infovis.tsx / react-infovis.htmlReact 信息可视化场景

入口 index.html 是一个纯链接导航页,在<main>中列出了六个 Demo 的入口:Pure JS - Geospatial、Pure JS - Multiview、Pure JS - Infovis,以及对应的三个 React 版本。这 6 个 Demo 分别对应「地图地理空间」「多视图分割」「非地理信息可视化」三大典型使用场景,且每种场景都同时给出纯 JS 与 React 两种写法,便于对照学习。

快速开始:安装、启动与构建

按 README.md 中的说明,该应用使用 Vite 进行打包与本地伺服。安装依赖:

npm install # 或者使用 yarn yarn

项目脚本定义在 package.json 中:

命令作用
npm start开发模式:启动 Vite 开发服务器并自动打开浏览器(vite --open),支持热更新
npm start-local以仓库根目录的 vite.config.local.mjs 作为配置启动(vite --config ../vite.config.local.mjs),用于以本地源码方式调试 deck.gl 模块
npm run build生产构建:生成最终 bundle 并写入磁盘(vite build

其中start-local是 deck.gl 仓库开发者常用的一条命令——它通过指向仓库根目录的本地 Vite 配置,将@deck.gl/*模块解析到本地源码(modules 目录),从而无需发布即可联调核心库改动。普通使用者直接使用npm start即可。

依赖分析

package.json 中的依赖揭示了 widgets 示例的构成:

  • dependencies@deck.gl/core@deck.gl/layers@deck.gl/widgets(版本^9.3.0,本仓库当前源码版本为 9.4.0-beta.4)。核心渲染依赖 core,图层使用 layers,控件体系来自 widgets;
  • devDependenciesvite^7.3.1),仅用于构建与开发服务器。

注意type: "module"声明,应用以 ESM 方式运行,示例源码均为.ts/.tsx,可直接被 Vite 原生编译。

场景一:地理空间应用(Geospatial)

geospatial.ts 构建了一个以伦敦(经纬度[-0.45, 51.47],zoom 4,pitch 30)为中心的地图应用,叠加了 WMS 底图、国家边界、机场点与飞行弧线四类图层:

const INITIAL_VIEW_STATE = { latitude: 51.47, longitude: 0.45, zoom: 4, bearing: 0, pitch: 30 }; const deck = new Deck({ parent: document.getElementById('map') as HTMLDivElement, views: new MapView({repeat: true}), // 平铺重复地图 initialViewState: INITIAL_VIEW_STATE, controller: true, layers: getLayers(), widgets: [ /* 见下文 */ ] });

图层数据来自 Natural Earth 公开 GeoJSON(国家边界与机场点),其中机场层启用了pickableautoHighlight,并结合 DataFilterExtension 的getFilterValue/filterRange按机场等级(scalerank)做数据过滤,为后续 Timeline 控件联动提供基础。

widgets 数组:一次性装配 16 个控件

该 Demo 在Deck构造参数的widgets数组中一次性注册了 16 个控件,是 widgets 模块能力的集中展示:

widgets: [ new _GeocoderWidget({geocoder: 'coordinates', _geolocation: true}), new ZoomWidget(), new CompassWidget(), new FullscreenWidget(), new ScreenshotWidget(), new ResetViewWidget(), new LoadingWidget(), new _ScaleWidget({placement: 'bottom-right'}), new ThemeWidget(), new ContextMenuWidget({ /* ... */ }), new InfoWidget({mode: 'hover', getTooltip, arrow: 10, offset: 10}), new PopupWidget({ /* ... */ }), new _TimelineWidget({ /* ... */ }), new _StatsWidget({type: 'deck'}), new IconWidget({ /* ... */ }), new ToggleWidget({ /* ... */ }), new SelectorWidget({ /* ... */ }) ]

对照 @deck.gl/widgets 的导出清单,这些控件可分为几类:

  • 导航类ZoomWidget(缩放)、CompassWidget(指南针/方位)、ResetViewWidget(重置视角)、GimbalWidget(三维旋转指示,见 infovis 场景);
  • 地理空间类_GeocoderWidget(地理编码搜索)、_ScaleWidget(比例尺);
  • 视图类FullscreenWidget(全屏)、_SplitterWidget(视图分割,见 multiview 场景);
  • 信息类InfoWidget(悬停/点击信息浮层)、PopupWidget(固定位置弹窗)、ContextMenuWidget(右键菜单)、ScrollbarWidget(滚动条);
  • 控制类IconWidget(自定义图标按钮)、ToggleWidget(开关按钮)、SelectorWidget(选项切换器)、_TimelineWidget(时间轴播放器);
  • 工具类ScreenshotWidget(截图导出)、ThemeWidget(明暗主题切换)、LoadingWidget(加载指示)、_StatsWidget(渲染统计)。

控件参数详解

ContextMenuWidget —— 按拾取对象生成右键菜单getMenuItems接收PickingInfo,当拾取到机场点时动态生成菜单项:

new ContextMenuWidget({ getMenuItems: (info: PickingInfo) => { const name = info.layer?.id === 'airports' && info.object?.properties.name; return ( name && [ {label: `Airport: ${name}`}, {value: 'open', label: 'Open in new tab'}, {value: 'favorite', label: 'Set as favorite'}, {value: 'filter', label: 'Exclude from filter'} ] ); }, onMenuItemSelected: console.log })

onMenuItemSelected接收用户点击的菜单项 value,示例中直接打印到控制台,实际应用中可在此执行打开链接、收藏、过滤等业务逻辑。

InfoWidget —— 悬停/点击信息浮层。通过mode: 'hover'指定悬停模式(也可用'click'),getTooltip(info, widget)返回浮层内容与定位:

new InfoWidget({mode: 'hover', getTooltip, arrow: 10, offset: 10}) function getTooltip(info: PickingInfo, widget: InfoWidget) { if (!info.object || info.layer?.id !== 'airports') { return null; // 未命中机场点时返回 null,不显示浮层 } let text: string; switch (widget.props.mode) { case 'hover': text = `${info.object.properties.name} (${info.object.properties.abbrev})`; break; case 'click': text = `${info.object.properties.name} (${info.object.properties.abbrev})\n...`; break; } return { position: info.object.geometry.coordinates, // 浮层锚定在地理坐标处 text, style: {minWidth: '200px'} }; }

arrow控制箭头尺寸(像素)、offset控制浮层与锚点的偏移距离。

PopupWidget —— 固定内容弹窗。可指定世界坐标positionmarker(自定义锚点 DOM 元素,示例用createPin()生成 SVG 图钉)、placement: 'top'offset: 20closeOnClickOutside: true

new PopupWidget({ position: [-5, 52], marker: {element: createPin()}, placement: 'top', offset: 20, content: `I'm here!`, closeOnClickOutside: true })

TimelineWidget —— 时间轴与数据联动。这是示例中最具交互价值的控件:timeRange: [2, 9]定义时间区间,step: 1为步长,playInterval: 500为播放间隔(毫秒);onTimeChange回调中通过deck.setProps重设图层的数据过滤区间,实现随时间轴播放动态过滤机场点的效果:

new _TimelineWidget({ _container: document.getElementById('controls') as HTMLDivElement, timeRange: [2, 9], step: 1, playInterval: 500, onTimeChange: time => deck.setProps({ layers: getLayers([2, time]) // 更新 filterRange 上界 }) })

IconWidget / ToggleWidget / SelectorWidget —— 自定义按钮组。三者均支持placement('top-right' 等)、SVG data URL 图标与labelToggleWidget额外提供onIcon/onLabel/onColor定义激活态,onChange(checked)回调开关事件;SelectorWidget通过initialValueoptions数组提供选项切换,示例给出了单视图/水平分割/垂直分割三选项,onChange打印当前值。

场景二:信息可视化(Infovis)

infovis.ts 展示了非地理坐标系下的控件应用:窗口左半为OrbitView(轨道视角),右半为OrthographicView(正射视角),共享一个 500 个随机三维散点的ScatterplotLayer

new Deck({ views: [ new OrbitView({id: 'orbit-view', x: 0, width: '50%', controller: true}), new OrthographicView({ id: 'ortho-view', x: '50%', width: '50%', controller: {maxBounds: [[-50, -50, -50], [50, 50, 50]]} // 限制平移边界 }) ], initialViewState: INITIAL_VIEW_STATE, layers: [ /* ScatterplotLayer */ ], widgets: [ new FullscreenWidget(), new GimbalWidget(), new ResetViewWidget({id: 'reset-orbit', viewId: 'orbit-view', placement: 'top-right'}), new ScrollbarWidget({ id: 'scroll-orbit', viewId: 'orbit-view', orientation: 'horizontal', contentBounds: [[-50, -50], [50, 50]] }), new ResetViewWidget({id: 'reset-ortho', viewId: 'ortho-view', placement: 'top-right'}), new ZoomWidget({viewId: 'ortho-view'}), new ThemeWidget({darkModeTheme: DarkTheme, lightModeTheme: LightTheme}), new InfoWidget({viewId: 'ortho-view', mode: 'hover', getTooltip: ({object}) => (object ? 'point' : null)}), new ScrollbarWidget({id: 'scrollbar-v', viewId: 'ortho-view', placement: 'bottom-right', orientation: 'vertical'}), new ScrollbarWidget({id: 'scrollbar-h', viewId: 'ortho-view', placement: 'bottom-right', orientation: 'horizontal'}) ] })

该场景引入了两个关键概念:

  1. viewId 绑定:在多视图场景下,控件通过viewId属性精确绑定到某个视图——例如两个ResetViewWidget分别用viewId: 'orbit-view'viewId: 'ortho-view'独立重置各自视角,ZoomWidgetInfoWidget也只作用于正射视图;
  2. ScrollbarWidget 滚动条orientation指定水平/垂直方向,contentBounds定义内容世界的坐标范围,配合placement(如 'bottom-right')放置;代码中还注释展示了decorations装饰(contentBounds + color + title)的用法。

此外GimbalWidget用于显示并操控轨道视角的旋转姿态,是三维信息可视化(点云、模型浏览)中的常用控件。

场景三:多视图分割(Multiview)

multiview.ts 是_SplitterWidget的用法示范:通过一个viewLayout描述树状视图布局——外层水平分割出左右两区,右区再垂直分割为上、下两个MapView,形成「左 + 右上 + 右下」的联动地图:

const VIEW_LAYOUT: SplitterWidgetProps['viewLayout'] = { orientation: 'horizontal', views: [ new MapView({id: 'left', controller: true}), { orientation: 'vertical', views: [ new MapView({id: 'right-top', controller: true}), new MapView({id: 'right-bottom', controller: true}) ] } ] }; new Deck({ initialViewState: INITIAL_VIEW_STATE, layers: LAYERS, widgets: [new SplitterWidget({viewLayout: VIEW_LAYOUT})] });

SplitterWidget会自动从viewLayout编译出实际的 view 组合(对应 view-layout/build-views-from-view-layout.ts 的实现),并渲染可拖拽的分割条;三个子视图复用同一份 GeoJSON/Arc 图层数据,但可独立平移缩放,适合多视角对比类应用(如左右眼立体、上下游对比)。

Pure JS 与 React 双 API 对照

三个场景均提供 React 版本(react-geospatial.tsxreact-infovis.tsxreact-multiview.tsx),二者差异体现在 API 形态上:

1. 导入来源不同。纯 JS 从@deck.gl/core导入Deck,从@deck.gl/widgets导入控件;React 版从@deck.gl/react导入DeckGL与控件。这是因为 @deck.gl/react 重新导出了 widgets 的全部控件,并额外提供useWidgetHook 与各控件 props 类型。示例中 React 版还显式使用createRoot(...).render(<App />)挂载应用。

2. 声明方式不同。纯 JS 用new Deck({...})widgets数组声明;React 版将控件作为<DeckGL>的子组件 JSX 声明,例如:

<DeckGL views={new MapView({repeat: true})} initialViewState={INITIAL_VIEW_STATE} controller layers={layers}> <_GeocoderWidget geocoder="coordinates" _geolocation /> <ZoomWidget /> <CompassWidget /> {/* ... */} </DeckGL>

3. 状态管理方式不同。React 版利用组件化优势管理动态状态:react-geospatial.tsxuseState(() => getLayers())持有图层,_TimelineWidgetonTimeChange中调用setLayers(getLayers([2, time]))触发重渲染;react-infovis.tsxviewState+onViewStateChange受控管理双视图状态:

const onViewStateChange = useCallback(({viewId, viewState}) => { setViewState(curr => ({...curr, [viewId]: viewState})); }, []);

react-multiview.tsx则先用useState维护一个单一MapView,配合SplitterWidget在交互后切换到完整布局。

主题、样式与实验性控件说明

所有 Demo 都在模块代码中引入了默认样式表:

import '@deck.gl/widgets/stylesheet.css';

该样式表由 stylesheet.css 经 PostCSS 构建后发布为@deck.gl/widgets/stylesheet.css(见 modules/widgets/package.json 的 build 脚本)。不引入它将导致控件无样式渲染。

主题切换ThemeWidget支持明暗主题。默认使用内置主题;也可如 infovis 示例所示显式传入自定义主题:

new ThemeWidget({darkModeTheme: DarkTheme, lightModeTheme: LightTheme})

DarkTheme/LightTheme(以及LightGlassTheme/DarkGlassTheme)由 themes.ts 导出,类型为DeckWidgetTheme,开发者可据此自定义配色。

下划线前缀(实验性):源码导出清单中_GeocoderWidget_ScaleWidget_SplitterWidget_TimelineWidget_StatsWidget等均带下划线前缀(见 modules/widgets/src/index.ts 中export {X as _X}的写法),表示其 API 尚未稳定、后续版本可能调整,生产环境使用需谨慎。该清单还导出了_ButtonGroup_IconButton_Tooltip_DropdownMenu_RangeInput等基于 Preact 的实验性基础组件,以及_GoogleGeocoder_MapboxGeocoder_OpenCageGeocoder_CoordinatesGeocoder_CurrentLocationGeocoder等实验性地理编码器(源码位于 modules/widgets/src/lib/geocode)。

底层实现要点:@deck.gl/widgets 依赖preact@floating-ui/dom(见 package.json),前者负责控件 UI 渲染,后者负责浮层(Tooltip/Popup/Dropdown)的定位计算;其 peerDependencies 要求@deck.gl/core@luma.gl/core版本匹配,构建示例时需保证依赖版本一致。

延伸阅读

  • 控件 API 完整参考:docs/api-reference/widgets(含 24 个控件文档)
  • 其他官方示例:本仓库 examples/website 下各展示页同样大量使用 widgets
  • React 集成方式:docs/get-started/using-with-react.md
  • 更多渲染回归测试:本示例属于 test/apps 系列,仓库另有widgets-layerlist等 widgets 相关测试应用可供参考

【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 22:01:32

汕头建站模板搭建避坑指南:3种方案对比

汕头建站模板搭建避坑指南:3种方案对比 别再被那些一眼假、加载慢、还容易出bug的模板网站坑了。很多汕头老板花了几千块买模板,结果上线三个月,客户问为什么网站打不开,SEO排名还掉到首页外。…

作者头像 李华
网站建设 2026/9/15 21:56:20

Qt/C++贪吃蛇游戏开发:从游戏循环到碰撞检测的工程实践

简介&#xff1a;基于Qt与C的贪吃蛇游戏毕业设计完整方案&#xff0c;适合计算机相关专业学生完成课程设计或毕业设计时参考。压缩包内含可编译运行的源码、毕业设计论文、任务书以及答辩PPT&#xff0c;从需求分析、总体设计到编码实现均有对应文档支撑&#xff1b;论文中对技…

作者头像 李华
网站建设 2026/9/15 21:55:30

拒绝丑模板!汕头建站模板搭建的3个最佳实践避坑指南

拒绝丑模板!汕头建站模板搭建的3个最佳实践避坑指南 别再盯着那些一眼假、加载慢、还改不动的免费模板发愁了。很多老板觉得建站就是下个模板换个Logo,结果上线后客户直摇头,说像十年前网页,更别提转化了。这就是典型的“模板网站太丑不够用”。…

作者头像 李华
网站建设 2026/9/15 21:50:52

Linux跑Windows应用中文乱码?WinBoat字体修复指南

Linux跑Windows应用中文乱码&#xff1f;WinBoat字体修复指南 【免费下载链接】winboat Run Windows apps on &#x1f427; Linux with ✨ seamless integration 项目地址: https://gitcode.com/GitHub_Trending/wi/winboat 在 WinBoat 里打开记事本输入中文&#xff0…

作者头像 李华