以下内容是来自 Aleks Hudochenkov 的客座文章。 Aleks 在这里出色地展示了 PostCSS 的优势以及它在前端堆栈中扮演的角色。 也就是说:在 CSS 中完成一些有用的工作。 您将看到各种用于处理图像的 PostCSS 插件。 到最后,我相信您会想象 PostCSS 如何在 CSS 中的其他领域发挥作用。
我们都会在 CSS 中使用图像。 这是常规操作。 我们可能没有意识到,但其中可能包含许多手动工作,而这些工作可以变得更加轻松。 我将向您展示各种专门设计用于帮助在 CSS 中处理图像的 PostCSS 插件。
本文中描述的每个插件都支持 PostCSS 可以解析的所有语法 - CSS、SCSS、Less 以及 PostCSS 插件创建的语法。 我不会描述如何使用 PostCSS 本身,因为已经有一篇由 Drew Minns 撰写的 优秀文章。
让我们从涵盖在 CSS 中处理图像时的绝大多数用例的插件开始。
图像助手
postcss-assets 插件几乎是处理图像必不可少的插件。 它具有许多功能。
内联图像
有时将图像转换为样式表中的数据 URL 很有用。 少一次 HTTP 请求!
/* input.css */
div {
background: inline("images/logos/postcss.png");
}
/* output.css */
div {
background: url("data:image/png;base64,iVBORw0KGgoAAAA...ggg==");
}
计算尺寸
有时您需要根据使用的图像的尺寸来调整元素的尺寸或调整背景本身的尺寸。 此插件可以进行这些测量并按需输出它们。
/* input.css */
body {
width: width("images/foobar.png");
height: height("images/foobar.png");
background-size: size("images/foobar.png");
}
/* output.css */
body {
width: 320px;
height: 240px;
background-size: 320px 240px;
}
如果我们处理的是高密度图像,我们可以通过添加第二个参数来修正输出
/* input.css */
body {
width: width("images/foobar.png", 2);
height: height("images/foobar.png", 2);
background-size: size("images/foobar.png", 2);
}
/* output.css */
body {
width: 160px;
height: 120px;
background-size: 160px 120px;
}
URL 解析
此插件可以帮助处理文件路径。 我们不需要知道图像的完整路径。 只有文件名就足够了。
例如,我们有以下文件夹结构
images/
logos/
postcss.png
input.css
我们这样为插件设置选项。 **
表示递归地搜索所有文件夹和文件。
postcss([
require('postcss-assets')({
loadPaths: ['**']
})
])
/* input.css */
div {
background: resolve("postcss.png");
background: resolve("logos/postcss.png");
}
/* output.css */
div {
background: url("/images/logos/postcss.png");
background: url("/images/logos/postcss.png");
}
缓存清除
此插件可以清除图像缓存。
postcss([
require('postcss-assets')({
cachebuster: true
})
])
/* input.css */
div {
background: resolve("images/logos/postcss.png");
}
/* output.css */
div {
background: url("images/logos/postcss.png?153bd5d59c8");
}
内联和修改 SVG
最近我处理的几乎所有图形都是 SVG。 这是一个很棒的格式,可以处理任何像素密度显示。 更好的是,它的语法是文本,这意味着我们可以使用像图形编辑程序这样的繁重工具来编辑它们。
有一个用于内联 SVG 的插件:postcss-inline-svg。 您可能会问为什么我们需要它,因为我们已经介绍过的 postcss-assets 可以做到这一点。 原因是 postcss-inline-svg 有一个杀手级功能:它可以修改 SVG。
假设我们有一个星形图标,我们在网站上的十个不同位置使用它,并使用不同的颜色。 我们可以用多种方法做到这一点。 我们可以使用一个带 <symbol></symbol>
和 <use>
的 内联 SVG 系统,以及其他所有内容。 或者,我们可以使用 CSS 中的 background
属性!
在 CSS 中使用图像有两种方法。 1) 带有文件路径的 url(/path/to/image.jpg)
值或 2) 带有数据 URL 的 url(data:...)
值。 后者有时被称为“内联”图像,它实现了图像精灵的主要优势之一:合并 HTTP 请求。 使用 postcss-inline-svg,我们可以做到这一点(将我们的 CSS 文件作为我们的精灵),并仍然独立调整颜色
/* input.css */
.star--red {
background-image: svg-load("img/star.svg", fill=#f00);
}
.star--green {
background-image: svg-load("img/star.svg", fill=#0f0, stroke=#abc);
}
/* output.css */
.star--red {
background: url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23f00'%3E...%3C/svg%3E");
}
.star--green {
background: url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%230f0' stroke='%23abc'%3E...%3C/svg%3E");
}
您可能会问,输出的 CSS 文件会很大吗? 既是也不是。 输出的 CSS 会更大,因为代码重复,但这并不重要,因为有 Gzip! 为了说明这一点,我做了一个测试。 我创建了一个带有 100 个不同选择器的 CSS 文件,并在每个规则集中添加了一个内联图标,并为 fill
使用随机颜色。 如下所示
.wibcsidpuaeqgbxvcjqq {
background: svg-load("images/star.svg", fill: #8c0);
}
然后我创建了此文件的副本,并删除了所有这些内联背景。 以下是文件大小结果
原始大小 | 压缩后 | |
---|---|---|
包含 100 个图像 | 48500 字节 | 2560 字节 |
包含 1 个图像 | 3158 字节 | 1817 字节 |
差异:2560 – 1817 = 743 字节
差别不大!
这种方法的唯一缺点:无法动画化图像的变化。 例如,如果颜色应该在悬停时发生变化并带有过渡,则没有便捷的方法来做到这一点,因为 transition
不会应用于 background-image
。
这些插件相互补充
现实世界的例子:我们需要一个带有图标的按钮。 按钮内部的图像需要一个特定的图像大小,并且还需要在悬停时改变颜色。 只有一个源 SVG 文件。
标记
<button type="button" class="delete">Delete</button>
没有任何帮助,我们可能会做这样的事情
.delete {
box-sizing: content-box;
padding: 15px;
/* Values based on this particular image */
width: 26px;
height: 32px;
border: 1px solid #ef5350;
border-radius: 3px;
background: #fff url("images/trash.svg") 50% 50% no-repeat;
text-indent: -9999px;
}
.delete:hover {
border-color: #c62828;
/* Manually duplicate file and change things */
background-image: url("images/trash-hover.svg");
}
使用 postcss-assets 自动化操作,我们可以这样做
postcss([
require('postcss-inline-svg')(),
require('postcss-assets')()
]);
.delete {
box-sizing: content-box;
padding: 15px;
width: width("images/trash.svg");
height: height("images/trash.svg");
border: 1px solid #ef5350;
border-radius: 3px;
background: #fff svg-load("images/trash.svg", fill=#ef5350) 50% 50% no-repeat;
text-indent: -9999px;
}
.delete:hover {
border-color: #c62828;
background-image: svg-load("images/trash.svg", fill=#c62828);
}
输出
.delete {
box-sizing: content-box;
padding: 15px;
width: 26px;
height: 32px;
border: 1px solid #ef5350;
border-radius: 3px;
background: #fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='32' viewBox='12 8 26 32' fill='%23ef5350'%3E%3Cpath d='M20 18h2v16h-2z'/%3E%3Cpath d='M24 18h2v16h-2z'/%3E%3Cpath d='M28 18h2v16h-2z'/%3E%3Cpath d='M12 12h26v2H12z'/%3E%3Cpath d='M30 12h-2v-1c0-.6-.4-1-1-1h-4c-.6 0-1 .4-1 1v1h-2v-1c0-1.7 1.3-3 3-3h4c1.7 0 3 1.3 3 3v1z'/%3E%3Cpath d='M31 40H19c-1.6 0-3-1.3-3.2-2.9l-1.8-24 2-.2 1.8 24c0 .6.6 1.1 1.2 1.1h12c.6 0 1.1-.5 1.2-1.1l1.8-24 2 .2-1.8 24C34 38.7 32.6 40 31 40z'/%3E%3C/svg%3E") 50% 50% no-repeat;
text-indent: -9999px;
}
.delete:hover {
border-color: #c62828;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='32' viewBox='12 8 26 32' fill='%23c62828'%3E%3Cpath d='M20 18h2v16h-2z'/%3E%3Cpath d='M24 18h2v16h-2z'/%3E%3Cpath d='M28 18h2v16h-2z'/%3E%3Cpath d='M12 12h26v2H12z'/%3E%3Cpath d='M30 12h-2v-1c0-.6-.4-1-1-1h-4c-.6 0-1 .4-1 1v1h-2v-1c0-1.7 1.3-3 3-3h4c1.7 0 3 1.3 3 3v1z'/%3E%3Cpath d='M31 40H19c-1.6 0-3-1.3-3.2-2.9l-1.8-24 2-.2 1.8 24c0 .6.6 1.1 1.2 1.1h12c.6 0 1.1-.5 1.2-1.1l1.8-24 2 .2-1.8 24C34 38.7 32.6 40 31 40z'/%3E%3C/svg%3E");
}
如果图像发生变化,您无需执行任何操作! postcss-assets 会更新尺寸。 需要更改颜色吗? 它们就在 CSS 中。 如果您使用提供这些变量的另一个插件或预处理器,您甚至可以使用变量。
输出示例
参见 CodePen 上 Aleks Hudochenkov (@hudochenkov) 的笔 NNvGJP。
精灵
仍然有一些原因让您可能希望使用所有图像组合在一起形成一张更大图像的图像精灵。 其中一个原因是,众所周知,手机解码内联图像的速度略慢于普通图像。
有很多工具可以创建图像精灵。 例如:grunt-spritesmith。 这些工具功能强大,但设置起来并不容易也不方便。 例如,在 grunt-spritesmith 中,您需要了解其模板引擎的工作原理。
postcss-sprites 插件(基于 spritesmith)更加方便。 它的工作原理如下
/* input.css */
.comment {
background-image: url("images/sprite/ico-comment.png");
}
.bubble {
background-image: url("images/sprite/ico-bubble.png");
}
/* output.css */
.comment {
background-image: url("images/sprite.png");
background-position: 0 0;
}
.bubble {
background-image: url("images/sprite.png");
background-position: 0 -50px;
}
它会在 CSS 中找到每个图像(可以进行过滤),创建精灵,并输出正确的 background-position 以获取这些图像。
处理高密度屏幕的精灵
尽管 postcss-sprites 支持视网膜图像,但它并没有真正为您提供可用于生产环境的代码。 例如,它不会为您提供针对高密度屏幕的媒体查询以实际使用这些图像。 这个问题可以通过另一个 PostCSS 插件来解决。 这就是 PostCSS 生态系统的优势 - 存在许多仅执行一项工作的插件,您可以将它们组合在一起以解决更复杂的问题。
有一个名为 postcss-at2x 的插件可以添加针对高密度屏幕的媒体查询。让我们将这些插件组合起来,为正常和高密度屏幕生成精灵图。
postcss([
require('postcss-at2x')(),
require('postcss-sprites').default({
retina: true
})
]);
/* input.css */
.circle {
background-image: url("images/circle.png") at-2x;
}
.square {
background-image: url("images/square.png") at-2x;
}
/* output.css */
.circle {
background-image: url("sprite.png");
background-position: 0px 0px;
}
.square {
background-image: url("sprite.png");
background-position: -25px 0px;
}
@media (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx) {
.circle {
background-image: url("[email protected]");
background-position: 0px 0px;
background-size: 50px 25px;
}
}
@media (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx) {
.square {
background-image: url("[email protected]");
background-position: -25px 0px;
background-size: 50px 25px;
}
}
做到了。
动态创建图片
有时我们需要非常简单的图片(比如几何形状),但仍然会打开图形编辑器,创建图片,导出它,把它放在正确的位置,优化它,并在 CSS 中使用它。如果我们可以在 CSS 中直接创建简单的图片呢?我敢肯定你能猜到:我们能!
postcss-write-svg 允许你在 CSS 中直接创建简单的 SVG 图片。只需描述 SVG 元素,它就会被内联为 background-image
。
/* input.css */
@svg square {
@rect {
fill: var(--color, black);
width: 100%;
height: 100%;
}
@polygon {
fill: green;
points: 50,100 0,0 0,100;
}
}
#example {
background: white svg(square param(--color #00b1ff));
}
/* output.css */
#example {
background: white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%2300b1ff' width='100%25' height='100%25'/%3E%3Cpolygon fill='green' points='50%2C100 0%2C0 0%2C100'/%3E%3C/svg%3E");
}
还有其他插件可以使用 CSS 属性来创建圆形和三角形。三角形。你可以在 CSS 中 自己创建三角形,但它并不简单,当你想创建不同类型的三角形时会变得更加困难。 postcss-triangle 允许你轻松地创建等腰三角形、直角等腰三角形和等边三角形。
/* input.css */
.isosceles-triangle {
triangle: pointing-right;
width: 150px;
height: 115px;
background-color: red;
}
.right-isosceles-triangle {
triangle: right-iso pointing-down;
width: 250px;
background-color: red;
}
.equilateral-triangle {
triangle: equilateral pointing-up;
height: 100px;
background-color: red;
}
/* output.css */
.isosceles-triangle {
width: 0;
height: 0;
border-style: solid;
border-color: transparent;
border-width: 57.5px 0 57.5px 150px;
border-left-color: red;
}
.right-isosceles-triangle {
width: 0;
height: 0;
border-style: solid;
border-color: transparent;
border-width: 125px 125px 0;
border-top-color: red;
}
.equilateral-triangle {
width: 0;
height: 0;
border-style: solid;
border-color: transparent;
border-width: 0 57.73503px 100px;
border-bottom-color: red;
}
圆形比较容易,但 postcss-circle 可能会为你节省几行代码并提高可读性。
/* input.css */
.circle {
circle: 100px red;
}
/* output.css */
.circle {
border-radius: 50%;
width: 100px;
height: 100px;
background-color: red;
}
缓存清除
假设你需要更新样式表中链接的图片。如果我们使用很长的过期时间头,用户的浏览器会将该图片缓存在缓存中,我们可能会遇到问题。解决方法是强制用户的浏览器下载新版本(缓存清除)。有两种方法可以做到:更改文件名或更改 URL。更改文件名要求太多,但更改 URL 很容易,这要归功于 URL 参数。
以下是使用 postcss-urlrev 更改 URL 的方式
/* input.css */
.foo {
background: url("images/test.png") 0 0 no-repeat;
}
/* output.css */
.foo {
background: url("images/test.png?v=e19ac7dee6") 0 0 no-repeat;
}
此任务也可以通过 postcss-cachebuster 和 postcss-assets 完成。
实用程序
PostCSS 插件可以帮助优化样式表。例如,postcss-svgo 可以使用 SVGO(最佳的 SVG 优化工具)来优化内联的 SVG。
如果你仍然需要支持不支持 SVG 的浏览器,但你想使用 SVG,postcss-svg-fallback 可以帮助你。此插件会为你的 CSS 中的 SVG 生成 PNG 备用图片(内联的和通过 url()
链接的),并在 CSS 中添加针对旧浏览器的额外规则。
内联图片可能会使输出的 CSS 膨胀,但有一个解决方案:postcss-data-packer 可以将嵌入的 data URL 提取到一个单独的文件中。现在你可以异步加载此文件以减少页面加载时间。
结论
在 PostCSS 出现之前,我们做了很多乏味的体力劳动:复制粘贴,重复操作,或者手动计算。现在我们可以使用一些 PostCSS 插件,让我们的计算机帮我们做这些事情。它加快了我们的工作速度,让我们更快乐。
看到这样的具体例子总是很有用的。很棒的文章。
很棒的文章!我一定要尝试一下,因为如果说我在 CSS 中最讨厌的一件事,那就是处理图片。
嘿,Chris!很棒的例子。谢谢。
关于动画方法。你始终可以使用内联图片的两层之间的不透明度过渡。它的性能很好,而且正如你所说,使用 gzip 不会增加太多大小。
抱歉,是我的错误。感谢 Aleks。:)
Postcss 非常有用,我始终在我所有的项目中使用它。好文章,谢谢分享。
很棒的文章。不过,我有一个小小的异议。包含 100 张相同图片的 CSS 可能会在网络上拥有近似相同的大小,但它在客户端会扩展到其完整大小并被解析为该大小。由于客户端无法知道这些图片实际上大部分是相同的,因此它无法优化解析或内存使用。你最终可能会遇到明显的负面影响。
我认为这并不能否定你的方法,只是我们必须注意我们在做什么(无论如何我们都应该这样做)。