打印部分页面问题的处理

284 阅读3分钟

  在前端做项目的过程中,需要处理打印的情况比较少,遇到需要打印页面部分的情况就更少了。但是这部分功能必须要实现,一般是客户比较看重的。例如:一些报表用户可以在页面查询,查询后可以打印页面的一部分,这种情况处理起来比较棘手,因为浏览器提供的api。window.print();只能打印整个网页,不支持打印页面的一部分,这篇文章主要处理这种情况,如果能对读者有用,不胜荣幸。
 这里先说一下解决思路:将需要打印的dom做成canvas图片,放到子iframe中打印。 说起来容易,做起来难,具体过程如下。

  1. 将需要打印的页面用一个元素包裹起来,命名一个id,例如id=“need_print”,
  2. 使用html2canvas组件将该元素生成一张图片。在生成的过程中注意宽度,需要对不同的表格单独调整,因为打印需要将整个纸张占满,不能只占用纸张的一部分,也不能打印显示不完全。这是一个经验值,
  3. 将图片缩放一下,宽度处理成1450(注意我打印的是横向纸张,如果是竖的就不是该值),高度随宽度同比缩放。
  4. 由于栅格图片缩放后会产生模糊,需要对图像做锐化处理,使用拉普拉斯算子(也可以使用其他算子,我直接对图像做了2值化处理)。
  5. 新建一个iframe,将处理好的图像放入到iframe中,调用iframe的打印。
  6. 打印完成隐藏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缩放工具(在这里感谢作者),在没有加入图像锐化以前,打印出来是很模糊的,大家感受一下:

1686926746392.jpg
所以在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的卷积核)  
}

当然也可以做其他,图像锐化,或者线模糊再锐化,反正有图像数据了,这里有一个好玩的技巧:这样的事情交给生成式人工智能去做,例如使用文心一言,生成的代码如下:

1686926920740.png
虽然不能直接使用,但还是有可以copy的地方。 页面局部打印,没有copy过来就直接可以使用的,像是打印dom宽度调整,和图像锐化处理都要反复测试。希望对有这方面需求的同学有帮助。 行文不易,如果感觉对你有帮助可以打赏作者,谢谢。

1686927391862.png