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

Flickering issue when scaling the entire pdf #1760

Closed
4 tasks done
ShashanKV98 opened this issue Apr 13, 2024 · 6 comments
Closed
4 tasks done

Flickering issue when scaling the entire pdf #1760

ShashanKV98 opened this issue Apr 13, 2024 · 6 comments
Labels
question Further information is requested

Comments

@ShashanKV98
Copy link

Before you start - checklist

  • I followed instructions in documentation written for my React-PDF version
  • I have checked if this bug is not already reported
  • I have checked if an issue is not listed in Known issues
  • If I have a problem with PDF rendering, I checked if my PDF renders properly in PDF.js demo

Description

Hi. I had some issues when dealing with zooming a document with all pages in a column (not just a single page). I browsed the issues in this repo, and found a sandbox link from @wojtekmaj #875 (comment).

This was for a single page in the viewport, so I extended it to include all the pages. The first page seems to render just fine as I use the slider, but the rest of the pages tend to flicker a lot. I've been on this for days but couldn't figure out if this is because of React or page.render refreshing the entire page from scratch at every new scale value or something else that I'm missing.

I'm also curious about the recommended approach for handling zooming with this library. Is it possible to get it close to how pdf.js viewer handles zooming?

Thanks and appreciate the work put into this library.

Steps to reproduce

Attached a codesandbox link showing this behavior.

https://codesandbox.io/p/sandbox/react-pdf-prevent-flash-with-scale-forked-pwcw8z

Expected behavior

Flickering for all pages is supposed to be minimal to none

Actual behavior

The first page doesn't flicker but the subsequent pages do.

Additional information

No response

Environment

  • Browser (if applicable): Edge, Chrome
  • React-PDF version: 7.7.1
  • React version: 18.2.0
  • Webpack version (if applicable):
@ShashanKV98 ShashanKV98 added the bug Something isn't working label Apr 13, 2024
@jblack020
Copy link

jblack020 commented May 16, 2024

Has a solution been posted for this? Would love to see a working fix for rendering the whole pdf without flashes. @wojtekmaj

@gfargo
Copy link

gfargo commented Jun 5, 2024

Having a similar issue, I've tried all of the provided solutions regarding hiding the canvas while the PDF is rendering e.g. #1340 & #1279 however these do not have any impact on the canvas disappearing when "zooming" the document.

The demo PDFJS does not have this issue and unfortunately this flicker is creating a horrible UIX when zooming/panning around a single page PDF.

From what I can tell, this wasn't an issue when using renderMode="svg" however that has been deprecated in the newer versions 😕 any guidance from others would be greatly appreciated!

@jblack020
Copy link

jblack020 commented Jun 5, 2024

This solution worked for me, albeit I have no idea why since this was quite long ago on a project I've since stopped working on. I'm quite new to React in general, but perhaps this may be of use.

My main PDFView component:

export const PDFView = ({ pdfURL }) => {
  const [numPages, setNumPages] = useState(null)

  // Config variables
  const initialWidth = 405 
  const stepSize = 0.5
  const magnifierMax = 4

  const { scale, newScale, pageVisible, scaleUp, scaleDown, scaleReset, togglePageVisibility } =
    usePDFScaling(initialWidth, stepSize, magnifierMax)

  useElectronAPI(scaleUp, scaleDown, scaleReset)

  return (
    <Document file={pdfURL} onLoadSuccess={({ numPages }) => setNumPages(numPages)}>
      {numPages &&
        Array.from(new Array(numPages), (_el, index) => (
          <React.Fragment key={`container_${index + 1}`}>
            <Page
              className={`mb-2 ${pageVisible ? 'visible' : 'hidden'}`}
              key={`front_page_${index + 1}`}
              pageNumber={index + 1}
              scale={pageVisible ? scale : newScale}
              width={initialWidth}
              onRenderSuccess={pageVisible ? null : togglePageVisibility}
            />
            <Page
              className={`mb-2 ${pageVisible ? 'hidden' : 'visible'}`}
              key={`back_page_${index + 1}`}
              pageNumber={index + 1}
              scale={pageVisible ? newScale : scale}
              width={initialWidth}
              onRenderSuccess={pageVisible ? togglePageVisibility : null}
            />
          </React.Fragment>
        ))}
    </Document>
  )
}

useElectronAPI hook:

export const useElectronAPI = (scaleUp, scaleDown, scaleReset) => {
  useEffect(() => {
    const handleZoom = (_event, action) => {
      switch (action) {
        case 'zoom-in':
          scaleUp()
          break
        case 'zoom-out':
          scaleDown()
          break
        case 'zoom-reset':
          scaleReset()
          break
      }
    }
    window.electronAPI.onMenuAction(handleZoom)

    return () => {
      window.electronAPI.removeMenuActionListener(handleZoom)
    }
  }, [scaleUp, scaleDown, scaleReset])
}

usePDFScaling hook:

export const usePDFScaling = (initialWidth, stepSize, magnifierMax) => {
  const [scale, setScale] = useState(1.0)
  const [newScale, setNewScale] = useState(1.0)
  const [pageVisible, setPageVisible] = useState(true)

  const minWidth = initialWidth / magnifierMax
  const maxWidth = initialWidth * magnifierMax

  const scaleUp = () => {
    setNewScale((prevScale) => Math.min(prevScale + stepSize, maxWidth / initialWidth))
  }

  const scaleDown = () => {
    setNewScale((prevScale) => Math.max(prevScale - stepSize, minWidth / initialWidth))
  }

  const scaleReset = () => {
    const windowWidth = window.innerWidth
    const resetScale = (windowWidth * 0.45) / initialWidth
    setNewScale(resetScale)
    setScale(resetScale)
  }

  useEffect(() => {
    scaleReset()
  }, [])

  const togglePageVisibility = () => {
    if (newScale !== scale) {
      setPageVisible(!pageVisible)
      setScale(newScale)
    }
  }

  return { scale, newScale, pageVisible, scaleUp, scaleDown, scaleReset, togglePageVisibility }
}

@ShashanKV98
Copy link
Author

Thanks. But it seems to have the same issue. I switched the electron API part with buttons in react for scaling. I am beginning to think that since page.render re creates from scratch, pages with heavy graphics tend to flicker or take time to render. I wonder if it's possible to just render the portion of the pdf in the viewport and not all the pages with every scale change or maybe utilizing an offscreencanvas, but I'm not sure how to go about it.

@wojtekmaj wojtekmaj added question Further information is requested and removed bug Something isn't working labels Jun 6, 2024
@wojtekmaj
Copy link
Owner

Duplicate of #1705
Duplicate of #875
Duplicate of #418

@wojtekmaj wojtekmaj closed this as not planned Won't fix, can't repro, duplicate, stale Jun 6, 2024
@ShashanKV98
Copy link
Author

Hi @wojtekmaj I've seen those issues but I just can't get the performance to be even close to how pdfjs does it (seems like it only renders the text part) . The conditional rendering does a decent job but it is not robust for bigger files. Can you please maybe point me in the direction of how to handle this or if you have any ideas I might try?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants