Bootstrap

调用浏览器局部打印,空白、只有一页问题、火狐兼容

调用浏览器局部打印,空白、只有一页问题、火狐兼容

项目中需要局部打印页面的图表,图表类型多而杂,因此html结构中包含了canvas和iframe,iframe中又包含canvas。
刚开始根据网上的教程试了原生打印和插件打印(react-to-print),还有html2canvas转换页面都不能满足。要么iframe内空白,要么canvas空白。所以我需要结合以上的方案一起解决。
并且我需要实现局部打印,利用网上的做法复制innerHTML,复制过来iframe内部是空的,打印出来的内容自然是空白的,利用很多方法依旧不可行。直接appendChild会破坏页面的布局,也不行。

最终解决方案:
1.将局部打印内容iframe内部的canvas先转换成图片并添加到iframe外部,隐藏iframe,这样看起来内容没有变化,只是动态图表变成了图片。
2.第一步处理完,现在直接将一整个局部打印区域通过html2canvas转换成canvas,然后转换成图片。
3.创建一个iframe,内容是打印预览。创建一个div,将上面的图片插入div中。iframe和div都放到body中。
4.将div内容写入iframe中。iframe调用打印。
5.删除iframe转换的图片,将iframe重新显示。

如果大家的页面结构没有iframe、canvas,是不需要像我这么复杂的。想要局部打印的直接将dom的innerHTML写入iframe内部就可以了。

只有一页的问题
我出现这个问题是因为,我包裹内容的div有position:fixed;的属性,把他改成relative就好了。看网上还有人说,overflow要设置为visible,可以看看自己具体问题,都是样式的原因。

谷歌正常打印,火狐空白
局部打印iframe需要添加一个属性src,值为“javascript:”,不然的话值就是“about:blank”,会出现空白。我加上之后其他浏览器并无影响所以没有做判断,如果需要的话判断一下浏览器类型。

下面是我的代码,可以参考一下。

const el = document.getElementById(id); // 局部打印区域
  if (!el) return;

  // 创建iframe打印预览
  const iframe = document.createElement('IFRAME');
  let doc: any = null;

  // 兼容火狐浏览器 解决iframe的src是about:blank 内容是空白的问题
  iframe.setAttribute('src', 'javascript:');
  iframe.setAttribute(
    'style',
    'position:absolute;width:0px;height:0px;left:500px;top:500px;',
  );
  document.body.appendChild(iframe);

  // 找到打印区域的iframe 获取内部canvas 转换成图片 添加到iframe前 隐藏iframe
  const iframes = el.querySelectorAll('iframe');
  iframes.forEach(item => {
    const itemChild = item.contentDocument?.body.querySelector('canvas');
    if (itemChild) {
      const image = new Image();
      image.src = itemChild.toDataURL('image/png');
      image.setAttribute('width', item.offsetWidth as any);
      image.setAttribute('height', item.offsetHeight as any);
      image.style.width = item.offsetWidth + 'px';
      image.style.height = item.offsetHeight + 'px';
      item.parentElement?.insertBefore(image, item);
      item.style.display = 'none';
    }
  });

  // 处理完所有iframe html2canvas转换一次el打印区域 得到的canvas转换成图片 赋值给打印iframe
  if (isIFrame(iframe) && iframe.contentWindow) {
    doc = iframe.contentWindow.document;

    html2canvas(el, {
      scale: 2,
      allowTaint: true,
      useCORS: true,
      width: el.offsetWidth,
      height: el.offsetHeight,
      windowWidth: el.scrollWidth,
      windowHeight: el.scrollHeight,
    }).then(canvas => {
      const context = canvas.getContext('2d') as any;
      context.mozImageSmoothingEnabled = false;
      context.webkitImageSmoothingEnabled = false;
      context.msImageSmoothingEnabled = false;
      context.imageSmoothingEnabled = false;
      const src64 = canvas.toDataURL();
      const contentWidth = canvas.width;
      const contentHeight = canvas.height;
      const imgWidth = el.clientWidth; // 根据打印区域宽度设定
      const imgHeight = (imgWidth / contentWidth) * contentHeight;
      const img = new Image();
      const div = document.createElement('div');
      div.appendChild(img);
      img.setAttribute('src', src64);
      img.setAttribute('width', imgWidth as any);
      img.setAttribute('height', imgHeight as any);
      img.setAttribute('id', 'imgs');
      document.body.appendChild(div);
      doc.open();
      doc.write(div.innerHTML);
      doc.close();

      // 图片加载完毕获取iframe的焦点调取打印
      img.onload = () => {
        // ,从iframe开始打印
        iframe.contentWindow?.focus();
        iframe.contentWindow?.print();

        // 删除所有img 显示iframe
        iframes.forEach((item, index) => {
          const imgNode = item.parentElement?.querySelector('img');
          imgNode && item.parentElement?.removeChild(imgNode);
          item.style.display = 'block';
        });
        document.body.removeChild(div);
      };
    });
  }

  if (navigator.userAgent.indexOf('MSIE') > 0) {
    document.body.removeChild(iframe);
  }
;