The project requirements

Download parts of the generated analysis report (the web page displays mainly echart’s various charts) as a PDF file.

First implementation

  1. NPM installs JSPDF and HTML2Canvas

npm install jspdf html2canvas

  1. Introduce JSPDF and HTML2Canvas
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
Copy the code
  1. jsx
<div className="chartList" ref={pdfWrap => this.refToPDF1 = pdfWrap}>
  <div className="chartWrap"> < H2 > online sales trend of shampoo category </ H2 > < P > Sales growth reflects the increasing trend of consumers' spending in this category. Assuming that the sales growth is fixed, the faster the sales growth is, the more consumers are willing to spend, and the consumption upgrading trend is obvious </p> <Echarts {... This. BarChartOne} /> // this. BarChartOne} />Copy the code
  1. Click the Download button to invoke the download PDF file method
@autobind
downloadPdf() {
    const pdf = new jsPDF({ format: 'a4' });
    const width = pdf.internal.pageSize.getWidth();
    
    const promistList = [
      html2canvas(this.refToPDF1)
    ];
    Promise.all(promistList).then(canvases => {
      canvases.map(canvas => {
        pdf.addImage(canvas.toDataURL(), 'JPEG', 0, 0, width, canvas.height * width / canvas.width, null, 'NONE');
        // pdf.addPage();
      });
      return true; }). Then (() => {PDF.internal. ScaleFactor = 1.33; pdf.save('output.pdf');
    });
}
Copy the code

Optimized implementation mode

If there are too many pages to generate PDF (above 50), this approach becomes unwieldy. After several thoughts, we decided to adopt the following scheme.

  1. Define the DOM container that you want to generate PDF with dynamically updated components
<div className="reportList" ref={pdfWrap => this.refToPDF1 = pdfWrap}>
  {this.chartComp}
</div>
Copy the code
  1. When you click the Download button, update the contents of the container repeatedly, taking a screenshot after each update
@observable chartComp = null; .downloadPdf() {
  const pdf = new jsPDF({ orientation: 'l', unit: 'px', format: [576, 1152] });

  showDownLoadTip('Generating analysis report... '); This.addallchartimgtopdf (PDF).then(() => {PDF.internal. ScaleFactor = 1.33; pdf.save('output.pdf');
    hideDownLoadTip();
  });
}

@autobind
async addAllChartImgToPdf(pdf) {
    const { store: { reportList } } = this.props;
    const width = pdf.internal.pageSize.getWidth();

    for (let i = 0, len = reportList.reportData && reportList.reportData.length; i < len; i++) {
      const item = reportList.reportData[i];
      const key = item.key.split('$') [0]; const ChartComp = chartTypeCompMap[key];if(ChartComp) {// Update the container contents to be captured this. ChartComp = <div key={I} className="chartList"> <ChartComp dataItem={item} /> </div>; // Wait for Echart to render the chart again, the 400ms is not available here, it is only a local test at the moment that this value can be truncated to the full Echart chart. await new Promise(resolve => {setTimeout(() => {
            resolve();
          }, 400);
        });

        await html2canvas(this.refToPDF1, {
          scale: 2,
          useCORS: true,
          width: 1152,
          letterRendering: true,
        }).then((canvas) => {
          pdf.addImage(canvas.toDataURL(), 'JPEG', 0, 0, width, canvas.height * width / canvas.width, null, 'NONE'); ShowDownLoadTip (' Generating an analysis report${i + 1}/${len}. `);if (i < len - 1) pdf.addPage({ orientation: 'l', unit: 'px', format: [576, 1152] }); }); }}}Copy the code

The problem record

Paging processing: if the content is more need paging is relatively troublesome. Manual pagination is involved in this project as it relates to chart integrity. In plain English, you manually split the content into several DOM fragments, then generate a graph for each DOM, and then manually append a blank page after each graph is added.

If you want to download the content directly without previewing it or displaying it on the current page, the downloaded content cannot be placed in display: None. Hide the field, and you can’t set the opacity to 0 (these two practices prove html2Canvas can’t generate a correct graph), but use fixed position to -9999px.

A problem was found when downloading as a PDF: the font size in the original HTML did not work. Font variant: normal; .

Another problem: sometimes the background color of some elements is missing in downloaded PDF files. Font feature-settings: normal; .

Above, if you have any questions, please get feedback ~ 😊