記一次react-hooks項目獲取圖表圖片集合併生成pdf的需求
需求: 獲取子組件中所有圖片的dom元素並生成圖片,再把生成的圖片轉化為pdf下載
-
難點
-
眾所周知,react是單向數據流,倡導f(data)⇒ UI的哲學, 並不建議過多直接操作dom,但是生成圖片的base64格式時使用的ant design charts 要求必須獲取ref才可以
-
邏輯流轉如下圖, 兄弟組件b發出按鈕click事件後告知a組件去收集自身組件內圖標的ref,由於是兄弟組件通訊,此處藉助了react-redux

-
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} /> }) -
a通過react-redux獲得b組件點擊指令後,開始按照3步驟收集dom到ref的current中,存在storage中,由於生成base64的過程是非同步的,所以在PageB中無法獲取實時的storage. 所以需要在PageB中通過狀態鎖讓頁面在跳轉的過程中只刷新一次
兄弟組件b點擊時同時生成一個同步的sessionStorage的標記作為在PageB中執行狀態鎖的依據 -
生成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') }) } -


