常见问题

使用 InkLayer 过程中可能遇到的常见问题与解答。如果你找不到答案,欢迎在 GitHub Issues 提问。

安装与启动

Q: 组件渲染后页面是空白的,什么都看不见?

A: 按以下顺序排查:

  1. 检查 CSS 是否导入:确认 import 'inklayer-react/style'(React)或 import 'inklayer-vue/style'(Vue)已执行
  2. 检查 layoutStyle 高度PdfAnnotator 内部采用绝对定位布局,需要明确高度。添加 layoutStyle={{ width: '100%', height: '600px' }}(React)或 :layout-style="{ width: '100%', height: '600px' }"(Vue)
  3. 打开浏览器控制台:查看是否有 JS 错误
  4. 检查 PDF URL 是否可访问:在浏览器地址栏直接打开 PDF URL,确认可以访问(无 CORS 问题)
  5. 使用 React/Vue DevTools 检查 DOM:确认 PdfAnnotator 的 DOM 元素是否有高度

Q: 安装后样式丢失 / 组件没有样式?

A: 确保你导入了对应的样式文件:

  • Reactimport 'inklayer-react/style'
  • Vueimport 'inklayer-vue/style'

Q: 为什么 PDF 加载很慢?

A: 可能的原因和解决方案:

  • PDF 文件过大:考虑使用服务端进行 PDF 分片加载或预压缩
  • 网络延迟:确保 PDF 文件部署在 CDN 或静态资源服务器上
  • Worker 未正确加载:PDF.js 使用 Web Worker 渲染,确保 Worker 文件可访问

Q: 支持哪些 PDF 版本?

A: InkLayer 基于 PDF.js 4.3,支持 PDF 1.0 到 2.0 的所有版本,包括加密 PDF(需提供密码)。

批注功能

Q: 支持哪些批注类型?

A: InkLayer 支持 14 种批注类型,包括:高亮、删除线、下划线、自由文本、矩形、圆形、自由画笔、自由高亮、签名、印章、注释、箭头、云朵、选区。详见批注系统文档

Q: 如何保存批注数据?

A: 通过 onSave(React)或 @save(Vue)事件获取 Annotation[] 数组,然后发送到你的后端存储:

// React
<PdfAnnotator
  onSave={(annotations) => {
    fetch('/api/save', {
      method: 'POST',
      body: JSON.stringify(annotations),
    })
  }}
/>

// Vue
<PdfAnnotator
  @save="(annotations) => saveToServer(annotations)"
/>

Q: 如何回显之前保存的批注?

A: 使用 initialAnnotations prop 传入之前保存的批注数据。详见 ReactVue 文档中的完整示例。

Q: 为什么 initialAnnotations 传入后批注不显示?

A: 请检查以下几点:

  1. 批注数据中的 pageIndex 是否从 0 开始(第一页是 0,不是 1)
  2. 批注的 target.coordinateSystem 是否为 'pdf'(不是 'canvas'
  3. PDF 加载完成后(onLoad 触发后),批注才会开始渲染
  4. 确认 initialAnnotations 是数组格式:Annotation[],不是单个对象

Q: 批注数据可以导出为 PDF 吗?

A: 可以。InkLayer 使用 pdf-lib 将批注直接嵌入 PDF 文件,导出后可在任何标准 PDF 阅读器中查看。

Q: 批注数据可以导出为 Excel 吗?

A: 可以。Vue 版本提供 exportToExcel() 函数,将批注列表导出为 Excel 表格,方便审阅和归档。

自定义与扩展

Q: 如何自定义工具栏?

A:

  • React:使用 actions prop 添加自定义按钮到工具栏右侧,或使用 children 完全替换。
  • Vue:使用 toolbaractions 插槽。

详见架构文档 — 自定义扩展

Q: 如何添加自定义批注类型?

A: 通过 AdapterRegistry 注册自定义 Adapter,将你定义的批注数据模型映射到 Konva 渲染节点。详见 Core API 参考

Q: React 和 Vue 版本的 API 是否完全一致?

A: 两个版本共享相同的 Core 层(Annotation 数据模型、Adapter、Integration),因此批注数据的结构完全一致。差异仅在于框架绑定层:

  • Props 命名:React 用 camelCase,Vue 用 kebab-case(或 camelCase + : 绑定)
  • 事件:React 用 onXxx,Vue 用 @xxx
  • 自定义 UI:React 用 Render Props,Vue 用 Slots

性能优化

Q: 如何处理大 PDF 文件(100+ 页)?

A:

  • 默认情况下 InkLayer 会渲染所有页面。对于超大 PDF,建议进行按需加载
  • 使用 PdfViewer 代替 PdfAnnotator 可减少工具栏和侧边栏的开销
  • 考虑使用虚拟滚动方案只渲染可视区域内的页面

Q: 批注数量很多时(1000+)为什么卡顿?

A:

  • Konva 会为每个批注创建独立的 Shape 节点,大量节点影响性能
  • 建议按页缓存批注,只渲染当前可见页面的批注
  • 使用 Konva.LayerbatchDraw() 减少重绘次数

Q: 移动端表现如何?

A: InkLayer 在移动端可正常使用,但需要注意:

  • Canvas 操作在移动端性能受限,建议减少同时渲染的批注数量
  • 使用 usePinchZoom() Hook 支持手势缩放
  • 小屏幕下工具栏会自动折叠,UI 已适配

兼容性

Q: 支持哪些浏览器?

浏览器最低版本
Chrome90+
Firefox90+
Safari15+
Edge90+

Q: 是否支持服务端渲染 (SSR)?

A: InkLayer 依赖 Canvas API 和 PDF.js(Web Worker),不支持 SSR。如果使用 Next.js 或 Nuxt,请在客户端组件中动态导入:

// React — Next.js
import dynamic from 'next/dynamic'

const PdfAnnotator = dynamic(
  () => import('inklayer-react').then(m => m.PdfAnnotator),
  { ssr: false }
)

// Vue — Nuxt 3
// 将组件放在 <ClientOnly> 中
<ClientOnly>
  <PdfAnnotator url="/doc.pdf" />
</ClientOnly>

Q: 其他 SSR 框架(Astro、Remix、SvelteKit)如何处理?

A: 通用解决方案:确保 InkLayer 的组件只在客户端渲染,避免在 SSR 阶段执行 Canvas 和 PDF.js 相关代码。各框架的具体做法:

  • Astro:使用 client:load 指令 <PdfAnnotator client:load />
  • Remix:在 useEffect 中动态导入,或使用 <ClientOnly> 包装组件
  • SvelteKit:使用 onMount() 动态导入,或 svelte:component 的 client-only 机制

国际化

Q: 如何切换语言?

A: 通过 locale prop 设置:

  • locale="zh-CN" — 中文(默认)
  • locale="en-US" — 英文

Q: 支持哪些语言?

A: 目前支持中文(简体)和英文。欢迎贡献更多语言翻译。

许可与支持

Q: InkLayer 是免费的吗?

A: 是的,InkLayer 使用 MIT 许可证,可自由用于个人和商业项目。

Q: 如何获取帮助?

A: