Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 将 PDFViewer 的 Page 调整为 absolute 定位 #28

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/green-knives-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@orca-fe/pdf-viewer': major
---

feat: 将 PDFViewer 的 Page 调整为 absolute 定位
2 changes: 1 addition & 1 deletion .github/workflows/changesets-auto-pre-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
"msgtype": "markdown",
"markdown": {
"title":"orca-fe 发布提醒",
"text": "## Orca-Pocket 临时版本已发布\n ${{steps.changesets.outputs.changedPackages}}\n\nTriggered by [PR#${{ github.event.pull_request.number }}](https://github.com/orca-team/pocket/pull/${{ github.event.pull_request.number }})"
"text": "## Orca-Pocket 临时版本已发布\n ${{steps.changesets.outputs.changedPackages}}\n\nTriggered by [PR#${{ github.event.pull_request.number }} {{github.event.pull_request.title}}](https://github.com/orca-team/pocket/pull/${{ github.event.pull_request.number }})"
},
"at": {
"isAtAll": false
Expand Down
19 changes: 16 additions & 3 deletions packages/pdf-viewer/demo/DemoDev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@
* debug: true
*/

import React from 'react';
import PdfViewer, { PDFContextPrintPlugin, PDFOpenFileButtonPlugin, PDFPainterPlugin, PDFTooltipPlugin, usePdfViewerRef } from '@orca-fe/pdf-viewer';
import React, { useState } from 'react';
import PdfViewer, {
PDFContextPrintPlugin,
PDFOpenFileButtonPlugin,
PDFPageDebugPlugin,
PDFPainterPlugin,
PDFTooltipPlugin,
usePdfViewerRef,
} from '@orca-fe/pdf-viewer';
import { JsonViewer } from '@orca-fe/pocket';

const Demo1 = () => {
const [data, setData] = useState<any[]>([]);

const pdfViewerRef = usePdfViewerRef();
return (
<div>
Expand All @@ -26,10 +36,13 @@ const Demo1 = () => {
/>
<PdfViewer ref={pdfViewerRef} dropFile pdfJsParams={{ cMapUrl: '/pdfjs-bcmaps/' }} style={{ height: 600 }}>
<PDFOpenFileButtonPlugin />
<PDFPainterPlugin />
<PDFPainterPlugin data={data} onDataChange={setData} />
<PDFTooltipPlugin />
<PDFContextPrintPlugin />
<PDFPageDebugPlugin />
</PdfViewer>
<div>绘图数据:</div>
<JsonViewer value={data} />
</div>
);
};
Expand Down
17 changes: 12 additions & 5 deletions packages/pdf-viewer/src/PDFViewer.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,18 @@ export default createUseStyles(
position: 'relative',
height: '100%',
overflow: 'auto',
padding: '24px 0',
// padding: '24px 0',
backgroundColor: '#f0f2f5',
paddingBottom: 48,
// paddingBottom: 48,
},
pageContainer: {
position: 'relative',
position: 'absolute',
backgroundColor: '#fff',
marginLeft: 'auto',
marginRight: 'auto',
// marginLeft: 'auto',
// marginRight: 'auto',
boxShadow: [0, 0, 12, 'rgba(0, 0, 0, 0.3)'],
left: '0%',
// transform: 'translateX(-50%)',
},
page: {
width: '100%',
Expand Down Expand Up @@ -95,6 +97,11 @@ export default createUseStyles(
backgroundColor: 'rgba(0,129,255,0.3)',
},
},
pageBottomPlaceholder: {
position: 'absolute',
pointerEvents: 'none',
visibility: 'hidden',
},
},
{
classNamePrefix: prefix,
Expand Down
75 changes: 51 additions & 24 deletions packages/pdf-viewer/src/PDFViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ const ef = () => undefined;

const round001 = roundBy(0.001);

const PAGE_PADDING_TOP = 24;
const PAGE_PADDING_HORIZONTAL = 24;
const PAGE_PADDING_BOTTOM = 60;

const DefaultLoadingTips = () => {
const [l] = useLocale(zhCN);
return <div className="pdf-viewer-default-loading-tips">{l.loadingTips}</div>;
Expand Down Expand Up @@ -218,39 +222,39 @@ const PDFViewer = React.forwardRef<PDFViewerHandle, PDFViewerProps>((props, pRef

// 根据 viewport 信息生成每一页的实际位置信息
const {
topArr: pageTopArr,
maxWidth,
topArr: topArrOrigin,
pageMaxWidth,
pageMaxHeight,
bottom: pageBottomOrigin,
} = useMemo(() => {
let top = 0;
let maxWidth = 0;
let pageMaxWidth = 0;
let pageMaxHeight = 0;
const topArr = viewports.map(({ height: _height, width: _width }) => {
const width = _width * PixelsPerInch.PDF_TO_CSS_UNITS;
const height = _height * PixelsPerInch.PDF_TO_CSS_UNITS;
const width = _width;
const height = _height;
const _top = top;
top += Math.floor(height + pageGap) * scale;
maxWidth = Math.max(width * scale, maxWidth);
top += height + pageGap;
pageMaxWidth = Math.max(width, pageMaxWidth);
pageMaxHeight = Math.max(height, pageMaxHeight);
return _top;
});
return { topArr, maxWidth, pageMaxWidth, pageMaxHeight };
}, [viewports, pageGap, scale]);
return { topArr, pageMaxWidth, pageMaxHeight, bottom: top };
}, [viewports, pageGap]);

const pageTopArr = useMemo(() => topArrOrigin.map(top => top * scale * PixelsPerInch.PDF_TO_CSS_UNITS), [scale, topArrOrigin]);

const [zoomMode, setZoomMode] = useState<false | 'autoWidth' | 'autoHeight'>(typeof defaultZoom === 'number' ? false : defaultZoom);

const autoZoomDebounce = useDebounceFn(
() => {
let newZoom = zoom;
if (zoomMode && _this.size && maxWidth && pageMaxHeight) {
if (zoomMode && _this.size && pageMaxWidth && pageMaxHeight) {
if (zoomMode === 'autoWidth') {
// 调整缩放级别,使其与容器宽度匹配
newZoom = Math.log2((_this.size.width - 32) / pageMaxWidth);
newZoom = Math.log2((_this.size.width - 20 - 2 * PAGE_PADDING_HORIZONTAL) / (pageMaxWidth * PixelsPerInch.PDF_TO_CSS_UNITS));
} else if (zoomMode === 'autoHeight') {
newZoom = Math.log2((_this.size.height - 32) / pageMaxHeight);
newZoom = Math.log2((_this.size.height - 32) / (pageMaxHeight * PixelsPerInch.PDF_TO_CSS_UNITS));
}
}

Expand All @@ -275,8 +279,13 @@ const PDFViewer = React.forwardRef<PDFViewerHandle, PDFViewerProps>((props, pRef

_this.size = size;

const body = bodyRef;
if (body) {
body.style.setProperty('--pdf-viewer-page-width', `${size.width}px`);
}

autoZoomDebounce.run();
}, rootRef);
}, bodyRef);

// 翻頁
const changePage = useMemoizedFn((page: number, anim = false) => {
Expand Down Expand Up @@ -487,9 +496,11 @@ const PDFViewer = React.forwardRef<PDFViewerHandle, PDFViewerProps>((props, pRef
const x = clientX - left;
const y = clientY - top;

const pageMaxWidthScale = pageMaxWidth * scale * PixelsPerInch.PDF_TO_CSS_UNITS;

if (!_this.mousePositionBeforeWheel) {
_this.mousePositionBeforeWheel = {
x: x + dom.scrollLeft - 0.5 * (maxWidth < width ? width - maxWidth : 0),
x: x + dom.scrollLeft - 0.5 * (pageMaxWidthScale < width ? width - pageMaxWidthScale : 0),
y: y + dom.scrollTop,
zoom,
};
Expand Down Expand Up @@ -690,7 +701,17 @@ const PDFViewer = React.forwardRef<PDFViewerHandle, PDFViewerProps>((props, pRef
setToolbarRightDom(dom);
}}
/>
<div ref={setBodyRef} className={cn(styles.pagesOuter, { [styles.droppable]: dropFile })}>
<div
ref={setBodyRef}
className={cn(styles.pagesOuter, { [styles.droppable]: dropFile })}
style={
{
'--scale-factor': scale * PixelsPerInch.PDF_TO_CSS_UNITS,
'--scale-factor-origin': scale,
'--pdf-viewer-page-scale': scale * PixelsPerInch.PDF_TO_CSS_UNITS,
} as CSSProperties
}
>
<ContextMenu
ref={pageContainerRef}
className={styles.pages}
Expand All @@ -708,22 +729,18 @@ const PDFViewer = React.forwardRef<PDFViewerHandle, PDFViewerProps>((props, pRef
},
} as ContextMenuItemType,
].concat(menuCollector.collect().sort((a, b) => (a.order || 0) - (b.order || 0)))}
style={
{
'--scale-factor': scale * PixelsPerInch.PDF_TO_CSS_UNITS,
'--scale-factor-origin': scale,
'--pdf-viewer-page-scale': scale * PixelsPerInch.PDF_TO_CSS_UNITS,
} as CSSProperties
}
>
{viewports.length === 0 && !loading && !pluginLoading && emptyTips}
{viewports.map((viewport, pageIndex) => {
const shouldRender = pageIndex >= renderRange[0] && pageIndex <= renderRange[1];
const top = `calc(var(--scale-factor) * ${PAGE_PADDING_TOP + Math.floor(topArrOrigin[pageIndex])}px)`;
const marginLeft = `max(${PAGE_PADDING_HORIZONTAL}px, (var(--pdf-viewer-page-width) - var(--scale-factor) * ${pageMaxWidth}px) * 0.5)`;
const left = `calc(${marginLeft} + var(--scale-factor) * ${Math.floor((pageMaxWidth - viewport.width) * 0.5)}px)`;
const width = `calc(var(--scale-factor) * ${Math.floor(viewport.width)}px)`;
const height = `calc(var(--scale-factor) * ${Math.floor(viewport.height)}px)`;
const gap = `calc(var(--scale-factor) * ${pageGap}px)`;
// const gap = `calc(var(--scale-factor) * ${pageGap}px)`;
return (
<div key={pageIndex} className={styles.pageContainer} style={{ width, height, marginBottom: gap }}>
<div key={pageIndex} className={styles.pageContainer} style={{ top, left, width, height }}>
{shouldRender && (
<>
<PDFPage className={styles.page} outputScale={outputScale} index={pageIndex} zoom={zoom} render={shouldRender} />
Expand All @@ -734,6 +751,16 @@ const PDFViewer = React.forwardRef<PDFViewerHandle, PDFViewerProps>((props, pRef
</div>
);
})}
<div
className={styles.pageBottomPlaceholder}
data-name="page-bottom-place-holder"
style={{
top: `calc(var(--scale-factor) * ${pageBottomOrigin}px + ${PAGE_PADDING_BOTTOM}px)`,
width: `calc(var(--scale-factor) * ${pageMaxWidth}px + 2 * ${PAGE_PADDING_HORIZONTAL}px)`,
}}
>
&nbsp;
</div>
</ContextMenu>
{(loading || !!pluginLoading) && loadingTips}

Expand Down
3 changes: 3 additions & 0 deletions packages/pdf-viewer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import PDFOpenFileButtonPlugin from './plugins/PDFOpenFileButtonPlugin';
import type { ToolbarButtonProps } from './ToolbarButton';
import ToolbarButton from './ToolbarButton';
import ToolbarPortal from './ToolbarPortal';
import PDFPageDebugPlugin from './plugins/PDFPageDebugPlugin';

export default PDFViewer;

Expand Down Expand Up @@ -47,3 +48,5 @@ export type * from './plugins/PDFOpenFileButtonPlugin';

export { PDFContextPrintPlugin, PDFContextMenuPlugin };
export type * from './plugins/PDFContextMenuPlugin';

export { PDFPageDebugPlugin };
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createUseStyles } from '@orca-fe/simple-jss';
import jssAutoPrefix from '@orca-fe/jss-plugin-auto-prefix';

const prefix = 'pdf-page-debug-plugin';

export default createUseStyles(
{
root: {
position: 'relative',
},
},
{
classNamePrefix: prefix,
plugins: [jssAutoPrefix({ prefix })],
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* eslint-disable react/no-unused-prop-types */
import React from 'react';
import { usePageCoverRenderer } from '../../context';

const PDFPageDebugPlugin = () => {
const renderPageCover = usePageCoverRenderer();

return (
<>
{renderPageCover((pageIndex, { viewport, zoom }) => (
<div>{pageIndex}</div>
))}
</>
);
};

export default PDFPageDebugPlugin;
3 changes: 3 additions & 0 deletions packages/pdf-viewer/src/plugins/PDFPageDebugPlugin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import PDFPageDebugPlugin from './PDFPageDebugPlugin';

export default PDFPageDebugPlugin;
65 changes: 65 additions & 0 deletions packages/pdf-viewer/src/plugins/PDFPageDebugPlugin/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { Point, ShapeDataType } from '@orca-fe/painter';
import { isGraphShapeType } from '@orca-fe/painter';
import { PixelsPerInch } from '../../utils';

/**
* 将 GraphShape 的 PDF 坐标转换为 CSS 坐标
* @param shape
*/
export function shapePdfToCss(shape: ShapeDataType): ShapeDataType {
if (isGraphShapeType(shape)) {
const newShape = { ...shape };
if ('x' in newShape && typeof newShape.x === 'number') {
newShape.x *= PixelsPerInch.PDF_TO_CSS_UNITS;
}
if ('y' in newShape && typeof newShape.y === 'number') {
newShape.y *= PixelsPerInch.PDF_TO_CSS_UNITS;
}
if ('width' in newShape && typeof newShape.width === 'number') {
newShape.width *= PixelsPerInch.PDF_TO_CSS_UNITS;
}
if ('height' in newShape && typeof newShape.height === 'number') {
newShape.height *= PixelsPerInch.PDF_TO_CSS_UNITS;
}
if ('point1' in newShape && Array.isArray(newShape.point1)) {
newShape.point1 = newShape.point1.map(v => v * PixelsPerInch.PDF_TO_CSS_UNITS) as Point;
}
if ('point2' in newShape && Array.isArray(newShape.point2)) {
newShape.point2 = newShape.point2.map(v => v * PixelsPerInch.PDF_TO_CSS_UNITS) as Point;
}

return newShape;
}
return shape;
}

/**
* 将 GraphShape 的 CSS 坐标转换为 PDF 坐标
* @param shape
*/
export function shapeCssToPdf(shape: ShapeDataType): ShapeDataType {
if (isGraphShapeType(shape)) {
const newShape = { ...shape };
if ('x' in newShape && typeof newShape.x === 'number') {
newShape.x /= PixelsPerInch.PDF_TO_CSS_UNITS;
}
if ('y' in newShape && typeof newShape.y === 'number') {
newShape.y /= PixelsPerInch.PDF_TO_CSS_UNITS;
}
if ('width' in newShape && typeof newShape.width === 'number') {
newShape.width /= PixelsPerInch.PDF_TO_CSS_UNITS;
}
if ('height' in newShape && typeof newShape.height === 'number') {
newShape.height /= PixelsPerInch.PDF_TO_CSS_UNITS;
}
if ('point1' in newShape && Array.isArray(newShape.point1)) {
newShape.point1 = newShape.point1.map(v => v / PixelsPerInch.PDF_TO_CSS_UNITS) as Point;
}
if ('point2' in newShape && Array.isArray(newShape.point2)) {
newShape.point2 = newShape.point2.map(v => v / PixelsPerInch.PDF_TO_CSS_UNITS) as Point;
}

return newShape;
}
return shape;
}
Loading
Loading