十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CSS精灵图全解析:原理、定位计算与Retina高清屏适配实战

CSS精灵图全解析:原理、定位计算与Retina高清屏适配实战 做前端这些年“CSS精灵图”几乎是每一轮面试、每一个项目评审里都会冒出来的话题。但有意思的是问的人和答的人经常不在一个频道上有人还停留在“把所有小图标拼成一张大图”的粗浅理解有人则直接抛出一句“HTTP/2时代早就不需要精灵图了”。这个知识点真没那么简单也不是过气技术。我自己的项目里直到现在依然会用到 sprite关键看你怎么用、用在什么场景。这篇文章我就把精灵图的原理、制作流程、background-position 的计算逻辑、Retina 屏适配以及我在实际项目中踩过的几个典型坑一次性讲清楚。不管你是刚入门 HTML/CSS/JS 的小白还是已经写过几年页面、想系统梳理一遍的开发者这篇都适合你。看完之后你至少能做到拿到一套小图标能独立决定要不要用精灵图要用的话能自己用 PhotoShop 做出一张规范的雪碧图也能用自动化工具替代手工流程碰上高清屏适配和定位错乱这类问题能快速定位原因。1. 先别急着下结论精灵图在HTTP/2时代还值不值得用每次提到精灵图总绕不开一个灵魂拷问HTTP/2 都普及了多路复用一开一个连接上可以并行发送多个请求那合并图片减少 HTTP 请求还有什么意义这句话看起来有道理但它把问题简化得太厉害了。1.1 一张图看透CSS精灵图到底在解决什么问题CSS精灵图CSS Sprites也叫雪碧图的核心思想是以空间换时间把几十张小图标拼到一张大图上页面加载时只需要发出一个图片请求然后通过 background-position 把需要的局部区域“抠”出来展示。在没有 HTTP/2 的年代页面每引用一张图片就是一次 HTTP 请求浏览器对同一域名下的并发连接数还有硬性限制HTTP/1.1 一般是6个左右。一个导航栏放 10 个图标可能就要排队发 10 个请求。如果遇上弱网每个请求都有 DNS 查询、TCP 握手、TLS 握手的开销页面加载速度会被明显拖慢。而当这些图标合并成一张精灵图后首屏只需要一个请求后面的图标展示全部由 CSS 本地完成这个性能收益是实打实的。还有一个经常被忽略的好处精灵图天然规避了图标切换时的“闪白”。比如一个按钮有 normal 和 hover 两种状态如果分别用两张图片鼠标移入的瞬间浏览器才去加载 hover 图用户会看到明显的白屏闪烁。换作精灵图因为两种状态都在这同一张图里切换 background-position 只是瞬间的移动没有网络请求自然没有闪烁。1.2 “HTTP/2下精灵图已死”这句话对在哪里、错在哪里HTTP/2 的多路复用、头部压缩确实让“减少请求数”的重要性大幅下降这是事实。但现实中的项目环境远比这句话复杂。首先HTTP/2 是通过 HTTPS 协商的如果你的站点还跑在 HTTP/1.1 上那“多路复用”跟你毫无关系。国内不少企业内网系统、旧服务器、部分 CDN 边缘节点仍然以 HTTP/1.1 为主这种情况下精灵图依然是最简单有效的优化手段。其次移动端弱网环境里一次大图请求的稳定性通常优于几十个小图并发请求。哪怕 HTTP/2 允许并发每一个请求在弱网下的失败概率都独立存在几十个请求哪怕只有一两个失败也会造成小图标显示不全、布局错乱。合并成一张图最多就是整张图加载慢一点或者整张图失败这时候可以再做兜底。最后精灵图能减少的不只是“请求数量”还有 DNS 查询和 TLS 握手。HTTP/2 虽然复用了连接但首次连接建立的开销还是存在的。你要加载 20 张来自同一域名的小图HTTP/2 也只是在一条连接上多路复用并不代表这些开销凭空消失。所以我的结论是精灵图并没有“死”只是它的适用场景变了。它不再是什么项目都必须用的银弹而是一种在特定条件下非常有效的性能优化手段。1.3 我的判断什么场景该用什么场景不该用我一般会按下面这张表来快速决策场景建议小图标数量多超过6~8个且会在页面多处重复使用优先考虑精灵图或 SVG Sprite项目还跑在 HTTP/1.1 / 内网 / 旧 CDN 环境精灵图依然值得用需要兼容 IE10 及以下老浏览器位图精灵图比 SVG 兼容性更稳妥图标颜色需要动态变化hover 变色、皮肤切换不要用位图精灵图改用 SVG / CSS mask 或字体图标图标本身就是高清大图、色彩丰富的照片绝不合并单图独立加载更合理图标只有两三个且不是高频复用直接单独放图片或 Base64 内联别折腾精灵图团队已全面使用 HTTP/2 现代构建工具优先 SVG Spritesymbol 方式位图精灵图作为降级方案一句话总结我的原则技术选型看场景不看热度。精灵图有没有价值先回答一个问题——你的页面真的会因为减少请求而变快吗如果答案是肯定的那就值得用。2. 从零到一制作一张规范精灵图的完整步骤讲完“要不要用”接下来就是“怎么用”。很多人知道精灵图的概念但真到了要做的时候要么手工切图切到怀疑人生要么做出来的图间距太挤、定位全靠瞎试。这一章我完整走一遍流程。2.1 素材整理与命名规范比想象中重要我做精灵图的第一步永远不是打开 PhotoShop而是先在项目里把图标素材整理清楚。假设你要做一套电商页面的图标购物车、搜索、用户、收藏、箭头、优惠券圆切等等。不要一股脑全丢进一个文件夹先按模块分类src/ icons/ common/ icon-cart.png icon-search.png icon-user.png nav/ icon-arrow-left.png icon-arrow-right.png icon-arrow-down.png命名一定要语义化格式统一。我惯用的规则是icon-前缀 业务语义 状态后缀例如icon-cart.png、icon-cart-hover.png。千万不要出现1.png、2.png这种废名精灵图生成之后每个图标都会对应一个类名或变量命名就是给后面的调用铺路。整理素材的同时顺手确认所有图标的视觉大小是否一致、是否有统一的比例。比如一个 32x32 的购物车图标和一个 16x16 的箭头图标如果混在一张图里后面写 CSS 时容易记错尺寸。我一般会把基础图标统一到 32x32 或 24x24 的逻辑尺寸小箭头最少也要 16x16这样拼图时心里有数。2.2 用PhotoShop手工合成一张最简单的精灵图用手工方式做精灵图要求你对 PS 的切图、选框、参考线有一定基础不过操作其实很机械。新建一个空白画布尺寸根据图标数量和排列方式预估。比如 8 个 32x32 的图标横排每个图标间距 8px那么画布宽度大约是 8×32 7×8 312px高度 32px再预留一点 4px 的内边距会更安全。将每个图标拖入画布用参考线或网格辅助对齐。这里有个小技巧先拖入最左侧的图标在它的右边添加一条参考线后续图标都以参考线为基准排列避免肉眼对齐造成的偏移。图标之间一定要留至少 4px 的间距建议 8px原因后面会专门讲。排列时保证水平垂直都在同一条轴线上不然写 background-position 时会发现图标偏上或偏下。排版完成后关掉背景层执行“文件” - “导出” - “导出为”选择 PNG 格式。如果图标都是纯色、边缘没有太多渐变导出为 PNG-8 能大幅减小体积如果包含半透明抗锯齿边缘用 PNG-24 更稳。导出后打开“信息”面板用移动工具选中单个图标查看它在图层中的 X、Y 坐标并记录下来。这就是你后面写 CSS 时需要的偏移量。手工方式的优点是一张图完全可控缺点是真实项目里图标经常增删改。今天加一个、明天换一个每次都手动重排效率太低了。所以生产项目我更推荐走自动化构建方案。2.3 自动化生成gulp.spritesmith用法与配置目前最主流的位图精灵图自动生成方案还是基于 gulp.spritesmith 或 postcss-sprites。原理都一样监听图标文件夹自动完成拼接、输出精灵图和对应的 SCSS/LESS/CSS 文件。我以一个简单的 gulp 任务为例const gulp require(gulp); const spritesmith require(gulp.spritesmith); gulp.task(sprite, function () { return gulp.src(src/icons/**/*.png) .pipe(spritesmith({ imgName: sprite.png, cssName: _sprite.scss, padding: 8, algorithm: binary-tree, cssFormat: scss, })) .pipe(gulp.dest(dist/)); });参数说明imgName生成的精灵图文件名。cssName生成的样式文件名里面会包含每个图标对应的类或 Sass 变量。padding每个图标之间的间距我固定设为 8。algorithm拼图算法binary-tree能最大化利用画布空间是最常用的一种。cssFormat生成样式文件的格式可以选scss、less、css等。生成出来的_sprite.scss大致长这样$icon-cart-x: 0px; $icon-cart-y: 0px; $icon-cart-offset-x: 0px; $icon-cart-offset-y: 0px; $icon-cart-width: 32px; $icon-cart-height: 32px; $icon-cart-image: sprite.png;这样你在业务代码里就可以把这些变量封装成一个混合宏来调用我一般这样封装mixin sprite-icon($icon) { display: inline-block; width: icon-width($icon); height: icon-height($icon); background-image: url(sprite-url($icon)); background-position: icon-position($icon); }如果用 spritesmith 的默认模板它直接会生成类似.icon-cart { background-position: 0px 0px; }这样的类你只需要在 HTML 里写i classicon-cart/i,并补上宽高和background-image。我更建议你在生成结果上再封装一层把宽高也带上这样调用时最省心。2.4 图片间距为什么不能省一个差点让我改到崩溃的教训刚开始做精灵图时我觉得留白间距纯粹是浪费画布空间于是把 padding 设成 0图标之间严丝合缝。结果项目上线后同事反馈某个图标在部分浏览器上显示时边缘会带出旁边图标的“残影”。原因并不复杂高清屏下 CSS 像素与实际物理像素之间不是一一对应关系。devicePixelRatio 为 2 或 3 时浏览器要把 CSS 像素换算成物理像素换算过程中经常出现小数。比如 background-position 写的是-33px在 2 倍屏上实际对应的是-66px一旦出现亚像素精度问题浏览器就会通过取整或插值去计算最终显示边缘很容易混淆到相邻图标的颜色。留白之所以关键就是给这种精度误差留了缓冲地带。间距 8px 时就算出现 1~2px 的取样偏差显示的还是目标图标的内容不会看到旁边图标的像素。这也是为什么所有成熟工具默认都会设置 padding不是没有道理的。私底下测试时我经常用 4px 甚至 8px实际项目中从没因为这些间距后悔过。3. background-position定位把坐标系算明白精灵图的调用核心就一个属性background-position。但很多新手死就死在不会算这个属性这个坐标数值一错整个图标显示就乱七八糟。我尽量把它的底层逻辑讲得直白一点。3.1 偏移量的底层逻辑从切图坐标到CSS的负数偏移先明确一个前提精灵图是一张大图CSS 背景图默认是“从元素盒模型的左上角开始对齐平铺”的。如果你只写background: url(sprite.png)那么元素背景显示的就是精灵图的左上角那一块区域。为了让位于大图右上角或右下角的图标正好出现在元素的可视区里我们需要把背景图“往左上方向移动”把目标图标移动到元素可视区的中心或左上角。这个“往左上移动”的距离就是 background-position 中填写的负数值。比如目标图标在精灵图中的左上角坐标是 (80, 60)那就意味着我们要把精灵图向左移 80px、向上移 60px对应 CSS 就是background-position: -80px -60px;所以 PS 里记录的 X、Y 坐标到了 CSS 里要取负数。方向搞反是精灵图不显示内容的头号原因。3.2 一个真实icon的定位全过程拿出计算器跟我操作假设我有一张 400x300 的精灵图里面放了 8 个图标。现在要显示其中一个 32x32 的用户图标它在精灵图里左上角坐标是 (80, 60)。那么样式这样写.icon-user { display: inline-block; width: 32px; height: 32px; background-image: url(sprite.png); background-repeat: no-repeat; background-position: -80px -60px; }有两点需要特别注意容器的宽高必须等于图标本身的宽高这样背景可视区正好露出整个图标。容器大了会看到目标图标周围的空白或相邻图标容器小了会把图标裁掉一部分。background-repeat必须设为no-repeat否则如果容器比精灵图小、又没关平铺背景会在容器里重复拼贴整张精灵图视觉上完全是一团乱麻。再看一个稍微复杂的例子。如果图标在精灵图中是横向排列的第一个图标是 (0, 0)第二个是 (40, 0)每个图标占 32x32间距 8px。那么第二个图标的定位就是background-position: -40px 0;第三个是background-position: -80px 0;。规律很明显x 的负值依次递减y 始终不变。3.3 百分比定位background-position: 50% 50%的隐藏公式很多教程讲到 background-position 时会顺带提一句“50% 50% 是居中”但精灵图场景下这个理解很容易害死人。因为百分比的计算公式不是“背景图左边缘到容器左边缘的距离等于宽度的 50%”而是偏移量 X (容器宽度 - 背景图片宽度) × 百分比X 偏移量 Y (容器高度 - 背景图片高度) × 百分比Y当容器宽度和背景图片宽度相等时公式结果为 0也就是 50% 和 0% 效果一样。但当背景图片宽度远大于容器宽度时50% 算出来是一个很大的负值背景图会向左偏移很多显示的根本不是你预想的区域。举一个具体的精灵图反例容器是 32x32精灵图是 400x300目标图标在 x80 处。如果我们写background-position: 50% 0计算结果是(32 - 400) × 50% -184px背景图左边缘被移到了 -184px显示的是精灵图中 x≈184px 处的图标而不是 80px 处。因此在精灵图定位中我基本只用负像素值不用百分比。只有在“背景图和容器一样大”这种特殊场景下百分比才安全。如果你想用 CSS 变量做图标库封装也建议直接用变量存 x、y 的像素值而不是百分比。3.4 配合伪元素与CSS选择器把精灵图封装成可复用的图标类精灵图项目里我最常用的封装套路是用一个基类加若干个修饰类配合:hover、:not()、::before这些 CSS 选择器做到“一处定位到处引用”。先看基类.s-icon { display: inline-block; background-image: url(sprite.png); background-repeat: no-repeat; vertical-align: middle; }然后给每个图标定义一个修饰类.s-icon--cart { width: 32px; height: 32px; background-position: -80px -60px; } .s-icon--search { width: 32px; height: 32px; background-position: -120px -60px; }HTML 中直接这样引用语义化也很清晰i classs-icon s-icon--cart/i button classs-icon s-icon--search/button如果图标需要出现在按钮文字旁边用伪元素更灵活.btn-buy::before { content: ; display: inline-block; width: 24px; height: 24px; background: url(sprite.png) no-repeat -160px -60px; margin-right: 6px; }这里其实就用到了 CSS 伪元素和类选择器的组合。写样式时有一个坑要注意如果基类和修饰类里都定义了background修饰类里的简写会覆盖基类的background-image导致精灵图失效。所以我统一用background-image、background-position分开写避免简写覆盖问题。4. 高清屏兼容Retina精灵图的double方案早期的精灵图方案在高清屏上有个尴尬问题明明设计稿里图标很清晰放到 iPhone 或 Retina Mac 上就发虚。这是因为传统精灵图是为 1x 屏幕准备的物理像素和 CSS 像素一对一但在 2x 屏上一个 CSS 像素需要 4 个物理像素来渲染图片不够只能拉伸于是边缘模糊、细节失真。解决办法就是额外准备一套 2x 精灵图。4.1 background-size缩放原理为什么是整张图而不是单个图标2x 精灵图的实际像素宽度是 1x 图的两倍。比如 1x 图是 400x3002x 图就是 800x600。如果直接把 2x 图作为背景浏览器默认会按照图片实际像素渲染也就是显示 800x600 的物理尺寸对于 32x32 的 CSS 容器背景可视区只有容器大小这意味着雪碧图的坐标系统全部变了原来 1x 图里图标坐标是 (80,60)同一图标在 2x 图里实际坐标可能是 (160,120)。如果 CSS 里 background-position 继续写 -80px -60px浏览器会把 2x 图整体缩放到 800x600 的物理尺寸然后定位显示出来的区域完全对不上。所以我们必须给 background-size 一个全局缩放值让 2x 图在计算定位时“假装”自己是一张 400x300 的图。写 background-size 时要填的是整张精灵图的逻辑尺寸也就是 1x 图的尺寸而不是某个图标的尺寸。这是 Retina 适配中最容易踩的坑。写成background-size: 400px 300px;之后background-position 仍然按照 1x 时的坐标写逻辑就完全统一了。4.2 一套2x精灵图的适配写法实际开发中我一般这样组织代码。首先准备两张图sprite.png和sprite2x.png。默认使用 1x 图通过媒体查询在 2x 及以上屏幕切换.icon { display: inline-block; width: 32px; height: 32px; background-image: url(sprite.png); background-repeat: no-repeat; background-position: -80px -60px; } /* 适配2x以上高清屏 */ media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { .icon { background-image: url(sprite2x.png); background-size: 400px 300px; /* 1x精灵图的逻辑尺寸 */ } }注意媒体查询里的min-resolution: 192dpi就是 2x 屏的标准另一个带-webkit-前缀的写法是为兼容旧版 Safari 和 Chrome。如果你的项目里只有一套 2x 图也可以直接在 base 样式里使用 2x 图并设置 background-size这样 1x 屏和 2x 屏都能显示只是 1x 屏会多消耗一点内存和下载流量在图片体积可控的情况下也能接受。如果你的项目使用 SCSS可以把坐标和 background-size 抽成变量避免每次手写$sprite-url: sprite.png; $sprite-url-2x: sprite2x.png; $sprite-width: 400px; $sprite-height: 300px; mixin sprite-item($x, $y, $w, $h) { display: inline-block; width: $w; height: $h; background: url($sprite-url) no-repeat $x $y; media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { background-image: url($sprite-url-2x); background-size: $sprite-width $sprite-height; } } .icon-user { include sprite-item(-80px, -60px, 32px, 32px); }这样好处很明显所有坐标都只写一套换图、换版本时只需改混合宏里的变量业务代码完全不用动。4.3 image-set与替代方案的取舍CSS 里有一个image-set()函数可以在一定程度上替代上面复杂的媒体查询写法例如.icon { background-image: image-set( url(sprite.png) 1x, url(sprite2x.png) 2x ); }它的最大好处是不用手写媒体查询但现实问题是兼容性。虽然现代浏览器基本都支持了但碰到 IE、老版本 Edge 依然只能靠兜底。所以我的习惯是在需要 robust 兼容的生产环境还是用媒体查询方案image-set 可以作为一种简化写法用在现代化管理后台这类对浏览器环境可控的项目里。还有一点值得提醒如果你的图标总量不大但又想高清屏不出问题很多时候用内联 SVG 图标是比位图精灵图更好的选择。SVG 是矢量格式任意缩放都清晰颜色也能通过 CSS 直接控制。位图精灵图的适用场景更像是一种相对保守、兼容性最稳的方案。现在很多团队做图标库都优先 SVG Sprite把所有 SVG symbol 合并到一个文件中通过use引用这种方案继承了“减少请求”的优点又解决了清晰度和颜色变化的问题。如果你维护的是新项目且不需要兼容旧浏览器不妨认真学一下 SVG Sprite。5. 实战排坑我在项目里遇到过的几个典型问题精灵图本身不复杂但它导致的线上问题往往很隐蔽比如“有的用户看到正常、有的用户图标串了”“刷新后偶发残影”。这里我把遇到过的、带典型性的问题整理成几段排查经验。5.1 背景图缺失或显示出一大堆图标残影先查background-repeat这个问题排在第一位因为出现频率实在太高。新手写样式时只写了background-image: url(sprite.png); background-position: -80px -60px;忘记写background-repeat: no-repeat。结果容器 32x32而背景图默认在水平和垂直方向平铺于是整个容器就像一个取景框里面填满了重复排列的精灵图案。视觉上就是“一堆图标挤在一起”根本看不出要什么。排查方法打开开发者工具选中目标元素查看计算样式里background-repeat是否为no-repeat。如果显示 repeat补上background-repeat: no-repeat立即就能恢复。顺带提醒一下background 简写属性里也可以一次带上 no-repeat但要注意简写属性的顺序以及它会覆盖其他背景子属性的问题。项目里如果你定义了一个全局的.s-icon基类建议把background-repeat写在基类里子类只用 background-position这样最不容易漏。5.2 hover切换状态时仍然出现闪白根源不在图片格式而在请求时机我前面说过精灵图能消除 hover 闪白但前提是两种状态确实在同一个精灵图文件里。有些项目并没有把普通态和 hover 态放在同一张雪碧图里而是 hover 时换了一张全新的背景图这时候闪白就不可避免鼠标移入瞬间浏览器才去发新图片请求网络一慢就是白花花一片。解决思路有两个。最彻底的是把 hover 态图标也拼进同一张精灵图通过 background-position 切换。其次是如果确实没法把它们放在同一张图里比如 hover 态来自设计稿单独切图那就提前用隐藏手段把图片加载到本地我一般这样处理.preload { background-image: url(hover.png), url(normal.png); }或者用link relpreload预加载这张 hover 图。不过说实话这些都是在不得已时的补救能用同一张精灵图的状态切换永远是首选。5.3 定位错乱但没有改动过代码先怀疑盒模型和父容器尺寸有一种很诡异的场景精灵图样式写好了本地测试正常但页面一上线发现某几个图标明显偏移。排查半天代码发现没改过样式。最后原因指向盒模型。浏览器默认情况下box-sizing是content-box但不少前端框架会设置* { box-sizing: border-box; }。如果你给元素设置了 padding 和 border实际可视区域的计算会发生变化背景定位区域也跟着变。比如我给一个按钮同时设置了padding: 10px和精灵图背景按钮的背景定位区域是整个 border-box但由于 padding 撑大了内容区背景定位的基准会偏移视觉上图标就不在预期位置了。还有另一种常见情况图标用了display: inline-block但父容器的font-size或line-height较大导致元素垂直基线移动看起来“明明 background-position 是对的怎么图标不在按钮垂直居中”。这不是 background-position 的问题而是行内块元素基线对齐问题。处理方式一般是给图标设置vertical-align: middle;或改用display: block并配合 flex 居中。排错顺序我建议这样走用开发者工具检查元素实际宽高是否等于图标尺寸检查背景定位区域是否受 padding/border 影响检查 vertical-align 是否造成了视觉偏移最后再看 background-position 数值是否因为构建工具或缓存产生了变化。5.4 图片格式与压缩为什么我不建议用JPG做精灵图很多从设计稿直接切出来的素材是 JPG但我几乎从不用 JPG 做精灵图。原因很直接JPG 是有损压缩遇到纯色图标或线条边缘会出现明显压缩痕迹而且 JPG 不支持透明通道图标旁边的透明区域会被填成白色或黑色拼到精灵图里直接毁掉整个画面。位图精灵图我优先用 PNG-24如果图标是纯色且没有半透明需求可以压缩成 PNG-8 来减小体积。这里有一个关键技巧用 png8 压之前先确认边缘抗锯齿的透明像素是否保存完整否则边缘会出现一圈难看的毛边。要是想进一步减小精灵图体积可以在构建流程里跑 TinyPNG 或者用 ImageOptim对 PNG 进行无损压缩。我自己在 gulp 任务里会再接一个gulp-tinypng-compress大概能把 PNG 体积减小 50%~70%成本很低收益很高。5.5 开发中定位调试的小技巧别靠“眼睛猜”写精灵图样式时最容易出 bug 的就是坐标。我调试时有个非常实用的习惯先用浏览器开发者工具临时修改元素的 background-position比如先设成0 0看到精灵图左上角再手动微调 x、y直到图标落到容器里。这个过程比反复改代码刷新页面快得多。另外如果你用的是自动化生成的 SCSS 文件可以在调试时临时在元素上覆盖 background-position 来源变量的值找到正确坐标后回写到生成模板里。这个调试思路对任意精灵图方案都通用。6. 我给前端新人的几条实用建议精灵图本身不是新东西但我见过太多人在这个知识点上绕弯子。最后结合我自己的经验给几条可以直接落地的建议。第一新项目优先考虑 SVG Sprite位图精灵图作为降级方案。这句话不是否定 CSS 精灵图而是因为 SVG 在清晰度、可变色、体积控制上的优势太明显。只要你的图标是纯矢量图形没有复杂纹理那么 SVG Sprite 几乎是更优选。反过来如果团队里就是有大量位图素材、项目还需要兼容老浏览器那就放心用位图精灵图别被“过时”的评价带偏。第二把精灵图的制作纳入构建流程而不是手工切图。手工方式适合临时演示和个人项目生产环境里图标增删改太频繁每次手工重排都会消耗大量时间而且容易出错。花一上午时间配好 gulp.spritesmith 或 postcss-sprites后面省下的时间至少是十倍。第三设计稿阶段就和设计师约定好图标尺寸与间距。如果设计师切出来的图标尺寸五花八门拼图后往往要写很多奇怪的宽高坐标。我现在做项目会先给设计师提一套基础图标尺寸规范比如通用 32x32、小箭头 16x16这样后续生成精灵图和写样式都会顺畅很多。第四遇到定位错误时用工具而不是靠肉眼。浏览器 DevTools 的实时样式编辑功能是排错神器直接在 Elements 面板调 background-position看着效果修等定位对了再把数值填回去比来回改代码刷新效率高得多。第五不要为了减少请求数而把所有图片都塞进一张精灵图。精灵图适合的是“小、多、重复”的图标类资源不适合色彩丰富的照片和需要独立加载的大图。强行把所有图片拼在一起生成的文件体积会失控首屏加载不一定会变快反而可能变慢。希望这篇关于 CSS 精灵图的内容能帮你把这个知识点彻底盘透。如果你接下来正好要做一个带小图标的前端页面不妨按照上面的流程先走一遍手工制作再试试自动化构建把两种方式都体验一次。只有亲自动手排过一两个定位错乱的问题你才会真正理解 background-position 背后的计算逻辑。之后不管项目里是继续用位图精灵图还是转向 SVG Sprite你的底子都会比只背概念的人扎实不少。
返回列表