記一次react-hooks項目獲取圖表圖片集合併生成pdf的需求

需求: 獲取子組件中所有圖片的dom元素並生成圖片,再把生成的圖片轉化為pdf下載

  • 難點

    1. 眾所周知,react是單向數據流,倡導f(data)⇒ UI的哲學, 並不建議過多直接操作dom,但是生成圖片的base64格式時使用的ant design charts 要求必須獲取ref才可以

    2. 邏輯流轉如下圖, 兄弟組件b發出按鈕click事件後告知a組件去收集自身組件內圖標的ref,由於是兄弟組件通訊,此處藉助了react-redux

    3. a組件獲取所有圖標ref

      const myRef = useRef()
      
      //每個組件類似於此:
      <BoxPlot ref={handleRefCallback}/>
      
      const handleRefCallback = React.useCallback((dom) => {
            handleRefs(dom)
      }, [])   //此處通過回調來獲取ref
      				// 之前,每個charts子組件通過memo淺比較,但是使用了ref之後,memo不再生效,所有此處要通過useCallback來快取回調函數來實現memo的效果
      // charts子組件: 
      const child = React.forwardRef((props,ref) =>{
      		<Box {...config} chartRef={ref} />
      })
      
    4. a通過react-redux獲得b組件點擊指令後,開始按照3步驟收集dom到ref的current中,存在storage中,由於生成base64的過程是非同步的,所以在PageB中無法獲取實時的storage. 所以需要在PageB中通過狀態鎖讓頁面在跳轉的過程中只刷新一次

      兄弟組件b點擊時同時生成一個同步的sessionStorage的標記作為在PageB中執行狀態鎖的依據
      
    5. 生成pdf的程式碼: html2canvas 和 jspdf
      1.

    import html2canvas from 'html2canvas'
    import {jsPDF} from 'jspdf'
    
    const printDocument = async () => {
            await html2canvas(pdfRef.current, {
                allowTaint: true,
                scrollY: 0,
                scrollX: 0,
                useCORS: true,
                width: 1040,
                height: pdfRef.current.offsetHeight,
            }).then(function (canvas) {
                var contentWidth = 900
                var contentHeight = 5060
                var pageHeight = (contentWidth / 592.28) * 841.89
                var leftHeight = contentHeight
                var position = 0
                var imgWidth = 595.28
                var imgHeight = 3400
                var pageData = canvas.toDataURL('image/jpeg', 1.0)
                var pdf = new jsPDF('', 'pt', 'a4')
                if (leftHeight < pageHeight) {
                    pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
                } else {
                    while (leftHeight > 0) {
                        pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
                        leftHeight -= pageHeight
                        position -= 841.89
                        if (leftHeight > 0) {
                            pdf.addPage()
                        }
                    }
                }
                window.open(pdf.output('bloburl', {filename: 'new_file.pdf'}), '_blank')
            })
        }
    
    
    2. 
    
    import html2canvas from 'html2canvas'
    import {jsPDF} from 'jspdf'
    
    const printDocument = async () => {
             await html2canvas(pdfRef.current, {scrollY: -window.scrollY, scale: 1}).then((canvas) => {
                 const contentDataURL = canvas.toDataURL('image/png', 1.0)
                 let pdf = new jsPDF('l', 'mm', 'a4')
                 let imgWidth = 300
                 let pageHeight = pdf.internal.pageSize.height
                 let imgHeight = (canvas.height * imgWidth) / canvas.width
                 let heightLeft = imgHeight
                 let position = 0
    
                 pdf.addImage(contentDataURL, 'PNG', 0, position, imgWidth, imgHeight)
                 heightLeft -= pageHeight
    
                 while (heightLeft >= 0) {
                     position = heightLeft - imgHeight
                     pdf.addPage()
                     pdf.addImage(contentDataURL, 'PNG', 0, position, imgWidth, imgHeight)
                     heightLeft -= pageHeight
                 }
              //直接保存   // pdf.save('download.pdf') 
              //在線預覽  // window.open(pdf.output('bloburl', {filename: 'new_file.pdf'}), '_blank')
             })
    
        }
    
    
Tags: