//
  
    
  
   解决了 pdf分页上一页与下一页内容错开样式不好看问题
  
  
   //
  
  
   ele:需要截图的元素节点对象 document.querySelector('#pdfDom')
  
  
   //
  
  
   pdfFileName:下载的文件名称
  
  const PdfLoader = (ele, pdfFileName) =>
  
   {
    ele.style.fontFamily
  
  = '宋体'
  
   ;
    ele.style.padding
  
  = '0 20px'
  
   ;
  
  
   //
  
  
   预留一定的时间给dom页面渲染完成 (如果你能保证dom已经渲染完成了包括图片 才去进行下面转化操作那就可以不用这个延迟器)
  
  setTimeout(() =>
  
   {
        html2Canvas(ele, {
  
  
   //
  
  
   dpi: 300, // 清晰度
  
  scale: 1,
  
   //
  
  
   将Canvas放大倍数 可以获得更具清晰的图片内容
  
  
   //
  
  
   !!!注意如果你生成的元素内容非常多是一个非常长列表 建议scale不要写太高或者删除这个属性 ,因为html2Canvas会吧内容转成
  
  
   //
  
  
   base64 会有一定的内容上限 最终返回没有base64编码(目前我尝试过生成55页的PDF,估计上限在70-100页)
  
  useCORS:
  
   true
  
  ,
  
   //
  
  
   是否允许跨域
  
  allowTaint:
  
   false
  
  
   ,
            height: ele.offsetHeight,
            width: ele.offsetWidth,
            windowWidth: document.body.scrollWidth,
            windowHeight: document.body.scrollHeight,
        }).then(canvas
  
  =>
  
   {
  
  
   //
  
  
   未生成pdf的html页面高度
  
  
   var
  
  leftHeight =
  
   canvas.height;
  
  
   var
  
  a4Width = 595.28
  
   var
  
  a4Height = 841.89
  
   //
  
  
   一页pdf显示html页面生成的canvas高度;
  
  
   var
  
  a4HeightRef = Math.floor(canvas.width / a4Width *
  
   a4Height);
  
  
   //
  
  
   pdf页面偏移
  
  
   var
  
  position = 0
  
   ;
  
  
   var
  
  pageData = canvas.toDataURL('image/jpeg', 1.0);
  
   //
  
  
   生成的base64 如果你只是要图片 到这里就可以拿到base64图片编码(可以查一下base64转二进制 使用new FormData对象传给后端到服务器)
  
  
   var
  
  pdf =
  
   new
  
  JsPDF('x', 'pt', 'a4');
  
   //
  
  
   生成A4内容大小的pdf每页 更多参数配置可以看看下面的网站
  
  
   //
  
  
   https://blog.csdn.net/weixin_42333548/article/details/107630706
  
  
   var
  
  index = 1
  
   ,
                canvas1
  
  = document.createElement('canvas'
  
   ),
                height;
            pdf.setDisplayMode(
  
  'fullwidth', 'continuous', 'FullScreen'
  
   );
  
  
   //
  
  
   处理 pdf 上一页 与 下一页内容之间交叉不好看的断点样式
  
  
   //
  
  
   并且把内容转成二进制 生成pdf文件
  
  
   function
  
  
   createImpl(canvas) {
  
  
   if
  
  (leftHeight > 0
  
   ) {
                    index
  
  ++
  
   ;
  
  
   var
  
  checkCount = 0
  
   ;
  
  
   if
  
  (leftHeight >
  
   a4HeightRef) {
  
  
   var
  
  i = position +
  
   a4HeightRef;
  
  
   for
  
  (i = position + a4HeightRef; i >= position; i--
  
   ) {
  
  
   var
  
  isWrite =
  
   true
  
  
   for
  
  (
  
   var
  
  j = 0; j < canvas.width; j++
  
   ) {
  
  
   var
  
  c = canvas.getContext('2d').getImageData(j, i, 1, 1
  
   ).data
  
  
   if
  
  (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff
  
   ) {
                                    isWrite
  
  =
  
   false
  
  
   break
  
  
  
  
   if
  
  
   (isWrite) {
                                checkCount
  
  ++
  
   if
  
  (checkCount >= 10
  
   ) {
  
  
   break
  
  
   }
  
  
   else
  
  
   {
                                checkCount
  
  = 0
  
   height
  
  = Math.round(i - position) ||
  
   Math.min(leftHeight, a4HeightRef);
  
  
   if
  
  (height <= 0
  
   ) {
                            height
  
  =
  
   a4HeightRef;
                    }
  
  
   else
  
  
   {
                        height
  
  =
  
   leftHeight;
                    canvas1.width
  
  =
  
   canvas.width;
                    canvas1.height
  
  =
  
   height;
  
  
   var
  
  ctx = canvas1.getContext('2d'
  
   );
                    ctx.drawImage(canvas,
  
  0, position, canvas.width, height, 0, 0
  
   , canvas
                        .width, height);
  
  
   if
  
  (position != 0
  
   ) {
                        pdf.addPage();
                    pdf.addImage(canvas1.toDataURL(
  
  'image/jpeg', 1.0), 'JPEG', 0, 0
  
   , a4Width,
                        a4Width
  
  /
  
   canvas1.width
  
  *
  
   height);
                    leftHeight
  
  -=
  
   height;
                    position
  
  +=
  
   height;
  
  
   if
  
  (leftHeight > 0
  
   ) {
  
  
   //
  
  
   给pdf文件 添加全屏水印
  
  
   //
  
  
   const base64 = '';  // 吧你要添加的水印内容搞成一张小图片 然后手动去转成base64编码 放在这里就可以了
  
  
   //
  
  
   for (let i = 0; i < 6; i++) {
  
  
   //
  
  
   for (let j = 0; j < 5; j++) {
  
  
   //
  
  
   const left = (j * 120) + 20;
  
  
   //
  
  
   pdf.addImage(base64, 'JPEG', left, i * 150, 20, 30);
  
  
   //
  
  
   };
  
  
   //
  
  
   };
  
  pdf.addImage(pageData, 'JPEG', 0, i * 150, 20, 30
  
   );
                        setTimeout(createImpl,
  
  500
  
   , canvas);
                    }
  
  
   else
  
  
   {
                        pdfSave()
  
  
   //
  
  
   当内容未超过pdf一页显示的范围,无需分页
  
  
   if
  
  (leftHeight <
  
   a4HeightRef) {
                pdf.addImage(pageData,
  
  'JPEG', 0, 0, a4Width, a4Width / canvas.width *
  
   leftHeight);
                pdfSave()
            }
  
  
   else
  
  
   {
  
  
   try
  
  
   {
                    pdf.deletePage(
  
  0
  
   );
                    setTimeout(createImpl,
  
  500
  
   , canvas);
                }
  
  
   catch
  
  
   (err) {
                    console.log(err);
  
  
   function
  
  
   pdfSave() {
  
  
   //
  
  
   pdf文件生成完毕 自动下载到客户本地
  
  pdf.save(pdfFileName + '.pdf'
  
   )
                setTimeout(()
  
  =>
  
   {
  
  
   //
  
  
   吧pdf文件上传到服务器
  
  let base64 = pdf.output("datauristring"
  
   );
                    let file
  
  = convertBase64ToFile(base64, "报告"
  
   );
                    let formData
  
  =
  
   new
  
  
   FormData();
                    formData.append(
  
  "file"
  
   , file);
  
  
   //
  
  
   直接post 接口吧formData对象传给后端接口去让后端去获取就可以了
  
  }, 1000
  
   )
    },
  
  500
  
   )
const convertBase64ToFile
  
  = (urlData, filename) =>
  
   {
  
  
   var
  
  arr = urlData.split('base64,'
  
   );
  
  
   var
  
  type = arr[0].match(/:(.*?);/)[1
  
   ];
  
  
   var
  
  fileExt = type.split('/')[1
  
   ];
  
  
   var
  
  bstr = atob(arr[1
  
   ]);
  
  
   var
  
  n =
  
   bstr.length;
  
  
   var
  
  u8arr =
  
   new
  
  
   Uint8Array(n);
  
  
   while
  
  (n--
  
   ) {
        u8arr[n]
  
  =
  
   bstr.charCodeAt(n);
  
  
   return
  
  
   new
  
  File([u8arr], filename + "." +
  
   fileExt, {
        type: type
export
  
  
   default
  
  PdfLoader;
  
   3.页面使用
  
  import PdfLoader from './pdf.js';
PdfLoader(document.getElementById('pdfDom'),'测试文件')
  
   
    注意:如果你要生成的元素内容有图片 一定要把图片转成base64编码 在页面上去展示不然 在生成pdf或者canvas的时候会跨域,
   
  
  
   
    如果你们的图片地址在腾讯云或者阿里上这种第三方 然后在使用地址转base64遇到了跨域问题 去找后端解决 让他们去配置白名单关闭跨域,(你可以尝试使用百度图库网络地址转base64不会遇到跨域问题)。
   
  
  // 图片地址转base64
// url:https:xxxxx
const getBase64Image = function(url) {
    function newImg(url) {
        return new Promise((resolve, reject) => {
            let image = new Image();
            image.crossOrigin = 'Anonymous';
            image.src = url;
            image.onload = function() {
                resolve(image);
    return newImg(url).then((img) => {
        let canvas = document.createElement("canvas");
        canvas.width = img.width;
        canvas.height = img.height;
        let ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0, img.width, img.height);
        let ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
        let dataURL = canvas.toDataURL("image/" + ext);
        return dataURL;
需求建议:如果你是通过条件查询后端返回数据 来动态生成这个元素 又不想让用户看到你渲染元素的过程,可以像我一样做个一个全局的遮罩弹窗并且展示下载进度 可以有一个更好的交互体验 执行完成后全部统一关闭。
注意:如果你生成的图片就是一个60-100页的pdf内容 想解决分页上限的导致的生成的pdf内容白屏或者黑屏 ,那你可以分段去生成base64编码 在这一步做同步并且吧截图模块做拆分,吧拿到的base64编码进行合并成一个base64编码
最终在去执行写入pdf文件的步骤(下面是base64编码合并),不需要的可以跳过这个代码。
// 友情提示 你要保存每个生成base64的编码以外 还要保存他们的元素高度进行求和不然图片的还原大小会不一致
let img1 = 'data:image/png;base64,iVBORwxxx'// 请替换base64内容 这个太长了我只是占位
let img2 = 'data:image/png;base64,iVBORwxxx'
     * 合并多张图片,返回新的图片
     * @param {Array} list 图片url数组
     * @param {Number} cwith 画布宽度 默认500
     * @param {Number} cheight 画布高度 默认500
    function mergeImgs(list, cwith = 500, cheight = 500) {
        return new Promise((resolve, reject) => {
            const baseList = []
            const canvas = document.createElement('canvas')
            canvas.width = cwith
            canvas.height = cheight * list.length
            const context = canvas.getContext('2d')
            list.map((item, index) => {
                const img = new Image()
                img.src = item
                // 跨域
                img.crossOrigin = 'Anonymous'
                img.onload = () => {
                    context.drawImage(img, 0, cheight * index, cwith, cheight)
                    const base64 = canvas.toDataURL('image/png')
                    baseList.push(base64)
                    if (baseList[list.length - 1]) {
                        console.log(baseList)
                        // 返回新的图片
                        resolve(baseList[list.length - 1])
    const urlList = [img1, img2]
    mergeImgs(urlList).then(base64 => {
        const imgDom = document.createElement('img')
        imgDom.src = base64
        document.body.appendChild(imgDom)
最终给大家看看生成的pdf文件打开的结果。