Retina屏幕适配的那些坑和优化技巧
我的写法,亲测靠谱
Retina 屏幕适配这事,我做了好几年项目了,一开始也以为就是换张高清图就完事。结果上线后 QA 一反馈:「你这图标怎么模糊?」我一看,好家伙,果然糊成一片。折腾了半天才发现问题出在图片倍率和 CSS 的处理方式上。
后来我总结了一套自己用着顺手的方案,核心思路就一句话:按设备像素比提供资源,优先用 CSS 控制尺寸,JS 只做兜底或动态场景。
比如最常见的图片高清化,我现在基本都这样写:
<img src="image-1x.jpg"
srcset="image-1x.jpg 1x, image-2x.jpg 2x, image-3x.jpg 3x"
alt="示例图片">
img {
width: 100px;
height: auto;
image-rendering: -webkit-optimize-contrast;
}
这里的关键是 srcset + 像素倍率描述符(1x, 2x 这种)。浏览器会根据当前设备的 devicePixelRatio 自动选最合适的图,不用你手动判断。而且这种写法兼容性也不错,IE 不行,但现代浏览器基本都没问题。
我以前还试过纯 JS 判断 dpr 然后替换 src,代码又长又容易出错。现在回头看,真没必要。除非你在做一些特别动态的资源加载,否则别碰 JS 动态切换这套逻辑。
这几种错误写法,别再踩坑了
先说说我见过最多、我自己也犯过的几个典型错误。
- 只给一张 2x 图,CSS 宽高直接写实际尺寸:比如设计稿是 100px 宽,你就写
width: 100px,然后扔个 200×200 的图进去。看着是高清了,但放大三倍屏(dpr=3)就糊了。而且流量白浪费,非 Retina 用户也在下大图。 - 用 background-image + media query 写一堆断点:像下面这样:
.bg-img {
background-image: url(image-1x.png);
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.bg-img {
background-image: url(image-2x.png);
}
}
@media (-webkit-min-device-pixel-ratio: 3), (min-resolution: 288dpi) {
.bg-img {
background-image: url(image-3x.png);
}
}
这种写法看着挺标准,但我建议避开。原因有二:一是维护成本高,每个图都要写三遍;二是分辨率单位混乱,dppx、dpi、device-pixel-ratio 各种混用,谁知道哪个浏览器支持哪个?而且一加新设备,你还得补 media query。
更离谱的是,有些项目里甚至把所有高清背景图都这么写一遍,几千行 CSS 全是这种重复代码,改起来要命。
- SVG 不压缩,随便丢进去:SVG 是矢量,天生适配 Retina,没错。但很多人直接从设计稿导出 SVG,几十 KB 一个文件,里面全是冗余路径和编辑器元数据。加载慢不说,还可能卡顿渲染。记得一定要用工具压一下,比如 SVGO 或者在线压缩网站。
实际项目中的坑
去年我们有个 H5 活动页,首页轮播图要用全屏背景图。设计师给了 2x 和 3x 版本,本来没问题。但上线后 iOS Safari 上某些机型显示模糊,安卓却正常。查了半天发现是用了 transform: scale(1.01) 强制触发硬件加速……这一触发,iOS WebKit 的图片渲染就抽风,自动降质了。
解决方案反而是去掉那个多余的 scale,换成 will-change: transform 来优化性能。这种细节不踩一次坑根本想不起来。
还有一次,团队新人用 canvas 绘图生成头像徽章,结果 Retina 下全是马赛克。原因是没考虑 canvas 的 devicePixelRatio,默认画布在高清屏下会被拉伸。修复方法如下:
function setupCanvas(canvas, width, height) {
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
canvas.width = width * dpr;
canvas.height = height * dpr;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
ctx.scale(dpr, dpr);
}
这段代码我封装成了小工具,以后凡是有 canvas 的地方都检查一遍。不然等用户投诉“头像模糊”,你都不知道从哪查起。
字体图标?能不用就不用
以前流行用 iconfont 解决高清问题,确实一度挺好用。但现在我不太推荐了。问题有几个:
- 字体加载有闪烁(FOIT/FOUT)
- 颜色控制麻烦,要靠
color属性 - 伪元素不好做复杂效果(比如描边、阴影)
- 某些安卓机渲染发虚
我现在更倾向用 inline SVG 或者雪碧图配合 background-size。尤其是小图标,直接内联 SVG,体积小、清晰、样式可控。
如果必须用 iconfont,至少确保:
.icon {
font-display: swap; /* 避免阻塞 */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
最后一点提醒:别忘了低网速用户
高清是好,但也得考虑流量。我见过有些页面一口气加载五六张 3x 背景图,每张几百 KB,WiFi 下都卡。这时候你可以考虑懒加载 + 按需加载策略。
比如首屏关键图用 srcset 提供多倍率,非首屏用 JS 判断 dpr 后再加载对应资源:
const isRetina = window.devicePixelRatio > 1;
const imgUrl = isRetina ? 'hero-2x.jpg' : 'hero-1x.jpg';
// 懒加载时使用
lazyImage.onload = () => lazyImage.src = imgUrl;
`>
<p>或者更激进一点,用 <code>loading="lazy"</code> + <code>srcset</code> 双保险:</p></code></pre>html
<img src="placeholder.jpg"
srcset="normal.jpg 1x, retina.jpg 2x"
loading="lazy"
alt="内容图片">
``
以上是我踩坑后的总结,希望对你有帮助
Retina 适配没有银弹,不同项目得灵活处理。但总的来说,srcset 是目前最省心的方式,优先用它。canvas 和动态绘图一定要注意 dpr 缩放。字体图标能避就避。最重要的是——别光在 MacBook 上测,拿台老款 iPhone 或低端安卓机试试,你会发现很多“理论上应该没问题”的东西其实糊得不行。
这个技巧的拓展用法还有很多,后续会继续分享这类博客。以上是我个人对这个主题的完整讲解,有更优的实现方式欢迎评论区交流。

暂无评论