这是一篇翻译文章,原文请看How to Create Printer-friendly Pages with CSS
这篇文章更新于2020年,用来展示css样式打印时最近的最佳实践。
本文中我们将一起学习用css创建适合打印的web页面。
“谁会打印网页呢?”我听到了你的叫嚷声。相对来说很少有网页会展示在纸上,但是请想一下下面的这些场景:
- 打印旅行的车票或者音乐会门票
- 公示路标或者日程表
- 保存在线阅读的内容
- 在比较闭塞的地区获取信息
- 在比较脏乱危险的地方使用数据,比如厨房或者工厂
- 书写注释草稿
- 为了记账打印网页收据
- 为使用屏幕有困难的人群提供文档
- 为拒绝使用网络模式提交作业的学校打印文档
不幸的是,打印页面可能有一些比较痛苦的回忆,比如:
- 文字太小、太大、太虚
- 行间距太小、太宽、甚至溢出页面
- 有些内容被裁减甚至消失不见
- 墨水被浪费在不需要的背景颜色和图片上去了
- icon,菜单,广告等从未点击的内容被打印了出来
很多开发者都积极维护web的易用性,然而很少有人想到打印页面的易用性。
转换响应式页面到各种大小和方向的纸上展示出来是一个挑战。其实css几年前就可以控制打印样式了,实现基本的打印样式甚至不会超过1小时。接下来的内容描述了创建打印页面友好的支持和实用的选项。
添加打印样式
任选下面两种方式中的一种添加样式就可以:
- 附加在屏幕样式后面。以屏幕样式为基础,用打印样式来覆盖普通样式。
- 作为独立样式放置。屏幕样式和打印样式分离,在浏览器中单独加载。
具体两种样式怎么选择,要根据网站或者app的情况来定。就我个人而言,我通常选择方式1,以屏幕样式作为打印样式的基础。但有时候我也会分离应用的样式来彻底隔离输出的内容,比如一个会议预定系统,在屏幕上用时间来展现表格,但是在纸上是按照时间顺序打印出来。
打印样式可以在html的head
标签中添加在标准样式后面,如下:
1 | <link rel="stylesheet" href="main.css" /> |
除了屏幕样式,在打印时print.css
也将生效,会覆盖部分屏幕样式。
区分屏幕和打印样式,main.css
将只在屏幕上生效,如下:
1 | <link rel="stylesheet" media="screen" href="main.css" /> |
除了上面两种方式外,打印样式可以通过媒体查询放在已有的css文件中,如下:
1 | /* main.css */ |
可以在任意一行插入@media print
规则,这对于将相关联的代码组织在一起是非常有用的。如果有需要,在这种情况下照样可以将屏幕样式和打印样式完全分开,如下:
1 | /*main.css*/ |
测试打印样式
在测试时每次都将打印内容打印在纸上既浪费又不环保,下面这些方法可以将打印内容复现在屏幕上。
预览打印
在浏览器上预览打印是一种非常可靠的方式,除了可以显示打印的样式,它还会将默认纸张上的分页情况显示出来。具体操作方法就是在浏览器右键,选择打印就可以看到。
在打印预览界面,你还可以将要打印的页面导出为pdf来保存或者预览。
Developer Tools
通过打开开发者工具,也可以预览打印样式,只是这种方式方式只能查看打印样式,无法看到具体纸张上的分页情况。
在chrome浏览器上,首先打开检查,然后选择更多工具,然后选择渲染,然后在模拟css媒体类型处选择打印,这时页面显示的就是打印样式。
在Firefox上,打开检查工具,然后点击切换媒体类型按钮即可。
改变媒体属性
用<link>
加载打印样式时,可以暂时将标签上的媒体属性media
修改为打印screen
同样的,这种方法也不会显示换页,测试完后记得将媒体类型修改为print
。
移除打印时不需要的内容
通过diaplay:none
属性将不需要打印的内容提前移除出去。通常菜单,网页头部和底部都是不需要打印的。因此可以像下面一样移除掉
1 | /*print.css*/ |
有时候,遇到行内样式,使用display: none !important;
是需要的。不推荐使用!important
,但是在打印时为了覆盖基础样式,用一下也可以。
使用传统的线性布局,而不是flex或者grid
这么说让我有带你难过,但是flex和grid在一些浏览器上的支持确实不太好,如果遇到问题,请尝试使用display:block;
或者盒子布局来调整,比如将宽度设置为100%
来实现内容充满纸张。
打印实战
现在可以开始实践打印友好的样式来,下面是一些最佳实践推荐:
- 确保是在白纸上打印黑色字体
- 确保使用比较容易阅读的字体
- 将字体调整到12pt或者更大
- 在需要的地方配置padding和margin,标注单位cm和mm在配置时可能更加实用。
使用CSS Columns
标准的A4纸可能会导致文本行长过长且难以阅读,可以使用css Columns布局,比如:
1 | /* print.css */ |
在上面的例子中,在水平空间中每一列将会是37em,如果纸张更宽,不许要增加媒体查询,新的列将会出来。
使用border而不是background
在打印时,可能有些部分有比较重的背景色,这个时候将背景色打印出来效果不好且浪费墨水,可以考虑用border来代替。
移除或者转换图片
用户很少愿意打印没有用的装饰和背景图片,可以考虑将没有的装饰和背景颜色隐藏掉,除非有print
的class属性。如下:1
2
3
4
5
6
7
8
9
10
11
12
13/* print.css */
* {
background-image: none !important;
}
img, svg {
display: none !important;
}
img.print, svg.print {
display: block;
max-width: 100%;
}
理想情况下,打印的图片应该在浅色背景上使用深色,可以在 CSS 中更改 HTML 嵌入的 SVG 颜色,但在某些情况下打印出来的图片会变暗。
可以用CSS filter 来解决这个问题,比如:
1 | /* print.css */ |
处理后的结果如下:
添加附加内容
打印时经常会需要一些在屏幕上不需要的东西,这些额外东西可以利用伪类after
,before
的content
属性来实现,比如给一段文字后面添加一个链接:
1 | /* print.css */ |
对于更多的内容,可以考虑在html标签内的class
属性上增加print
值,print
在屏幕上隐藏,在打印时显示。如下
1 | /* 在屏幕上隐藏 */ |
分页
可以使用css的break-before
,break-after
属性在一些标签前后控制分页。这些属性当前浏览器支持的非常好,但是在一些老旧浏览器中,还得使用page-break-before
,page-break-after
属性。
break-before
,break-after
有以下一些值:
auto
:默认值,页面按照出现顺序自动分页avoid
: 避免在页面上的某个区域,某个列内分页avoid-page
: 避免分页page
: 强制分页always
:同page
,强制分页left
:强制分页,下一个页面时左页(双面打印时正面,单页打印时单数页面)right
: 强制分页,下一个页面时右页(双面打印时反面,单页打印时双页面)
比如,强制页面在h1
标签前分页
1 | /* print.css */ |
break-inside
(老式浏览器中的page-break-inside
)用来决定十分在某个内容区域内分页,属性值有:
auto
:默认值,页面按照出现顺序自动分页avoid
: 避免内部分页,页面满了新起一页打印avoid-page
:避免内部分页,页面满了新起一页打印
break-inside
可能比break-before
,break-after
更有用一些,它可以保证尽可能少的使用纸张,又不至于让图标等因为分页断开,比如不允许图片,图表分页
1 | /* print.css */ |
windows
属性用来配置页面顶部出现的最少行数,想象一个有五行的文本,想要在第四行分页,但是设置了widows: 3;
,这时候就会在第二行处分页,将三行换到下一页。
orphans
属性用来配置页面底部出现的最少行数。
box-decoration-break
属性用来控制夸页面时页面border的显示方式,有以下属性值:
slice
:默认值,顶部边框显示在第一页,底部边框显示在第二页clone
:复制边框,分页后边框在两个页面都显示
这个属性我测试时没有显示,应该是废弃了。
最后,@page属性虽然在一些浏览器中有限制,但是提供了一个配置页面的方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/* print.css */
/* 给整个页面增加margin */
@page {
margin: 2cm;
}
/* 只给第一个页面增加 */
@page :first {
margin-top: 6cm;
}
/* 单页配置 */
@page :left {
margin-right: 4cm;
}
/* 双页配置 */
@page :right {
margin-left: 4cm;
}
本篇结束