OHIF Viewers 中基于 Shepherd.js 的引导式导览(Tours)配置完整指南
【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers
导读
本文面向 OHIF Viewers 平台的开发者和集成人员,系统讲解如何基于 Shepherd.js 在应用配置文件中定义、定制交互式引导导览(Tours),为使用者提供 step-by-step 的功能引导与新手引导(Onboarding)体验。读完本文,你将掌握ohif.tours定制项的完整数据结构(route、steps、tourOptions、floatingUIOptions),理解 Onboarding 组件的底层消费逻辑与默认定位中间件,并能够参照仓库内置的basicViewerTour范例快速落地自己的引导流程。
概述:Tours 能做什么
Tours 为 OHIF Viewer 提供分步引导能力,让使用者逐步了解某个模式(Mode)、扩展(Extension)或查看器(Viewer)中的特定功能。每条导览(Tour)与一条应用路由(route)绑定,由若干步骤(steps)组成,每个步骤引导用户在查看器中完成一次具体的交互——例如缩放图像、平移、调节窗宽窗位、使用测量工具等。
在 OHIF 中,Tours 基于 Shepherd.js 这一 JavaScript 导览构建库实现。整个链路由三部分协作完成:
- 定制数据:通过 Customization Service 的
ohif.tours键注册导览定义; - 消费组件:
platform/ui-next中的Onboarding组件读取定制并按路由触发导览(见 Onboarding.tsx); - 布局挂载:
extensions/default的查看器布局把导览数据注入 Onboarding 组件(见 ViewerLayout/index.tsx)。
在配置文件中添加一个 Tour
OHIF 3.10 及之后版本中,导览不再直接定义在window.config.tours,而是统一通过 Customization Service 的ohif.tours键注册,并使用$set更新模式写入。以下是最小可运行示例:
window.config = { customizationService: { 'ohif.tours': { $set: [ { id: 'basicViewerTour', route: '/viewer', steps: [ { id: 'zoom', title: 'Zooming In and Out', text: 'You can zoom the images using the right click.', attachTo: { element: '.viewport-element', on: 'left', }, advanceOn: { selector: '.cornerstone-viewport-element', event: 'CORNERSTONE_TOOLS_MOUSE_UP', }, }, ], }, ], }, }, };当用户导航到/viewer路由时,如果该导览此前未被展示过,Onboarding组件会自动构建并启动这条导览(详见下文“Onboarding 组件的底层实现”)。
参数详解
tours数组
ohif.tours的$set值是一个数组,数组中的每一项定义一条针对特定路由的导览,包含以下属性:
id:导览的唯一标识符。用于追踪该导览是否已被展示(存储于localStorage的shownTours键中)。route:导览适用的应用路由。用户导航到该路由时,若导览此前未展示过,将自动触发。steps:步骤数组,定义导览中的各个引导元素。每个步骤对应一个 UI 元素并引导用户完成交互。tourOptions:对象,用于配置导览的整体行为,例如是否使用模态遮罩(modal overlay)、定义默认步骤选项等。
steps数组
每个步骤定义导览中的一部分,可配置的属性如下:
id:步骤在导览内的唯一标识符。title:步骤标题,显示在步骤气泡(tooltip)的顶部。text:步骤的内容说明,解释用户需要做什么或理解什么。attachTo:指定步骤气泡在 DOM 中的附着位置,包含:element:字符串选择器或 DOM 元素,步骤气泡将附着其上;on:气泡相对元素的位置(如'top'、'left'、'bottom'、'right',也支持'left-start'等 Floating UI 的复合位置)。
advanceOn:定义自动推进导览到下一步的事件。适用于点击按钮、滚动等操作,包含:selector:触发推进的元素的 CSS 选择器;event:推进步骤的事件名,可以是 OHIF 服务事件、cornerstone 事件或任意原生 JS 事件(如'click'、'CORNERSTONE_TOOLS_MOUSE_WHEEL'、'event::measurement_added')。
beforeShowPromise:返回 Promise 的函数。Promise resolve 后才会执行该步骤后续的显示逻辑。可用来确保目标元素就绪后再显示步骤。
tourOptions
tourOptions用于配置导览的整体行为:
useModalOverlay:布尔值,设为true时步骤气泡将置于变暗的模态遮罩之上,遮罩在目标元素周围开出一个窗口,使目标区域保持可交互。defaultStepOptions:应用于导览所有步骤的默认选项,可在单个步骤中覆盖。常用选项包括:buttons:出现在每个步骤底部(footer)的按钮对象数组,每个按钮可触发前进或跳过等动作:text:按钮上显示的文本;action:点击按钮时执行的函数,可通过this.next()前进、this.complete()完成导览;secondary:布尔值,设为true时按钮渲染为次要样式(常用于“跳过”类操作)。
floatingUIOptions
通过 Floating UI 中间件定义步骤气泡的定位选项,用于控制气泡在浏览器边缘附近的摆放。例如,使用preventOverflow中间件确保步骤与视口边缘保持 24px 边距:
floatingUIOptions: { middleware: [ preventOverflow({ padding: 24 }), flip(), // 允许气泡在溢出时翻转 ] }从源码结构看,OHIF 还在组件层提供了默认的 Floating UI 中间件组合(见下文),你既可以直接使用,也可以通过defaultStepOptions.floatingUIOptions覆盖。
Shepherd.js 生命周期事件
每个步骤和导览都可以挂载show、hide、complete、cancel等生命周期事件,用于在导览进行到特定时刻时执行自定义动作。例如:
when: { show() { console.log('Step shown!'); }, hide() { console.log('Step hidden.'); } }when既可作为单个步骤的when属性,也可放入defaultStepOptions作为所有步骤的统一行为。
Onboarding 组件的底层实现
要真正用好 Tours,有必要理解Onboarding组件如何消费ohif.tours定制。其核心逻辑位于 platform/ui-next/src/components/Onboarding/Onboarding.tsx:
- 路由匹配与去重:组件通过
useLocation()获取当前路径,在tours数组中查找tour.route === location.pathname的导览;若找不到匹配项,或hasTourBeenShown(tour.id)返回true(即该导览已在localStorage的shownTours中被标记),则直接跳过。 - 构建 Shepherd 实例:以
new Shepherd.Tour({ ...matchingTour.tourOptions, ... })构造导览实例。注意它会把defaultStepOptions.floatingUIOptions兜底为内置的middleware,并把defaultStepOptions.when.show兜底为defaultShowHandler(详见下文)。 - 注册步骤并启动:遍历
matchingTour.steps,通过tourInstance.addStep(step)逐条注册,随后tourInstance.start()启动导览,并调用markTourAsShown(matchingTour.id)写入localStorage,保证同一导览每个会话只自动展示一次。
配套的 utilities.ts 提供了如下能力:
hasTourBeenShown/markTourAsShown:读写localStorage中的shownTours数组,实现导览“只展示一次”的追踪;defaultShowHandler:在步骤气泡的 footer 中注入进度指示(如1/12),直观显示当前步数与总步数;middleware:默认的 Floating UI 中间件组合[offset(15), shift(), flip(), customMiddleware],其中customMiddleware通过detectOverflow(state, { boundary: document.body, padding: 24 })计算溢出量并微调气泡坐标,确保气泡始终留在视口内。
组件还通过 Onboarding.css 对 Shepherd 的默认样式进行了 Tailwind 定制(标题、文本、按钮、箭头、模态遮罩透明度等),使导览气泡与 OHIF 的整体视觉风格保持一致。
此外,platform/core的类型系统中直接引用了 Shepherd 的类型定义:import { StepOptions, TourOptions } from 'shepherd.js'(见 AppTypes.ts),说明steps与tourOptions的字段约束与 Shepherd.js 官方类型一一对应。当前仓库各相关包统一依赖shepherd.js15.3.0(见 ui-next/package.json、core/package.json 等)。
仓库内置范例:basicViewerTour
extensions/default中内置了一条完整的开箱即用导览,定义于 onboardingCustomization.ts,它注册了ohif.tours定制并实现了 12 个步骤的查看器新手引导,堪称最完整的参考样例:
| 步骤 id | 引导内容 | attachTo 目标 | 前进事件 |
|---|---|---|---|
scroll | 用鼠标滚轮或滚动条滚动图像 | .viewport-element(top) | CORNERSTONE_TOOLS_MOUSE_WHEEL |
zoom | 右键缩放图像 | .viewport-element(left) | CORNERSTONE_TOOLS_MOUSE_UP |
pan | 中键平移图像 | .viewport-element(top) | CORNERSTONE_TOOLS_MOUSE_UP |
windowing | 左键调节窗宽窗位 | .viewport-element(left) | CORNERSTONE_TOOLS_MOUSE_UP |
length | 使用 Length 测量工具 | [data-cy="MeasurementTools-split-button-primary"](bottom) | click |
drawAnnotation | 在视口上绘制长度标注 | .viewport-element(right) | event::measurement_added(OHIF 测量服务事件) |
trackMeasurement | 在测量面板中追踪测量结果 | [data-cy="prompt-begin-tracking-yes-btn"](bottom) | click |
openMeasurementPanel | 打开测量面板 | #trackedMeasurements-btn(left-start) | click |
scrollAwayFromMeasurement | 滚离测量位置 | .viewport-element(left) | CORNERSTONE_TOOLS_MOUSE_WHEEL |
jumpToMeasurement | 点击面板中的测量项跳转 | [data-cy="data-row"](left-start) | click |
changeLayout | 使用布局按钮切换布局(含 MPR) | [data-cy="Layout"](bottom) | click |
这条内置导览还展示了几个重要实践:
- 事件驱动的步骤推进:交互类步骤(滚轮、缩放、平移、窗宽窗位)通过 cornerstone 事件(
CORNERSTONE_TOOLS_MOUSE_UP、CORNERSTONE_TOOLS_MOUSE_WHEEL)自动前进;UI 操作类步骤通过原生click事件前进;而“绘制长度标注”步骤监听 OHIF 测量服务事件event::measurement_added前进,实现了“用户真正完成动作后才进入下一步”的沉浸式引导。 beforeShowPromise确保元素就绪:每个步骤都带有beforeShowPromise: () => waitForElement(...)。waitForElement是文件顶部定义的工具函数,以setInterval轮询(最多 20 次、间隔 25ms)等待目标选择器出现后再显示步骤,避免气泡指向尚未渲染的 DOM 元素。tourOptions组合:useModalOverlay: true开启遮罩聚焦,defaultStepOptions.buttons中配置了唯一的“Skip all”次要按钮,其action调用this.complete()直接结束导览。- 国际化文案:
title与text均通过i18n.t('Onboarding:...')取词,导览文案随 OHIF i18n 语言包走。
进阶定制与最佳实践
在掌握基础配置后,可以从以下方向扩展你的导览:
- 自定义滚动/等待行为:直接复用
onboardingCustomization.ts中的waitForElement模式,把beforeShowPromise绑定到任何异步资源加载完成之后再展示步骤,避免气泡指向空元素。 - 动态元素定位:
attachTo.element既支持字符串选择器也支持 DOM 元素。对于运行时才生成的元素,优先配合beforeShowPromise等待渲染;对于需要精确定位的弹窗或按钮,使用data-cy属性作为稳定锚点(如上表所示)。 - 事件驱动推进:
advanceOn.event支持三种事件来源——原生 JS 事件(click)、cornerstone 交互事件(CORNERSTONE_TOOLS_MOUSE_WHEEL等)、OHIF 服务事件(event::measurement_added等),可据此让导览与真实用户行为强绑定。 - 模式级导览:由于定制数据按 Customization Service 作用域组织,你可以在不同模式(Mode)中注册各自独立的
ohif.tours条目,实现模式专属的引导内容,而不必污染全局配置。 - 定位兜底:不传
floatingUIOptions时,组件会使用内置的middleware(offset + shift + flip + 24px 溢出修正);需要自定义边界行为时,可在defaultStepOptions.floatingUIOptions中传入自己的中间件组合。
迁移指南:3.9 到 3.10 的变更
如果你从 OHIF 3.9 及更早版本升级,需要注意 Tours 相关的破坏性变更(详见 迁移文档):
- 导览不再定义在
window.config.tours,改为通过 Customization Service 的ohif.tours键注册; waitForElement工具函数从配置文件移动到了专门的定制文件中;- 导览定义的整体结构(steps、options 等)保持不变。
迁移时主要做两处改动:
1. 更新对window.config.tours的直接引用
- const tours = window.config.tours; + const tours = customizationService.getCustomization('ohif.tours');2. 用配置更新模式组织导览
- window.config = { - tours: [ - { - id: 'basicViewerTour', - route: '/viewer', - steps: [ - // tour steps... - ], - tourOptions: { - // tour options... - }, - }, - ], - }; + window.config = { + customizationService: { + 'ohif.tours': { + $set: [ + { + id: 'basicViewerTour', + route: '/viewer', + steps: [ + // Your tour steps + ], + }, + ], + }, + }, + };这一变更带来的核心收益是模式级导览:现在你可以为不同模式配置各自独立的引导流程,配合 Customization Service 的作用域机制实现更清晰的组织与复用。
许可说明
Shepherd.js 14.0 以下所有版本基于 MIT 许可证发布;如需使用 14.0 以上版本,请访问 Shepherd.js 官网了解其定价与计划。本仓库各相关包当前锁定依赖为shepherd.js@15.3.0,集成方在评估许可证时需留意这一版本分界。
结论
通过 Customization Service 的ohif.tours定制项与 Shepherd.js,OHIF Viewers 为最终用户提供了一套灵活、可扩展的交互式引导导览机制:配置层面只需声明route、steps、tourOptions与floatingUIOptions,运行时由Onboarding组件自动完成路由匹配、实例构建、去重展示与进度呈现。结合仓库内置的basicViewerTour十二步范例(覆盖滚动、缩放、平移、窗宽窗位、测量、面板交互与布局切换),开发者可以在几分钟内为自己的模式或扩展搭建起专业的新手引导体验,从而显著降低用户的认知门槛、提升查看器的易用性。
【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考