常见问题
使用 InkLayer 过程中可能遇到的常见问题与解答。如果你找不到答案,欢迎在 GitHub Issues 提问。
安装与启动
Q: 组件渲染后页面是空白的,什么都看不见?
A: 按以下顺序排查:
- 检查 CSS 是否导入:确认
import 'inklayer-react/style'(React)或import 'inklayer-vue/style'(Vue)已执行 - 检查 layoutStyle 高度:
PdfAnnotator内部采用绝对定位布局,需要明确高度。添加layoutStyle={{ width: '100%', height: '600px' }}(React)或:layout-style="{ width: '100%', height: '600px' }"(Vue) - 打开浏览器控制台:查看是否有 JS 错误
- 检查 PDF URL 是否可访问:在浏览器地址栏直接打开 PDF URL,确认可以访问(无 CORS 问题)
- 使用 React/Vue DevTools 检查 DOM:确认 PdfAnnotator 的 DOM 元素是否有高度
Q: 安装后样式丢失 / 组件没有样式?
A: 确保你导入了对应的样式文件:
- React:
import 'inklayer-react/style' - Vue:
import '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 传入之前保存的批注数据。详见 React 或 Vue 文档中的完整示例。
Q: 为什么 initialAnnotations 传入后批注不显示?
A: 请检查以下几点:
- 批注数据中的
pageIndex是否从 0 开始(第一页是 0,不是 1) - 批注的
target.coordinateSystem是否为'pdf'(不是'canvas') - PDF 加载完成后(
onLoad触发后),批注才会开始渲染 - 确认
initialAnnotations是数组格式:Annotation[],不是单个对象
Q: 批注数据可以导出为 PDF 吗?
A: 可以。InkLayer 使用 pdf-lib 将批注直接嵌入 PDF 文件,导出后可在任何标准 PDF 阅读器中查看。
Q: 批注数据可以导出为 Excel 吗?
A: 可以。Vue 版本提供 exportToExcel() 函数,将批注列表导出为 Excel 表格,方便审阅和归档。
自定义与扩展
Q: 如何自定义工具栏?
A:
- React:使用
actionsprop 添加自定义按钮到工具栏右侧,或使用children完全替换。 - Vue:使用
toolbar和actions插槽。
详见架构文档 — 自定义扩展。
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.Layer的batchDraw()减少重绘次数
Q: 移动端表现如何?
A: InkLayer 在移动端可正常使用,但需要注意:
- Canvas 操作在移动端性能受限,建议减少同时渲染的批注数量
- 使用
usePinchZoom()Hook 支持手势缩放 - 小屏幕下工具栏会自动折叠,UI 已适配
兼容性
Q: 支持哪些浏览器?
| 浏览器 | 最低版本 |
|---|---|
| Chrome | 90+ |
| Firefox | 90+ |
| Safari | 15+ |
| Edge | 90+ |
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:
- inklayer-react — React 包仓库
- inklayer-vue — Vue 包仓库