自定义JS PDF图片渲染器缩放时页面跳转问题解决方案咨询
问题
我有一套将PDF渲染为图片的自定义JavaScript遗留代码,现在遇到的问题是:执行放大(Zoom in)/缩小(Zoom out)操作时,页面会自动切换,我希望缩放过程中始终停留在当前页面。
我尝试用transformOrigin属性让缩放聚焦于中心,但这会导致包含所有渲染图片的.pages-container元素原始尺寸发生变化。请问有没有办法修复这个尺寸问题,或者有更优的实现方式确保缩放时页面不切换?
相关代码
render() { return ( <ReactResizeDetector handleWidth onResize={() => this.handleResize()}> <div className='evidence-document-viewer-component vbox hfill vfill overflow-hidden evidence-document-viewer-ez'> <style dangerouslySetInnerHTML={this.evidenceStyle} /> {this.renderDocHeaderContents()} <div data-test='ev-doc-scrollable' className='ev-doc-scrollable hfill vfill'> <div className='ev-doc-inline-contents overflow-auto' tabIndex={0} ref={this.inlineContentsRef} id='mainContainer'> {(this.props.noClinicalMessageFound && !this.props.loadedResult?.ezChartAsJson) && <div className='no-document-msg'> ${this.props.noClinicalMessageFound} </div> } {(!this.props.noClinicalMessageFound && this.props.loadedResult?.ezChartAsJson) && this.renderPages()} </div> </div> </div> </ReactResizeDetector>); } renderPages() { if (!this.props.loadedResult?.ezChartAsJson?.pages) { return null; } window.setTimeout(() => { // for IE support, we will create canvases for all elements on this page if the page image is already loaded if (EvidenceDocUtils.useCanvasElements) { $('.page-container').each((index, page) => { EvidenceToCanvasUtil.createForPage($(page)); }); } }); let { pageImagePath } = this.props.loadedResult.ezChartAsJson; if (pageImagePath) { pageImagePath = `${location.protocol}//${location.host}${pageImagePath}`; } const pages = this.props.loadedResult.ezChartAsJson.pages; const transform = `scale(${this.scaling}) translateX(${this.translateX}px`; const zeroPad = (num: number, places: number) => String(num).padStart(places, '0'); const pageContainerStyle = { width: this.contentsWidth, height: this.contentsHeight, transformOrigin: 'center', transform }; return <div className='pages-container' ref={this.pagesContainerRef} style={pageContainerStyle}> {pages.map((page, pageIndex) => { const pageBounds = this.pageBoundsArray[pageIndex]; const isVisible = pageIndex >= this.pageIndexToRenderStart && pageIndex <= this.pageIndexToRenderEnd; const rotation = this.rotation; const style = { width: pageBounds.w, height: pageBounds.h, left: pageBounds.x, top: pageBounds.y, transform: `rotate(${rotation}deg)` }; return (isVisible && <div className='page-container' data-visible={true} key={pageIndex} style={style}> <img src="/assets/images/loading_circle128.gif" className='page-image-loading' /> {!this.suppressPageImages && <img src={`${pageImagePath}${zeroPad(pageIndex, 4)}.jpg`} className='page-image' /> } {page.evidences.map(evidence => { // get the index of the evidence in the results const conceptIndex = this.evidencesToInstancesMap.get(evidence); const key = `${evidence.conceptId}|${conceptIndex}`; const { conceptIdsAndInstancesToClassMap } = this; const evidenceClassName = (conceptIdsAndInstancesToClassMap[evidence.conceptId] ?? {})[conceptIndex] ?? ''; if (evidenceClassName) { const toolTip = this.toolTipsForConceptIds[evidence.conceptId]; const style = { left: evidence.x, top: evidence.y, width: evidence.w, height: evidence.h }; return <span key={key} className={`nlp cg${evidence.conceptId} ${evidenceClassName}`} onClick={this.handleClickEvidence} title={toolTip} style={style} data-concept-id={evidence.conceptId} data-concept-index={conceptIndex}></span>; } else { return null; } })} </div>); })} </div>; } @action evidenceZoomIn() { this.scaleToFit = false; this.scaleToFitPage = false; this.evidenceDocScaling *= 1.1; } @action evidenceZoomOut() { this.scaleToFit = false; this.scaleToFitPage = false; this.evidenceDocScaling *= .9; }
解决思路与方案
核心问题分析
缩放时页面切换的本质是:transform: scale()仅改变容器视觉尺寸,但滚动容器的滚动位置基于原始布局尺寸计算,缩放后内容实际滚动范围错位,导致视觉上的页面跳变。同时transformOrigin: center固定容器中心为缩放点,而非当前视口中心,进一步加剧滚动偏移。
具体修复方案
1. 固定.pages-container的原始尺寸
容器始终保持文档的真实原始尺寸(所有页面拼接后的总宽高),仅通过transform: scale()实现视觉缩放,避免滚动范围混乱:
// 先提前计算文档原始尺寸(比如纵向排版时,高度为所有页面高度之和,宽度取最大页面宽度) // 示例计算逻辑(可根据实际排版调整) const calculateOriginalDocumentSize = () => { const pages = this.props.loadedResult.ezChartAsJson.pages; let maxWidth = 0; let totalHeight = 0; pages.forEach(page => { const bounds = this.pageBoundsArray[pages.indexOf(page)]; maxWidth = Math.max(maxWidth, bounds.w); totalHeight += bounds.h; }); return { width: maxWidth, height: totalHeight }; }; // 修改pageContainerStyle const { width: originalDocumentWidth, height: originalDocumentHeight } = calculateOriginalDocumentSize(); const pageContainerStyle = { width: originalDocumentWidth, height: originalDocumentHeight, transformOrigin: '0 0', // 改为左上角原点,便于后续滚动计算 transform: `scale(${this.scaling})` };
2. 缩放时锁定当前视口中心
缩放前后记录当前视口对应的文档原始坐标,缩放后调整滚动位置,确保该坐标始终在视口中心:
@action evidenceZoomIn() { this.scaleToFit = false; this.scaleToFitPage = false; this.adjustScrollPositionBeforeZoom(); this.evidenceDocScaling *= 1.1; this.adjustScrollPositionAfterZoom(); } @action evidenceZoomOut() { this.scaleToFit = false; this.scaleToFitPage = false; this.adjustScrollPositionBeforeZoom(); this.evidenceDocScaling *= .9; this.adjustScrollPositionAfterZoom(); } // 缩放前记录视口中心对应的原始文档坐标 adjustScrollPositionBeforeZoom() { const scrollContainer = this.inlineContentsRef.current; if (!scrollContainer) return; const viewportCenterX = scrollContainer.scrollLeft + scrollContainer.clientWidth / 2; const viewportCenterY = scrollContainer.scrollTop + scrollContainer.clientHeight / 2; // 转换为原始文档坐标(除以当前缩放比例) this.preZoomDocCenterX = viewportCenterX / this.scaling; this.preZoomDocCenterY = viewportCenterY / this.scaling; } // 缩放后调整滚动位置,让原始坐标回到视口中心 adjustScrollPositionAfterZoom() { const scrollContainer = this.inlineContentsRef.current; if (!scrollContainer) return; const newViewportCenterX = this.preZoomDocCenterX * this.scaling; const newViewportCenterY = this.preZoomDocCenterY * this.scaling; // 设置新的滚动位置 scrollContainer.scrollLeft = newViewportCenterX - scrollContainer.clientWidth / 2; scrollContainer.scrollTop = newViewportCenterY - scrollContainer.clientHeight / 2; }
3. 简化替代方案:使用CSS zoom属性
如果项目目标浏览器兼容(Chrome、Edge、IE支持良好,Firefox需额外配置),可直接用zoom替代transform: scale(),它会直接缩放元素且不改变布局尺寸,滚动位置自动保持:
const pageContainerStyle = { width: originalDocumentWidth, height: originalDocumentHeight, zoom: this.scaling // 替换transform为zoom };
额外优化
- 移除
transform中的translateX硬编码(无特殊平移需求时无需保留) - 确保
pageIndexToRenderStart/pageIndexToRenderEnd基于原始页面位置计算,避免渲染错误页面
内容的提问来源于stack exchange,提问作者Akash Singh
相关产品推荐
相关产品推荐

