使用react-pdf时如何获取当前滚动的页码?
解决react-pdf滚动时显示当前页码的问题
你之前用handlePageChange和handlePageRenderSuccess无效,是因为这两个事件是页面加载/渲染完成的触发事件,和滚动到该页的行为无关。要实现滚动时显示当前页码,需要监听滚动容器的滚动事件,检测哪个Page元素处于视口范围内。
具体修改步骤:
- 添加滚动容器的Ref,用于获取容器元素并监听滚动
- 编写滚动监听函数,判断当前可见的页码
- 在组件挂载时绑定滚动事件,卸载时移除
修改后的完整代码:
import React, { useState, useRef, useEffect } from 'react'; import styles from './Previewer.module.css'; import { toast } from 'react-toastify'; import { Document, Page, pdfjs } from 'react-pdf'; import { PDFDocumentProxy } from 'pdfjs-dist'; pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`; import 'react-pdf/dist/Page/TextLayer.css'; import 'react-pdf/dist/Page/AnnotationLayer.css'; export type ViewerProps = { pdfUrl: string | undefined; }; const Previewer: React.FC<ViewerProps> = (props: ViewerProps) => { const [pdfScale, setPdfScale] = useState<number>(1); const [numPages, setNumPages] = useState<number>(); const [pageNumber, setPageNumber] = useState<number>(1); // 滚动容器的Ref const previewBodyRef = useRef<HTMLDivElement>(null); const options = { cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.9.179/cmaps/', }; if (!props.pdfUrl) { return (<div>Loading</div>); } const handleDownloadPdf = async (pdfUrl: any) => { if (!pdfUrl) { toast.error("PDF Url is null"); return; } try { const response = await fetch(pdfUrl); const blob = await response.blob(); const downloadUrl = window.URL.createObjectURL(new Blob([blob])); const link = document.createElement('a'); link.href = downloadUrl; link.setAttribute('download', 'main.pdf'); document.body.appendChild(link); link.click(); document.body.removeChild(link); } catch (error) { console.error('Error downloading PDF:', error); } } const handleZoomIn = async () => { setPdfScale(pdfScale + 0.1); } const handleZoomOut = async () => { setPdfScale(pdfScale - 0.1); } const onDocumentLoadSuccess = (pdf: any) => { const { numPages } = pdf; setNumPages(numPages); } // 滚动监听:检测当前可见页码 const handleScroll = () => { if (!previewBodyRef.current || !numPages) return; const pages = previewBodyRef.current.querySelectorAll(`.${styles.pdfPage}`); const viewportTop = previewBodyRef.current.scrollTop; const viewportBottom = viewportTop + previewBodyRef.current.clientHeight; let currentVisiblePage = 1; pages.forEach((page, index) => { const pageRect = page.getBoundingClientRect(); // 计算页面相对于滚动容器的位置 const pageTop = pageRect.top + previewBodyRef.current!.scrollTop; const pageBottom = pageTop + pageRect.height; // 判断页面是否处于视口核心区域(避免边缘露出时误判) const isVisible = pageTop < viewportBottom - pageRect.height / 3 && pageBottom > viewportTop + pageRect.height / 3; if (isVisible) { currentVisiblePage = index + 1; } }); if (currentVisiblePage !== pageNumber) { setPageNumber(currentVisiblePage); } } // 绑定/解绑滚动事件 useEffect(() => { const bodyElement = previewBodyRef.current; if (bodyElement) { bodyElement.addEventListener('scroll', handleScroll); // 初始加载时检测一次当前页码 setTimeout(handleScroll, 100); } return () => { if (bodyElement) { bodyElement.removeEventListener('scroll', handleScroll); } }; }, [numPages, pageNumber, styles.pdfPage]); const renderPages = (totalPageNum: number | undefined) => { if (!totalPageNum || totalPageNum < 1) return; const tagList: JSX.Element[] = []; for (let i = 1; i <= totalPageNum; i++) { tagList.push( <Page key={i} className={styles.pdfPage} scale={pdfScale} pageNumber={i} > </Page> ); } return tagList; } return ( <div id="preview" className={styles.preview}> <div className={styles.previewHader}> <button onClick={() => { handleDownloadPdf(props.pdfUrl) }}>下载PDF</button> <button id="zoominbutton" onClick={() => { handleZoomIn() }}>放大</button> <button id="zoomoutbutton" onClick={() => { handleZoomOut() }}>缩小</button> </div> {/* 绑定滚动容器的Ref */} <div ref={previewBodyRef} className={styles.previewBody}> <Document options={options} file={props.pdfUrl} onLoadSuccess={onDocumentLoadSuccess}> {renderPages(numPages)} </Document> </div> <div className={styles.previewFooter}> <p> Page {pageNumber} of {numPages} </p> </div> </div> ); } export default Previewer;
关键说明:
- 通过
previewBodyRef获取滚动容器,监听其scroll事件 - 在
handleScroll中,用getBoundingClientRect计算每个Page的位置,判断是否处于视口核心区域(避免页面边缘露出时误判) - 初始加载时通过
setTimeout调用一次handleScroll,确保页码显示正确 - 移除了无用的
handlePageChange和handlePageRenderSuccess函数,它们和滚动行为无关
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

