调用浏览器局部打印,空白、只有一页问题、火狐兼容
项目中需要局部打印页面的图表,图表类型多而杂,因此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);
}