精灵图生成

把一堆小图拼成一张雪碧图,顺手算出每张图的 background-position,配套 CSS 类直接复制进样式表就能用。

一次可以选多张,也可以把文件拖进这个面板或按 Ctrl+V 粘贴。列表顺序就是排版顺序。每张图都保持原始像素、不会被拉伸,所以最后拼出来的位置都是整数像素。

还没有添加图片

使用说明

精灵图的原理是把多张小图放进同一张图,元素通过固定宽高和 background-position 只露出其中一块。它省的不只是请求次数:状态切换(悬停、选中、按下)指向同一张图的不同坐标,图片早就加载完了,不会出现切过去先空白一下再刷出来的闪烁。HTTP/2 的多路复用让省请求的收益小了很多,所以现在更值得拼的是尺寸接近、成组出现的图标,而不是把不相干的照片凑一张。只有两三个图标、或者本来就打算内联 SVG,那就别拼了。

排版规则是「列宽取该列最宽、行高取该行最高」,每张图保持原始像素,不做任何缩放。这不是偷懒,而是为了让坐标是整数:background-position 一旦落在小数上,浏览器取整后图标边缘会切掉或多出半个像素,看到的要么是一像素白边,要么是旁边那张图漏出来的颜色。小图在格子里的位置可以用左上对齐(最紧凑、图最小)或格子居中(大小不一时看起来更整齐),两者都不改变坐标,因为坐标指的是每张图自己那一块的左上角。

生成的 CSS 分成一个基础类和每张图一个类。基础类负责 display、background-image、background-repeat,以及 2 倍图时的 background-size;每个具体类只写宽高和负数的 background-position。负号来自属性本身的语义:它是把图片的某一点对到元素左上角,所以要把整张图往左上推。位置为 0 的那张写 0,不写负零。类名从文件名清洗得到,中文名清洗后是空的,会退化成序号名,想要可读的类名就先把素材改成英文短名。

2 倍图要理解清楚它解决的是什么问题。这里导出 2 倍只是让画布和绘制坐标都放大一倍,源素材如果只有 1 倍像素,放大不会产生新细节,只是避免在 2 倍屏上再被放大模糊一次;真正的清晰 2 倍图需要一套 2 倍尺寸的素材,并且保留生成的 background-size 把显示尺寸压回 1 倍,否则图标会显示成两倍大。另外间距默认 0,加了只会让图变大,唯一的用处是当安全边距——页面缩放时相邻图标可能串出一像素,图标颜色差异很大的话加 2 像素间距最保险。

所有处理都在本机完成,图片不会上传。素材准备上,先用图片裁剪把尺寸裁到接近,再用图片压缩压一下单张体积,拼出来的精灵图会更小,页面加载也更快。

常见问题

HTTP/2 时代还有必要做精灵图吗?

收益比以前小,但没有归零。HTTP/2 的多路复用让「减少请求数」的好处大打折扣,所以为了省请求把几张不相干的照片拼一起已经没意义了。精灵图仍然划算的场景是图标集:一张图加载完之后切换状态不会再发请求,鼠标悬停、选中态不会有那一瞬空白闪烁;图标尺寸固定、数量有限,拼起来也只是一个规整的小文件。反过来,如果只有两三个图标、或者本来就是内联 SVG,直接用 SVG 更省事,改颜色和缩放都不用重新出图。

为什么排版时不把小图缩放到统一尺寸?

因为 background-position 是像素单位的,位置一旦落到小数上,浏览器取整就会让某一边多出或少掉一个像素,表现是图标边缘出现别张图的颜色,或者出现一像素白边。所以这里的做法是列宽取该列最宽的一张、行高取该行最高的一张,每张图保持原始像素,坐标必然是整数。想让它更整齐可以用「居中」对齐,把小的图标放进格子的正中,位置数字依然是整数。

2 倍(Retina)版本该怎么做?

关键在素材本身。工具的 2 倍图是把画布放大两倍后按 2 倍坐标绘制,源图如果只有 1 倍像素,放出来的只是把同一份像素铺得更宽,不会凭空长出新细节,在 2 倍屏上照样是模糊的。正确做法是准备一套 2 倍尺寸的图标(比如 32 像素的图标就用 64 像素的图),导出 2 倍图,并且必须保留生成的 background-size,把显示尺寸压回 1 倍,否则图标会显示成两倍大。

background-position 为什么是负数?

这个属性的语义是「把背景图的哪一个点对齐到元素的左上角」,而不是「图片往哪边挪」。想让第 N 张精灵出现在元素里,就得把整张图片往左上推,让那张精灵的位置正好落在原点,所以写出来是负的坐标:(0, 0) 的那张就直接写 0。另外这套坐标是相对整张精灵图的,如果 CSS 里的图和生成时不是同一张(比如被压缩过、或者换过排列方式),位置就会全部错位,改完排列要重新生成一次 CSS。

间距(gap)要不要加?

大多数时候别加,默认就是 0。间距会让整张图变大、白白多占文件体积,而 CSS 里的位置本来就已经算进了间距,加了只是让图标之间更空。唯一需要注意的是页面缩放:浏览器把一张图放大显示时,采样会略微越界,紧挨着的两个图标边缘有可能串出一像素。如果你的图标之间颜色差异很大、又要支持任意缩放,加 2 像素间距当安全边距是最稳妥的。

图片尺寸差得很多能混着拼吗?

能,但不建议。列宽和行高按该列最宽、该行最高来定,一张大图会把整列的宽度撑大,其余小图那一列就留出大片空白,体积和观感都不划算。尺寸接近的图标拼在一起效果最好,尺寸跨度大的话先用图片裁剪统一到相近的尺寸,再生成精灵图。

类名是怎么起的,中文文件名怎么办?

类名从文件名推导:去掉目录和扩展名、转小写、把非字母数字的整段(包括中文、空格、括号)换成连字符、首尾连字符删掉,数字开头的补 s- 前缀,重名的自动加 -2、-3。纯中文文件名会被清空,这时退化成序号名(比如 item-1)。想要可读的类名,最省事的办法是先把素材文件改成英文短名,比如 icon-home、icon-user。

图片会被上传吗?

不会。排版和绘制都在浏览器里完成,图片读取后只在本机内存里参与计算,关掉页面就没了。单个图太大先用图片压缩压一下,尺寸不统一先用图片裁剪裁成一致再拼,生成出来的图会更小更整齐。

相关:图片压缩图片裁剪