Skip to content

Commit

Permalink
Desktop: Add zoom feature on PDF viewer (laurent22#6748)
Browse files Browse the repository at this point in the history
  • Loading branch information
asrient committed Aug 28, 2022
1 parent ae300de commit 6498f94
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 63 deletions.
12 changes: 9 additions & 3 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -842,12 +842,12 @@ packages/app-mobile/components/BackButtonDialogBox.js.map
packages/app-mobile/components/CameraView.d.ts
packages/app-mobile/components/CameraView.js
packages/app-mobile/components/CameraView.js.map
packages/app-mobile/components/Dropdown.d.ts
packages/app-mobile/components/Dropdown.js
packages/app-mobile/components/Dropdown.js.map
packages/app-mobile/components/CustomButton.d.ts
packages/app-mobile/components/CustomButton.js
packages/app-mobile/components/CustomButton.js.map
packages/app-mobile/components/Dropdown.d.ts
packages/app-mobile/components/Dropdown.js
packages/app-mobile/components/Dropdown.js.map
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.d.ts
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js.map
Expand Down Expand Up @@ -1985,6 +1985,9 @@ packages/pdf-viewer/hooks/usePdfData.js.map
packages/pdf-viewer/hooks/useScaledSize.d.ts
packages/pdf-viewer/hooks/useScaledSize.js
packages/pdf-viewer/hooks/useScaledSize.js.map
packages/pdf-viewer/hooks/useScrollSaver.d.ts
packages/pdf-viewer/hooks/useScrollSaver.js
packages/pdf-viewer/hooks/useScrollSaver.js.map
packages/pdf-viewer/main.d.ts
packages/pdf-viewer/main.js
packages/pdf-viewer/main.js.map
Expand All @@ -1997,6 +2000,9 @@ packages/pdf-viewer/pdfSource.js.map
packages/pdf-viewer/pdfSource.test.d.ts
packages/pdf-viewer/pdfSource.test.js
packages/pdf-viewer/pdfSource.test.js.map
packages/pdf-viewer/ui/ZoomControls.d.ts
packages/pdf-viewer/ui/ZoomControls.js
packages/pdf-viewer/ui/ZoomControls.js.map
packages/plugin-repo-cli/commands/updateRelease.d.ts
packages/plugin-repo-cli/commands/updateRelease.js
packages/plugin-repo-cli/commands/updateRelease.js.map
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,9 @@ packages/pdf-viewer/hooks/usePdfData.js.map
packages/pdf-viewer/hooks/useScaledSize.d.ts
packages/pdf-viewer/hooks/useScaledSize.js
packages/pdf-viewer/hooks/useScaledSize.js.map
packages/pdf-viewer/hooks/useScrollSaver.d.ts
packages/pdf-viewer/hooks/useScrollSaver.js
packages/pdf-viewer/hooks/useScrollSaver.js.map
packages/pdf-viewer/main.d.ts
packages/pdf-viewer/main.js
packages/pdf-viewer/main.js.map
Expand All @@ -1986,6 +1989,9 @@ packages/pdf-viewer/pdfSource.js.map
packages/pdf-viewer/pdfSource.test.d.ts
packages/pdf-viewer/pdfSource.test.js
packages/pdf-viewer/pdfSource.test.js.map
packages/pdf-viewer/ui/ZoomControls.d.ts
packages/pdf-viewer/ui/ZoomControls.js
packages/pdf-viewer/ui/ZoomControls.js.map
packages/plugin-repo-cli/commands/updateRelease.d.ts
packages/plugin-repo-cli/commands/updateRelease.js
packages/plugin-repo-cli/commands/updateRelease.js.map
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"scripts": {
"buildParallel": "yarn workspaces foreach --verbose --interlaced --parallel --jobs 2 run build && yarn run tsc",
"buildSequential": "yarn workspaces foreach --verbose --interlaced run build && yarn run tsc",
"buildSequential": "yarn workspaces foreach --verbose --interlaced --topological run build && yarn run tsc",
"buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md",
"buildCommandIndex": "gulp buildCommandIndex",
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out ../joplin-website/docs/api/references/plugin_api packages/lib/services/plugins/api/",
Expand Down
49 changes: 15 additions & 34 deletions packages/pdf-viewer/VerticalPages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import { PdfData } from './pdfSource';
import Page from './Page';
import styled from 'styled-components';
import useScaledSize, { ScaledSizeParams } from './hooks/useScaledSize';
import useScrollSaver, { ScrollSaver } from './hooks/useScrollSaver';


const PagesHolder = styled.div<{ pageGap: number }>`
display: flex;
justify-content: center;
align-items: center;
flex-flow: column;
width: 100%;
width: fit-content;
min-width: 100%;
min-height: 100%;
row-gap: ${(props)=> props.pageGap || 2}px;
`;
Expand All @@ -22,15 +24,16 @@ export interface VerticalPagesProps {
anchorPage?: number;
rememberScroll?: boolean;
pdfId?: string;
zoom?: number;
container: MutableRefObject<HTMLElement>;
pageGap?: number;
showPageNumbers?: boolean;
}


export default function VerticalPages(props: VerticalPagesProps) {
const [containerWidth, setContainerWidth] = useState<number>(null);
const innerContainerEl = useRef<HTMLDivElement>(null);

const scaledSize = useScaledSize({
pdf: props.pdf,
pdfId: props.pdfId,
Expand All @@ -40,15 +43,23 @@ export default function VerticalPages(props: VerticalPagesProps) {
container: props.container,
innerContainerEl,
pageGap: props.pageGap,
zoom: props.zoom,
} as ScaledSizeParams);

useScrollSaver({
container: props.container,
scaledSize,
pdfId: props.pdfId,
rememberScroll: props.rememberScroll,
} as ScrollSaver);

useEffect(() => {
let resizeTimer: number = null;
let cancelled = false;

const updateWidth = () => {
if (cancelled) return;
setContainerWidth(innerContainerEl.current.clientWidth);
setContainerWidth(props.container.current.clientWidth);
};

const onResize = () => {
Expand All @@ -70,37 +81,7 @@ export default function VerticalPages(props: VerticalPagesProps) {
resizeTimer = null;
}
};
}, [props.pdf]);

useEffect(() => {
let scrollTimer: number = null;

const saveScroll = () => {
const scrollTop = props.container.current.scrollTop;
if (props.rememberScroll && props.pdfId) {
sessionStorage.setItem(`pdf.${props.pdfId}.scrollTop`, `${scrollTop}`);
}
};

const onScroll = () => {
if (scrollTimer) {
clearTimeout(scrollTimer);
scrollTimer = null;
}
scrollTimer = window.setTimeout(saveScroll, 200);
};

props.container.current.addEventListener('scroll', onScroll);

return () => {
props.container.current.removeEventListener('scroll', onScroll);
if (scrollTimer) {
clearTimeout(scrollTimer);
scrollTimer = null;
}
};

}, [props.container, props.pdfId, props.rememberScroll]);
}, [props.container, props.pdf]);

return (<PagesHolder pageGap={props.pageGap || 2} ref={innerContainerEl} >
{scaledSize ?
Expand Down
1 change: 1 addition & 0 deletions packages/pdf-viewer/hooks/useIsVisible.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useEffect, useState, MutableRefObject } from 'react';

const useIsVisible = (elementRef: MutableRefObject<HTMLElement>, rootRef: MutableRefObject<HTMLElement>) => {
const [isVisible, setIsVisible] = useState(false);

useEffect(() => {
let observer: IntersectionObserver = null;
if (elementRef.current) {
Expand Down
11 changes: 7 additions & 4 deletions packages/pdf-viewer/hooks/useScaledSize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ export interface ScaledSizeParams {
container: MutableRefObject<HTMLElement>;
innerContainerEl: MutableRefObject<HTMLElement>;
pageGap: number;
zoom: number;
}

const useScaledSize = ({ pdf, pdfId, containerWidth, rememberScroll, anchorPage, container, innerContainerEl, pageGap }: ScaledSizeParams) => {
const useScaledSize = ({ pdf, pdfId, containerWidth, rememberScroll, anchorPage, container, innerContainerEl, pageGap, zoom }: ScaledSizeParams) => {
const [scaledSize, setScaledSize] = useState<ScaledSize>(null);
const currentScaleSize = useRef(scaledSize);

useAsyncEffect(async (event: AsyncEffectEvent) => {
if (!pdf || !containerWidth) return;
// console.log('scaledSize calculation triggered');
const scaledSize_ = await pdf.getScaledSize(null, containerWidth - 10);
const effectiveWidth = Math.min(containerWidth - 20, 900) * (zoom || 1);
const scaledSize_ = await pdf.getScaledSize(null, effectiveWidth);
if (event.cancelled) return;

const oldScaleSize = currentScaleSize.current;
Expand All @@ -40,11 +42,12 @@ const useScaledSize = ({ pdf, pdfId, containerWidth, rememberScroll, anchorPage,
if (rememberScroll && pdfId && !oldScaleSize && !anchorPage) {
const scrollOffset = parseInt(sessionStorage.getItem(`pdf.${pdfId}.scrollTop`), 10) || null;
if (scrollOffset) {
container.current.scrollTop = scrollOffset;
// Adjusting it according to the new scale
container.current.scrollTop = scrollOffset * scaledSize_.scale;
// console.log('scroll set',container.current.scrollTop);
}
}
}, [pdf, pdfId, rememberScroll, anchorPage, containerWidth]);
}, [pdf, pdfId, rememberScroll, anchorPage, containerWidth, zoom]);

return scaledSize;
};
Expand Down
53 changes: 53 additions & 0 deletions packages/pdf-viewer/hooks/useScrollSaver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useRef, useEffect, MutableRefObject } from 'react';
import { ScaledSize } from '../pdfSource';

export interface ScrollSaver {
container: MutableRefObject<HTMLElement>;
scaledSize: ScaledSize;
pdfId: string;
rememberScroll: boolean;
}

const useScrollSaver = ({ container, scaledSize, pdfId, rememberScroll }: ScrollSaver) => {
const currentScaleSize = useRef(scaledSize);

useEffect(() => {
let scrollTimer: number = null;
const containerElement = container.current;

const saveScroll = () => {
if (!currentScaleSize.current) return;
const scale = currentScaleSize.current.scale;
const scrollTop = container.current.scrollTop / scale;
if (rememberScroll && pdfId) {
sessionStorage.setItem(`pdf.${pdfId}.scrollTop`, `${scrollTop}`);
}
};

const onScroll = () => {
if (scrollTimer) {
clearTimeout(scrollTimer);
scrollTimer = null;
}
scrollTimer = window.setTimeout(saveScroll, 200);
};

containerElement.addEventListener('scroll', onScroll);

return () => {
containerElement.removeEventListener('scroll', onScroll);
if (scrollTimer) {
clearTimeout(scrollTimer);
scrollTimer = null;
}
};
}, [container, pdfId, rememberScroll, currentScaleSize]);

useEffect(() => {
currentScaleSize.current = scaledSize;
} , [scaledSize]);

return scaledSize;
};

export default useScrollSaver;
18 changes: 14 additions & 4 deletions packages/pdf-viewer/miniViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useRef } from 'react';
import React, { useRef, useState } from 'react';
import useIsFocused from './hooks/useIsFocused';
import usePdfData from './hooks/usePdfData';
import VerticalPages from './VerticalPages';
import ZoomControls from './ui/ZoomControls';

export interface MiniViewerAppProps {
pdfPath: string;
Expand All @@ -13,6 +14,7 @@ export interface MiniViewerAppProps {
export default function MiniViewerApp(props: MiniViewerAppProps) {
const pdf = usePdfData(props.pdfPath);
const isFocused = useIsFocused();
const [zoom, setZoom] = useState<number>(1);
const containerEl = useRef<HTMLDivElement>(null);

if (!pdf) {
Expand All @@ -25,12 +27,20 @@ export default function MiniViewerApp(props: MiniViewerAppProps) {
return (
<div className={`mini-app${isFocused ? ' focused' : ''}`}>
<div className={`app-pages${isFocused ? ' focused' : ''}`} ref={containerEl}>
<VerticalPages pdf={pdf} isDarkTheme={props.isDarkTheme} anchorPage={props.anchorPage} pdfId={props.pdfId} rememberScroll={true}
container={containerEl} showPageNumbers={true} />
<VerticalPages
pdf={pdf}
isDarkTheme={props.isDarkTheme}
anchorPage={props.anchorPage}
pdfId={props.pdfId}
rememberScroll={true}
container={containerEl}
showPageNumbers={true}
zoom={zoom} />
</div>
<div className='app-bottom-bar'>
<div className='pdf-info'>
{pdf.pageCount} pages
<div style={{ paddingRight: '0.4rem' }}>{pdf.pageCount} pages</div>
<ZoomControls onChange={setZoom} zoom={zoom} />
</div>
<div>{isFocused ? '' : 'Click to enable scroll'}</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions packages/pdf-viewer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
"webpack-cli": "^4.10.0"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.1.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@joplin/lib": "workspace:^",
"pdfjs-dist": "^2.14.305",
"react": "16.13.1",
Expand Down
46 changes: 46 additions & 0 deletions packages/pdf-viewer/ui/ZoomControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import styled from 'styled-components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';

const ZoomGroup = styled.div<{ size: number }>`
display: flex;
justify-content: center;
align-items: center;
flex-flow: row;
color: var(--grey);
cursor: initial;
font-size: ${props => props.size}rem;
padding: 0.2rem 0.4rem;
user-select: none;
border-radius: 5px;
&:hover {
background: #7676764d;
}
svg:hover {
color: var(--secondary);
}
`;

export interface ZoomControlsProps {
zoom: number;
onChange: (zoom: number)=> void;
size?: number;
}

export default function ZoomControls(props: ZoomControlsProps) {

const zoomIn = () => {
props.onChange(Math.min(props.zoom + 0.25, 2));
};

const zoomOut = () => {
props.onChange(Math.max(props.zoom - 0.25, 0.5));
};

return (<ZoomGroup size={props.size || 0.8}>
<FontAwesomeIcon icon={faMinus} title="Zoom Out" style={{ paddingRight: '0.2rem', cursor: 'pointer' }} onClick={zoomOut} />
<span style={{ color: 'grey' }} >{props.zoom * 100}%</span>
<FontAwesomeIcon icon={faPlus} title="Zoom In" style={{ paddingLeft: '0.2rem', cursor: 'pointer' }} onClick={zoomIn} />
</ZoomGroup>);
}
19 changes: 3 additions & 16 deletions packages/pdf-viewer/viewer.css
Original file line number Diff line number Diff line change
Expand Up @@ -115,23 +115,9 @@ hr {
color: var(--grey);
}

.pdf-info>a {
padding: 0rem 0.4rem;
color: var(--blue);
cursor: pointer;
border: solid thin transparent;
border-radius: 5px;
user-select: none;
}

.pdf-info>a:hover {
border-color: var(--grey);
}

.app-pages {
display: flex;
justify-content: center;
align-items: start;
display: block;
margin: 0px auto;
overflow-x: hidden;
width: 100%;
height: 100%;
Expand All @@ -154,4 +140,5 @@ hr {
flex-direction: row;
justify-content: space-around;
align-items: center;
column-gap: 0.2rem;
}
Loading

0 comments on commit 6498f94

Please sign in to comment.