React-PDF编辑器与签名后PDF的字段垂直错位问题(逐字段下移一个字段高度)
React-PDF编辑器与签名后PDF的字段垂直错位问题(逐字段下移一个字段高度)
一、编辑器内字段逐次下移的问题修复
从你描述的现象和代码来看,核心问题出在字段位置计算的依赖监听和坐标更新逻辑上,我帮你梳理两个关键修复点:
1. 修复useEffect的依赖监听错误
你的PlacedFieldComponent里的useEffect依赖了canvasRef.current,但useRef的current属性是可变引用,不会触发useEffect重新执行,导致字段位置计算不及时或错误。
修改步骤:
- 在父组件
PdfEditor中,监听PDF页面加载完成事件,获取并保存canvas的尺寸到state:// 在PdfEditor组件内添加 const [canvasSize, setCanvasSize] = useState<{ width: number; height: number } | null>(null); useEffect(() => { const canvas = canvasRef.current; if (canvas) { setCanvasSize({ width: canvas.width, height: canvas.height }); } }, [currentPage]); // 切换页面时重新获取尺寸 - 把
canvasSize作为props传递给PlacedFieldComponent,然后在子组件的useEffect中依赖canvasSize而非canvasRef.current:// PlacedFieldComponent的useEffect修改 useEffect(() => { if (!canvasSize) return; const dpr = window.devicePixelRatio || 1; const canvasWidth = canvasSize.width / dpr; const canvasHeight = canvasSize.height / dpr; const px = (field.position?.x ?? 0) * canvasWidth - FIELD_WIDTH / 2; const py = (field.virtualY ?? 0) * canvasHeight - FIELD_HEIGHT / 2; // 移除之前的条件判断,直接更新位置,避免累积偏移 setPos({ x: px, y: py }); }, [field.position, field.virtualY, field.page, canvasSize]);
2. 移除位置更新的条件判断
你之前的代码里,只有当位置差异大于1px时才更新pos,这会导致后续字段的位置无法正确重置到点击位置,累积偏移。直接移除这个条件,每次字段属性变化时强制更新位置即可。
二、签名后PDF字段错位的问题修复
这个问题的核心是PDF坐标系统和浏览器坐标系统的差异:
- 浏览器(react-pdf)的坐标原点在页面左上角,y轴向下递增
- pdf-lib的坐标原点在页面左下角,y轴向上递增
你之前手动减40px是临时补丁,正确的做法是做坐标转换:
修复代码示例(pdf-lib绘制时的坐标转换)
async function embedFieldsToPdf(originalPdf: Uint8Array, placedFields: PlacedField[]) { const pdfDoc = await PDFDocument.load(originalPdf); const pages = pdfDoc.getPages(); placedFields.forEach(field => { const targetPage = pages[field.page - 1]; // 注意:pdf-lib的页面索引从0开始 const pageWidth = targetPage.getWidth(); const pageHeight = targetPage.getHeight(); // 转换编辑器的比例坐标到pdf-lib的实际坐标 const editorXRatio = field.position?.x ?? 0; const editorYRatio = field.virtualY ?? 0; // X坐标:直接按比例转换(左右方向一致) const pdfX = editorXRatio * pageWidth - FIELD_WIDTH / 2; // Y坐标:关键转换,从浏览器的顶部基准转为pdf的底部基准 const editorYFromTop = editorYRatio * pageHeight; const pdfY = pageHeight - editorYFromTop - FIELD_HEIGHT; // 示例:绘制文本字段和边框 if (field.type === 'Text') { // 绘制边框 targetPage.drawRectangle({ x: pdfX, y: pdfY, width: FIELD_WIDTH, height: FIELD_HEIGHT, borderColor: rgb(0, 0, 1), borderWidth: 1, }); // 绘制文本 targetPage.drawText(field.value ?? '', { x: pdfX + 4, y: pdfY + 10, size: 12, color: rgb(0, 0, 0), }); } // 其他字段类型(如签名图片)同理转换坐标 }); return await pdfDoc.save(); }
三、额外验证点
- 确保添加字段时的初始坐标计算正确:在
PdfEditor的点击事件中,计算virtualY时直接用(e.clientY - rect.top) / rect.height,不要添加额外的偏移量。 - 检查所有字段是否使用
absolute定位,且父容器设置了position: relative,避免定位基准错误。
按照这些步骤修复后,编辑器内的字段漂移和最终PDF的错位问题应该都能解决,不需要再依赖临时的40px调整啦。




