[js] 浏览器原生打印总结

630 阅读3分钟

1.1 前言

本文包含浏览器原生打印的常用技术点:打印样式纸张修改、分页相关、唤起别的页面打印、局部打印等。

如果文章帮到你了,可以点个赞。

1.2 修改打印样式

使用@media print或<link href="" media="print" rel="stylesheet" />

注意:不是所有样式都能应用在打印中,具体在打印预览窗口查看是否生效。

/* 其内部样式只在打印时生效 */
@media print {
    /* 隐藏非打印元素 */
    ... {
        display: none !important;
    }

    /* 隐藏不必要的边距 */
    ... {
        margin: 0 !important;
        padding: 0 !important;
    }

    /* 重置所有样式 */
    ... {
        all: unset !important;
    }
}

1.3 修改打印纸张和方向

使用@page的size属性,可以使用不同的打印纸张、切换打印方向,以及自定义纸张大小。

注意:实际打印纸张以打印窗口中选择的为准。

@media print {
    @page {
        size: A4 landscape; // 使用A4纸张,横向打印
        // size: 50mm 150mm; // 自定义纸张大小
    }
}

1.4 修改页边距(去除浏览器自带的页眉页脚)

使用@page的margin属性,可以修改页边距,去除浏览器自带的页眉页脚。

注意:浏览器的自带的页眉页脚是不能修改的,要想自定义就得先去除它,再自己实现。

@media print {
    @page {
        margin: 15mm; // A4纸页边距
        // margin: 0; // 去除浏览器自带的页眉页脚
    }
}

自己实现页眉页脚的思路:

将页面分成三个部分:重复的页眉、重复的页脚、内容,然后根据数据动态生成页面,生成时要检查内容高度是否超过阈值(即内容高度 = 页面高度 – 页眉高度 – 页脚高度),如果超过了,就裁剪数据再生成新页面。

1.5 强制分页

page-break-before: always; // 强制在元素前分页

page-break-after: always;  // 强制在元素后分页

1.6 自动分页

要打印的内容区域,有滚动条,怎么打印完整?

怎么在唤出打印预览窗口时,浏览器自动分页,而不是只打印可视区?

关键在于,只让body滚动,这就够了。

@media print {
    .common-layout,
    .main-body,
    .gmp-container {
        /* 打印区域及其所有父级元素只要有设置过overflow,全部重置 */
        overflow: unset !important;
    }
    body {
        /* 只有body设为auto */
        overflow: auto !important;
    }
}

1.7 表格分页时表头重复

当上面表格长过一页时,浏览器会在下一页的顶部自动复制<thead>

<table>
    <thead> // 关键
        <tr>
            <th>列1</th>
            <th>列2</th>
        </tr>
    </thead>
    <tbody>
        <tr><td>0</td><td>0</td></tr>
        <tr><td>1</td><td>1</td></tr>
        <tr><td>2</td><td>4</td></tr>
        ...
    </tbody>
</table>

1.8 在当前页面打印别的页面

function handleGmpPrint() {
    const hideMessage = message.loading('正在准备打印...', 0)
    const hideFrame = document.createElement('iframe')
    hideFrame.addEventListener('load', () => {
        hideMessage()
        hideFrame.contentWindow?.addEventListener('afterprint', () => {
            document.body.removeChild(hideFrame)
        })
        hideFrame.contentWindow?.print()
    })
    hideFrame.style.display = 'none'
    hideFrame.src = '/gmp/print' // 指定要打印的页面路由
    document.body.appendChild(hideFrame)
}

打印数据可以【通过url带参在打印页请求】或【直接通过状态管理库、localStorage传递】,

期间可能存在异步问题,利用定时器监听某个指标,这个指标只在打印数据加载完成时为true.

1.9 局部打印

局部打印:只打印部分内容,而不是整个页面打印

1. 把内容放入新页面中打印或修改原页面body(不推荐,用户体验不好)

2. 把内容放入隐藏的iframe中打印(不推荐,比较麻烦,样式不好处理,表单输入会丢失)

3. 隐藏所有不需要打印的元素(推荐)

方法三:通过动态控制no-print类,再配合nextTick实现局部打印

@media print {
    .no-print {
        display: none !important;
    }
}

<div class="print-page" :class="{ 'no-print': currentItemIndex !== 1 }">
    
/** 当前要局部打印的项索引 */
const currentItemIndex = ref(0)
/** 局部打印 */
function handleItemPrint(index: number) {
    currentItemIndex.value = index
    nextTick(() => {
        window.print()
    })
}

最后

之前使用vue-plugin-hiprint也实现过打印功能,包含自动打印、选择打印机、自定义打印模板设计等,有兴趣的朋友可以看看,文章地址