在前端做项目的过程中,需要处理打印的情况比较少,遇到需要打印页面部分的情况就更少了。但是这部分功能必须要实现,一般是客户比较看重的。例如:一些报表用户可以在页面查询,查询后可以打印页面的一部分,这种情况处理起来比较棘手,因为浏览器提供的api。window.print();只能打印整个网页,不支持打印页面的一部分,这篇文章主要处理这种情况,如果能对读者有用,不胜荣幸。
这里先说一下解决思路:将需要打印的dom做成canvas图片,放到子iframe中打印。 说起来容易,做起来难,具体过程如下。
- 将需要打印的页面用一个元素包裹起来,命名一个id,例如id=“need_print”,
- 使用html2canvas组件将该元素生成一张图片。在生成的过程中注意宽度,需要对不同的表格单独调整,因为打印需要将整个纸张占满,不能只占用纸张的一部分,也不能打印显示不完全。这是一个经验值,
- 将图片缩放一下,宽度处理成1450(注意我打印的是横向纸张,如果是竖的就不是该值),高度随宽度同比缩放。
- 由于栅格图片缩放后会产生模糊,需要对图像做锐化处理,使用拉普拉斯算子(也可以使用其他算子,我直接对图像做了2值化处理)。
- 新建一个iframe,将处理好的图像放入到iframe中,调用iframe的打印。
- 打印完成隐藏iframe,将元素宽度调整为页面正常显示宽度 具体代码如下:
`
handlePrint(){
this.printLoading = true;
let printElement = document.querySelector("#need_print");
let printWidth = window.innerWidth,printHeight = window.innerHeight,elementWidth=0,elementHeight=0;
elementHeight = printElement.clientHeight;
if(this.currentPath == 'queryYearAllSalesRank'){
elementWidth = 2000;
}else if(this.currentPath == 'queryQuarterSales'){
elementWidth = 1600;
}else if(this.currentPath == 'queryQuarterTaskSales'){
elementWidth = 1600;
}else if(this.currentPath == 'queryQuarterSales1'){
elementWidth = 1600;
}else if(this.currentPath == 'queryQxQuarterSales'){
elementWidth = 1400;
}
printElement.style.width = elementWidth + 'px';
// console.log(elementWidth,elementHeight);
html2canvas(printElement,{
width:elementWidth,
height:elementHeight,
}).then((canvas) => {
let printCanvas = Canvas2Image().scaleCanvas(canvas,1450,1450*elementHeight/elementWidth)
document.hideframe.document.body.appendChild(printCanvas);
document.hideframe.print();
document.hideframe.document.body.removeChild(printCanvas);
this.printLoading = false;
// document.querySelector("#show_pic").appendChild(canvas);
// document.querySelector("#show_pic").appendChild(printCanvas);
printElement.style.width = '100%';
});
},
`
Canvas2Image是在网上搜索的canvas缩放工具(在这里感谢作者),在没有加入图像锐化以前,打印出来是很模糊的,大家感受一下:
所以在canvas缩放后要做图像锐化处理,使用拉普拉斯算法处理如下:
function sharpening(canvas){
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
//这里有个疑问,为什么imageData的数据宽高不是canvas的宽高呢?
let imageWidth = imageData.width;
let imageHeight = imageData.height;
const kernel = [
[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1]
];
let resultData = new Uint8ClampedArray(imageWidth*imageHeight*4);
console.log(imageWidth,imageHeight,resultData.length);
//边上的图像元素不做处理
for(let i=0;i<imageWidth;i++){
for(let j=0;j<imageHeight;j++){
let index = (i + j*imageWidth) * 4;
if(i==0 || j==0 || i == imageWidth || j== imageHeight){
resultData[index] = data[index];
resultData[index+1] = data[index+1];
resultData[index+2] = data[index+2];
resultData[index+3] = data[index+3];
continue;
}
let leftIndex = (i + j*imageWidth) * 4 - 4;
let rightIndex = (i + j*imageWidth) * 4 + 4;
let upIndx = (i + (j-1)*imageWidth) * 4;
let upLeftIndx = (i + (j-1)*imageWidth) * 4 - 4;
let upRightIndx = (i + (j-1)*imageWidth) * 4 + 4;
let bottomIndex = (i + (j+1)*imageWidth) * 4;
let bottomLeftIndex = (i + (j+1)*imageWidth) * 4 - 4;
let bottomRightIndex = (i + (j+1)*imageWidth) * 4 + 4;
resultData[index] = data[index]*9 - data[leftIndex] - data[rightIndex] - data[upIndx] - data[upLeftIndx]-data[upRightIndx]-data[bottomIndex]-data[bottomLeftIndex]-data[bottomRightIndex];
resultData[index+1] = data[index+1]*9 - data[leftIndex+1] - data[rightIndex+1] - data[upIndx+1] - data[upLeftIndx+1]-data[upRightIndx+1]-data[bottomIndex+1]-data[bottomLeftIndex+1]-data[bottomRightIndex+1];
resultData[index+2] = data[index+2]*9 - data[leftIndex+2] - data[rightIndex+2] - data[upIndx+2] - data[upLeftIndx+2]-data[upRightIndx+2]-data[bottomIndex+2]-data[bottomLeftIndex+2]-data[bottomRightIndex+2];
resultData[index+3] = data[index+3]*9 - data[leftIndex+3] - data[rightIndex+3] - data[upIndx+3] - data[upLeftIndx+3]-data[upRightIndx+3]-data[bottomIndex+3]-data[bottomLeftIndex+3]-data[bottomRightIndex+3];
}
}
console.log(resultData);
let shepData = new ImageData(resultData, imageWidth, imageHeight);
ctx.putImageData(shepData, 0, 0);
return canvas;
console.log(imageData,data.length,canvas.width,canvas.width);
// 计算锐化强度(这里使用3x3的卷积核)
}
当然也可以做其他,图像锐化,或者线模糊再锐化,反正有图像数据了,这里有一个好玩的技巧:这样的事情交给生成式人工智能去做,例如使用文心一言,生成的代码如下:
虽然不能直接使用,但还是有可以copy的地方。
页面局部打印,没有copy过来就直接可以使用的,像是打印dom宽度调整,和图像锐化处理都要反复测试。希望对有这方面需求的同学有帮助。
行文不易,如果感觉对你有帮助可以打赏作者,谢谢。