react-pdf页面上层自定义Canvas缩放异常问题求助
React-PDF 缩放时Canvas绘图像素化/线条变粗问题解决
问题描述
我正在基于react-pdf渲染的页面构建PDF编辑器,没有使用Page组件暴露的canvasRef,而是单独创建了一个Canvas元素覆盖在react-pdf的Page上方。Page所在的父div会将页面缩放到超过A4初始尺寸(596×842px),并显示滚动条允许用户滚动。当前Canvas元素已定位在Page上方,但缩放操作后绘图出现异常:在596×842及更小尺寸下绘图正常,一旦页面缩放超过该尺寸,绘图就会像素化,线条也变粗。此问题并非react-pdf仓库的bug,需要解决缩放适配相关逻辑。
现有代码片段
组件渲染代码
{pdfFile && ( <div id="row" style={{ marginTop: '60px', width: "100vw", maxWidth: '1024px', display: "flex", overflowY: "auto" }}> <StyledEditorHolder id="row"> <PdfContent ref={pdfWrapperRef}> <StyledPdfDocument file={pdfFile} onLoadSuccess={handleDocumentLoadSuccess}> <StyledPdfPage pageNumber={currentPage} width={wrapperDivSize} /> </StyledPdfDocument> <CanvasContainer ref={canvasRef} onPointerDown={handleCanvasTouchStart} onPointerUp={handleCanvasTouchEnd} onPointerMove={handleCanvasTouchMove} blockScroll={drawingEnabled} /> </PdfContent> </StyledEditorHolder> </div> )}
核心逻辑代码
useEffect(() => { if(pdfWrapperRef.current) { setWrapperDivSize(pdfWrapperRef.current.getBoundingClientRect().width); const handleResize = throttle(() => { if (pdfWrapperRef.current) { const pdfPage = document.querySelector('.react-pdf__Page'); const newWidth = pdfPage.getBoundingClientRect().width; setWrapperDivSize(pdfWrapperRef.current.getBoundingClientRect().width); } }, 500); window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; } }, [isOpen, pdfWrapperRef.current]); const handleDocumentLoadSuccess = ({ numPages }) => { setNumPages(numPages); setCurrentPage(1); setDrawnPaths(Array.from({ length: numPages }, () => [])); const pdfPage = document.querySelector('.react-pdf__Page'); if (pdfPage) { const rect = pdfPage.getBoundingClientRect(); console.log("Initial width", rect.width) setCurrentWidth(prevState => rect.width); canvasRef.current.width = 596; canvasRef.current.height = 842; console.log("Canvas", rect.width, rect.height) // 计算初始缩放比例 const scaleFactor = rect.width / 595.276; setScaleFactor(scaleFactor); console.log("Initial Scale factor", scaleFactor) setIsOpen(true); } };
样式代码
export const StyledPdfEditor = styled.div` display: flex; flex-direction: column; flex: 1; align-items: center; `; export const StyledEditorHolder = styled.div` box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px; width: 100%; border-radius: 20px; display: flex; position: relative; justify-content: center; overflow: hidden; ` export const PdfContent = styled.div<CanvasProps>` position: relative; display: flex; max-width: 1024px; touch-action: ${props => props.blockScroll ? "none" : "auto"}; justify-content: center; overflow: auto; width: 100vw; `; export const CanvasContainer = styled.canvas<CanvasProps>` position: absolute; width: 100%; height: auto; touch-action: ${props => props.blockScroll ? "none" : "auto"}; z-index: 100; `; export const StyledPdfPage = styled(Page)` position: relative; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; .react-pdf__Page { display: flex; justify-content: center; align-items: center; align-self: center; } `; export const StyledPdfDocument = styled(Document)` position: relative; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; .react-pdf__Document { width: 100%; } `;
解决方案
1. 分离Canvas的实际像素尺寸与显示尺寸
Canvas的width/height属性是绘图的实际像素分辨率,CSS的width/height是显示尺寸。当前固定Canvas为596×842,缩放时CSS拉伸导致像素化,需根据页面缩放比例动态设置Canvas实际尺寸:
修改handleDocumentLoadSuccess:
const handleDocumentLoadSuccess = ({ numPages }) => { setNumPages(numPages); setCurrentPage(1); setDrawnPaths(Array.from({ length: numPages }, () => [])); const pdfPage = document.querySelector('.react-pdf__Page'); if (pdfPage) { const rect = pdfPage.getBoundingClientRect(); const scaleFactor = rect.width / 595.276; // A4标准宽度(pt转px) // 计算Canvas实际像素尺寸,取整避免模糊 const canvasWidth = Math.round(596 * scaleFactor); const canvasHeight = Math.round(842 * scaleFactor); canvasRef.current.width = canvasWidth; canvasRef.current.height = canvasHeight; setCurrentWidth(rect.width); setScaleFactor(scaleFactor); setIsOpen(true); } };
修改resize监听的handleResize:
const handleResize = throttle(() => { if (pdfWrapperRef.current && canvasRef.current) { const pdfPage = document.querySelector('.react-pdf__Page'); if (pdfPage) { const rect = pdfPage.getBoundingClientRect(); const scaleFactor = rect.width / 595.276; const canvasWidth = Math.round(596 * scaleFactor); const canvasHeight = Math.round(842 * scaleFactor); canvasRef.current.width = canvasWidth; canvasRef.current.height = canvasHeight; setScaleFactor(scaleFactor); setWrapperDivSize(rect.width); } } }, 500);
2. 绘图坐标转换
用户的鼠标/触摸坐标基于显示尺寸,需转换为Canvas实际像素坐标,避免线条变粗或位置偏移:
// 以handleCanvasTouchMove为例 const handleCanvasTouchMove = (e) => { if (!drawingEnabled) return; const rect = canvasRef.current.getBoundingClientRect(); // 将客户端坐标转换为Canvas实际像素坐标 const x = (e.clientX - rect.left) / scaleFactor; const y = (e.clientY - rect.top) / scaleFactor; // 使用转换后的x、y执行绘图逻辑 // ...你的绘图代码 };
3. 修正Canvas CSS样式
确保Canvas与Page尺寸完全匹配,避免比例偏差:
export const CanvasContainer = styled.canvas<CanvasProps>` position: absolute; width: 100%; height: 100%; /* 替换auto为100%,与Page高度完全匹配 */ touch-action: ${props => props.blockScroll ? "none" : "auto"}; z-index: 100; image-rendering: crisp-edges; /* 可选,优化缩放后绘图清晰度 */ `;
4. 同步Page与Canvas的缩放状态
确保wrapperDivSize始终对应Page实际宽度,react-pdf的Page组件width属性绑定该值,保证两者缩放比例一致。
内容的提问来源于stack exchange,提问作者imim
相关产品推荐
相关产品推荐

