title

记一次react-hooks项目获取图表图片集合并生成pdf的需求

需求: 获取子组件中所有图片的dom元素并生成图片,再把生成的图片转化为pdf下载

  • 难点

    1. 众所周知,react是单向数据流,倡导f(data)⇒ UI的哲学, 并不建议过多直接操作dom,但是生成图片的base64格式时使用的ant design charts 要求必须获取ref才可以
    2. 逻辑流转如下图, 兄弟组件b发出按钮click事件后告知a组件去收集自身组件内图标的ref,由于是兄弟组件通信,此处借助了react-redux

    1. 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} /> })
    1. a通过react-redux获得b组件点击指令后,开始按照3步骤收集dom到ref的current中,存在storage中,由于生成base64的过程是异步的,所以在PageB中无法获取实时的storage. 所以需要在PageB中通过状态锁让页面在跳转的过程中只刷新一次
    兄弟组件b点击时同时生成一个同步的sessionStorage的标记作为在PageB中执行状态锁的依据
    1. 生成pdf的代码: html2canvas 和 jspdf

    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') }) } ```
    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') }) } ```