From 2485a077d0c7ee9d0a403550075bcff141a11d8d Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 21 Dec 2023 22:17:16 -0500 Subject: [PATCH] Following PDF link animates arrow to destination (fix #494) --- CHANGELOG.md | 2 ++ client/MessagePDF.coffee | 38 +++++++++++++++++++++++++++++++++++++- client/message.styl | 3 +++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9d6493..e9f55a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ instead of version numbers. ## 2023-12-21 * You can now select and copy text from PDFs. +* Clicking on a link in PDF now draws an arrow at the specific location + linked to, in addition to flipping to the relevant page. ## 2023-12-20 diff --git a/client/MessagePDF.coffee b/client/MessagePDF.coffee index b030f9f..27e216c 100644 --- a/client/MessagePDF.coffee +++ b/client/MessagePDF.coffee @@ -44,6 +44,8 @@ WrappedMessagePDF = React.memo ({file}) -> rootMargin: '100%' # within one screenful is "in view" canvasRef = useRef() textRef = useRef() + annotRef = useRef() + arrow = useRef() [progress, setProgress] = useState 0 [rendering, setRendering] = useState false [pageInput, setPageInput] = useState 1 @@ -113,6 +115,7 @@ WrappedMessagePDF = React.memo ({file}) -> setDims {width, height} unless dims.width == width and dims.height == height ## If out of view, destroy any rendering unless inView + arrow.current?.remove() replaceCanvas canvasRef, nullCanvas() if canvasRef.current.width return ## Keep track of latest rendered PDF @@ -142,7 +145,9 @@ WrappedMessagePDF = React.memo ({file}) -> Promise.all [renderTask.promise, page.getAnnotations()] .then ([rendered, annotationsLoaded]) => return if canceled + arrow.current?.remove() unless arrow.current?.pageNum == pageNum replaceCanvas canvasRef, canvas + setAnnotations [] # clear old annotations, while new annotations load ## Annotation links, based loosely on ## https://stackoverflow.com/a/20141227/7797661 newAnnotations = @@ -291,7 +296,7 @@ WrappedMessagePDF = React.memo ({file}) ->
-
{for annotation in annotations @@ -314,6 +319,37 @@ WrappedMessagePDF = React.memo ({file}) -> setPageBack [pageNum, ...pageBack] setPageForward [] setPageNum annotation.explicitPage + arrow.current?.remove() + arrow.current = a = document.createElement 'span' + annotRef.current.appendChild a + a.pageNum = annotation.explicitPage + a.className = 'fas fa-arrow-right' + a.style.left = "#{annotation.explicit[2]}px" + a.style.top = "#{annotation.explicit[3]}px" + a.style.transform = "rotate(-45deg) translate(-75%, -55%)" + a.animate [ + offset: 0.000 + opacity: 0.4 + #transform: "scale(1) rotate(0deg) translate(-100%, -75%)" + transform: "scale(3) rotate(0deg) translate(-100%, -55%)" + , + offset: 0.075 + opacity: 0.75 + transform: "scale(1) rotate(-45deg) translate(-75%, -55%)" + , + offset: 0.150 + transform: "scale(1.75) rotate(-45deg) translate(-90%, -55%)" + , + offset: 0.225 + transform: "scale(1) rotate(-45deg) translate(-75%, -55%)" + , + offset: 0.7 + opacity: 0.75 + , + offset: 1 + opacity: 0 + ], 5000 + .addEventListener 'finish', => a.remove() ### eslint-disable coffee/jsx-no-target-blank ### br::selection background: transparent + > .fas + color: magenta + opacity: 75% @media print .pdf > .annotations display: none