一篇文章介绍前端PDF导出方案、html2canvas+jsPDF、导出canvas柱状图为PDF

如何将页面上的图表转换成图片?

图表是使用HTML Canvas绘制的,则可以通过Canvas元素的 toDataURL()方法。

接口将其转换成base64图片。

若图表是使用SVG绘制的,则可以用canvg等工具将SVG转换成Canvas,而后再转换成图片。

若图表使用其他技术绘制,则可以用html2canvas等工具将HTML元素转换成Canvas,而后再转换成图片。

用于绘制图表的原始数据发送给服务器,由服务器启动Puppeteer等无头浏览器来绘制图表。

如何将图片及标题文字等内容组装成PDF?

浏览器前端通过jsPDF等客户端工具将图表图片及标题文字元素组装成PDF。

PDF导出策略

1.前端截图、前端排版

该策略执行步骤:

1.该策略将页面上的图表相关的元素通过html2canvas等工具转为图片,或调用浏览器的getDisolayMedia()接口来截屏。

2.而后通过jsPDF等工具自行完成PDF的组装生成。

3.最后用javascript触发浏览器的下载操作,完成PDF的导出。

优点:

  1. 开发实现简单,难度低。

  1. 导出速度快:所有操作都在前端浏览器发生,不涉及与服务器之间的操作。

  1. 在前端浏览器所有操作中也不存在需要消耗大量CPU计算的场景,可以短时间内完成组装生成PDF。

缺点:

  1. 导出PDF效果不好,画质效果感比较差。

该策略也是“将图表导出成PDF”场景中主流方案,尤其是html2canvas+jsPDF方案,如果在搜索引擎搜索类似“ECharts导出PDF的字样”,大部分都是采用这一方案。

2.前端截图、后端排版

和第一种方案类似,只是把逻辑后移交给了后端,并没有解决截图质量的问题。

3.后端截图、后端排版

该策略执行步骤:

1.将用于绘制图表的原始数据或用于获取这些数据的参数发送给后端。

2.由后端启动Puppeteer等无头浏览器来绘制图表。

3.然后调用无头浏览器中的截图接口来获取图表图片。

4.最后由后端负责PDF排版组装。

这种策略好处很多,可以在后端使用LaTex这样的专业排版工具实现高质量的文档排版,呈现精致的报表PDF效果。

这一策略是以开发成本和导出速度为代价,换取高质量的PDF文档效果。

总结

若追求PDF导出速度,则采用前端截图+前端排版的方案,如html2canvas

若追求PDF导出文档质量,则可采用后端截图+后端排版的方案,如Puppeteer+LaTex

案例

使用vue3实现的一个demo

所需依赖:

代码实现:

<template>
  <h1 style="text-align: center;">html2canvas+jsPDF导出canvas柱状图</h1>
  <canvas class="canvasSty"
          ref="ctxRef"></canvas>
  <div style="text-align: center;">
    <button @click="toPdf">导出为PDF</button>
  </div>
</template>
​
<scriptsetup>
import { ref, onMounted } from'vue'
import html2canvas from'html2canvas'
import jsPDF from'jspdf'
import*as echarts from'echarts'  //引入echarts
​
let ctxRef=ref()
// 一个简单的canvas柱状图
function drawCanvas () {
  // 基于准备好的dom,初始化echarts实例
  var myChart=echarts.init(ctxRef.value);
  let xData= ["枯水期", "丰水期", "平水期"]
  let seriesData= [
    {
      name: "监测值",
      value: [95.04, 96.13, 94.76]
    },
    {
      name: "标准值",
      value: [94.96, 96.02, 94.68]
    }
  ]
  // 指定图表的配置项和数据
  var option= {
    "grid": {
      left: '4%',
      right: '6%',
      bottom: '15%',
      top: '23%',
      containLabel: true,
    },
    "backgroundColor": "black",
    "color": ["#1DB750", "#C7F36A"],
    "tooltip": {
      "trigger": "axis",
    },
    "legend": {
      "data": seriesData,
      right: '4%',
      top: '10%',
      align: 'left',
      icon: 'rect',
      itemWidth: 12,
      itemHeight: 12,
      "textStyle": {
        "fontSize": 14,
        color: "#fff"
      }
    },
    "xAxis": {
      showBackground: true,
      nameTextStyle: {
        "color": "#c0c3cd",
        "padding": [0, 0, -10, 0],
        "fontSize": 14
      },
      axisLine: {
        show: true, //隐藏X轴轴线
        lineStyle: {
          color: '#555f58'
        }
      },
      axisLabel: {
        interval: 0,
        textStyle: {
          color: '#fff'//坐标轴字颜色
        },
        margin: 15
      },
      axisTick: {
        show: false//隐藏X轴刻度
      },
      splitLine: {     //网格线
        "show": false
      },
      data: xData,
      type: "category"
    },
    "yAxis": {
      name: '单位:mg/L',
      nameTextStyle: {
        fontSize: 14,
        color: "#fff",
      },
      axisLine: {
        show: true, //隐藏X轴轴线
        lineStyle: {
          color: 'rgba(220,220,220,0.3)'
        }
      },
      axisTick: {
        show: false//隐藏X轴刻度
      },
      axisLabel: {
        textStyle: {
          color: '#fff'
        }
      },
      splitLine: {     //网格线
        "show": true,
        lineStyle: {
          color: 'rgba(220,220,220,0.3)'
        }
      }
    },
    "series": function () {
      let series= []
      for (let i=0; i<seriesData.length; i++) {
        let serie= {
          "name": seriesData[i].name,
          "type": "bar",
          "barWidth": "13%",
          "data": seriesData[i].value
        }
        series.push(serie)
      }
      return series
    }()
  }
​
  // 使用刚指定的配置项和数据显示图表。
  myChart.setOption(option);
}
onMounted(() => {
  drawCanvas()
})
​
// 图片转pdf方法
const toPdf= () => {
  console.log(html2canvas(ctxRef.value));
​
  html2canvas(ctxRef.value).then(_d=> {
    console.log(_d);
    console.log(_d.toDataURL('image/png'));
​
    // 转为base64格式
    let imgUrlData=_d.toDataURL('image/png', 1.0)
    /*
     new jsPDF(orientation, unit, format, compress)
     orientation(方向):默认值为"portrait"  orientation:'landscape' 横向
     unit:告诉jsPDF 在哪个单元工作 "pt(点)"、"mm(默认)"、"cm"、"in"
     format(格式):"a4(默认)"、"a3"、"a5"、"letter"、"legal"
​
    const pdfDoc = new jsPDF({
      orientation:"landscape", //方向
      unit:"in", //pdf大小计量单位
      format:[4,2] //自定义第一页pdf页面大小,按html元素比例设定
    })
    */
    const pdfDoc=newjsPDF('', 'pt', 'a4')
    pdfDoc.addImage(imgUrlData, 'jpeg', 0, 0, 600, 300)
    pdfDoc.save('导出的图表pdf.pdf')
  })
}
</script>
​
<style scoped>
.canvasSty {
  position: relative;
  width: 400px;
  height: 200px;
  left: 50%;
  transform: translateX(-50%);
}
</style>
​

浏览器页面展示:

点击导出为PDF后打开pdf图展示:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.怪兽

希望大家能够多多支持,我会继续

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值