以下是由 Parker Bennett 发布的客座文章。Parker 之前曾在 CSS-Tricks 上发表过文章,在 Crop Top 文章中,他讨论了灵活图像的定位问题。这篇文章是对该文章的良好补充,在无休止的响应式图像传奇中,展示了另一种选择。它也与 昨天的文章 形成有趣的对比,您可以看到解决这个问题的不同方法。
我们希望网页加载速度快,我们希望有视网膜图像。我们可以兼得吗?



我们不想向不需要的人提供超大尺寸的图像。有几种方法可以避免这种情况,包括 Scott Jehl 的 picturefill、服务器端解决方案和 延迟加载技术,但最简单的方法可能是使用 background-image
和 CSS 媒体查询的魔力。这样,您幸运的视网膜用户就能获得高分辨率 @2x 版本,而我们其他人……好吧,至少我不必等待巨型文件下载。
(为了便于下面的说明,我将我的“移动优先”默认图像设置为黑白,中等尺寸版本设置为棕褐色,如果您将窗口调整到足够宽或使用高 DPI 屏幕,还会显示一个更大的彩色版本。)

/* base background-image class */
.bg-image {
width: 100%;
/* default background-position */
background-position: center center;
/* lt ie 8 */
-ms-background-position-x: center;
-ms-background-position-y: center;
/* scale proportionately */
background-size: cover;
/* IE8 workaround - http://louisremi.github.io/background-size-polyfill/ */
-ms-behavior: url(/backgroundsize.min.htc); }
/* mobile-first default (b&w) */
.bg-image-sedona {
background-image: url(img/photo-sedona_512x320.jpg);
background-position: center 21%; }
/* example media queries (IE8 needs this:
http://code.google.com/p/css3-mediaqueries-js) */
@media
/* "mama-bear" - plus any-retina */
only screen and min-width : 513px,
only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and ( min-device-pixel-ratio: 1.5) {
/* mid-size (sepia) */
.bg-image-sedona {
background-image: url(img/photo-sedona_1024x640.jpg); }
}
@media
/* "papa-bear" - plus larger retina */
only screen and (min-width : 1025px),
only screen and (min-device-width : 768px) and (-webkit-min-device-pixel-ratio: 1.5),
only screen and (min-device-width : 768px) and ( min-device-pixel-ratio: 1.5) {
/* high-res (color) */
.bg-image-sedona {
background-image: url(img/[email protected]); }
}
显示背景图像的 div 需要一个高度,可以手动设置,也可以像我在这里做的那样,通过包装一个设置为响应式缩放的透明“代理” img
来设置 (更多内容请见此处)。
现在,您可能已经注意到,页面第一次呈现大型图像时,加载过程中可能会出现明显的延迟。即使是较小的图像,在缓存之前,加载或替换时也可能会出现令人讨厌的闪屏。但我们可以解决这个问题……
CSS3 多重背景:它们如何叠放
较新的浏览器允许我们通过声明以逗号分隔的多个值来堆叠背景图像。通过这种方式,我们可以显示原始缓存的图像,同时替换图像平滑地加载到其上(请注意下面的代码中的 堆叠顺序)。


要查看它的实际效果,请缩小浏览器窗口并清空缓存(在 Chrome 菜单中选择“清除浏览数据”或在 Safari 的“开发”菜单中选择“清空缓存”)。现在重新加载页面。向下滚动到此处并将窗口扩展到彩色图像加载到顶部为止。(或者尝试此 弹出窗口。)
不幸的是,旧版浏览器(例如 IE8*)会看到多个背景声明并束手无策 - 不会显示任何内容(糟糕!)。因此,我们需要使用 modernizr.js 进行功能检测,并创建回退(如果我们希望这些浏览器显示的内容大于移动优先默认图像)。
/* .bg-image and .bg-image-sedona same as above.
.multiplebgs class added by modernizer.js. */
@media
/* "mama-bear" - plus any-retina */
only screen and min-width : 513px,
only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and ( min-device-pixel-ratio: 1.5) {
/* no-multiplebgs - mid-size fallback (sepia) */
.no-multiplebgs .bg-image-sedona,
/* upscale to mid-size if no javascript */
.no-js .bg-image-sedona {
background-image: url(img/photo-sedona_1024x640.jpg); }
.multiplebgs .bg-image-sedona {
background-image:
/* mid-size on top (sepia) */
url(img/photo-sedona_1024x640.jpg),
/* mobile-first default on bottom (b&w) */
url(img/photo-sedona_512x320.jpg);
}
}
@media
/* "papa-bear" - all three images */
only screen and (min-width : 1025px) {
/* no-multiplebgs fallback is above */
.multiplebgs .bg-image-sedona {
background-image:
/* high-res on top (color) */
url(img/[email protected]),
/* mid-size in middle (sepia) */
url(img/photo-sedona_1024x640.jpg),
/* mobile-first default on bottom (b&w) */
url(img/photo-sedona_512x320.jpg);
}
}
@media
/* larger retina device - triggered immediately,
so mid-size image not needed */
only screen and (min-device-width : 768px) and
(-webkit-min-device-pixel-ratio: 1.5),
only screen and (min-device-width : 768px) and
( min-device-pixel-ratio: 1.5) {
/* no-multiplebgs fallback is above */
.multiplebgs .bg-image-sedona {
background-image:
/* high-res on top (color) */
url(img/[email protected]),
/* mobile-first default on bottom (b&w) */
url(img/photo-sedona_512x640.jpg);
}
}
标准 JPEG 与渐进式 JPEG
对于 JPEG,图像在多重背景中渲染到另一个图像上的方式取决于它的保存方式。标准 JPEG 在下载时按顺序“绘制”图像。渐进式 JPEG 在完全下载后“出现”。(标准方式对我来说似乎更平滑。)请注意,像 ImageOptim 这样的图像压缩器默认设置为渐进式保存(Jpegrescan 已选中),因为它可以节省少量空间。
当然,我们不希望用户不必要地下载图像,也不想让维护变得过于复杂,因此,务必使断点保持克制,并从逻辑上进行思考。但是,现在我们可以使图像交换变得不那么显眼,这为我们打开了一些可能性……
模拟“lowsrc”
在蒸汽为互联网提供动力的时代,拨号接入速度非常慢,因此他们创建了一个特殊的属性,以便用户在下载动画 gif 的 1 分半钟时间内看到某些内容:它被称为“lowsrc”,看起来像这样:IMG SRC="big.gif" LOWSRC="small.gif"
。
浏览器在 20 世纪 50 年代后期停止支持此功能。
但是现在,类似的功能可能很有用,这样用户就可以在下载视网膜就绪的高分辨率图像的 2 秒半时间内看到某些内容。(而且别忘了,4K 即将到来。)
现代浏览器非常智能,可以在获取图像后立即填充图像,因此,通过指定较小、更压缩的“lowsrc”图像作为默认图像,然后在我们的 CSS 媒体查询中将它们叠放在 @2x 视网膜图像下方,事情可能会感觉更敏捷。我们可以使用 jQuery 更进一步……
其想法是在使用我们的默认“lowsrc”图像完全呈现页面之前,暂缓图像交换。然后,我们使用 jQuery 向我们的主要“bg-image”类添加“hd”类,这将触发我们的媒体查询来交换图像。我们也可以暂缓并“延迟加载”高分辨率图像,以便在滚动到它们时加载,使用类似于 jQuery Waypoints 插件 的内容。
/* .bg-image and .bg-image-sedona same as above
.hd class added by jQuery after page loads
(or perhaps "lazy loaded" as user scrolls) */
@media
/* "mama-bear" - plus any-retina */
only screen and (min-width : 513px),
only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and ( min-device-pixel-ratio: 1.5) {
/* no-multiplebgs - mid-size fallback */
.no-multiplebgs .bg-image-sedona.hd,
.no-js .bg-image-sedona {
/* mid-size (sepia) */
background-image: url(img/photo-sedona_1024x640.jpg); }
.multiplebgs .bg-image-sedona.hd {
background-image:
/* mid-size on top (sepia) */
url(img/photo-sedona_1024x640.jpg),
/* mobile-first "lowsrc" on bottom (b&w) */
url(img/photo-sedona_512x320.jpg); }
}
@media
/* "papa-bear" - size only */
only screen and (min-width : 1025px) {
/* no-multiplebgs fallback is above */
.multiplebgs .bg-image-sedona.hd {
background-image:
/* high-res on top (color) */
url(img/[email protected]),
/* mid-size in middle (sepia) */
url(img/photo-sedona_1024x640.jpg),
/* mobile-first "lowsrc" on bottom (b&w) */
url(img/photo-sedona_512x320.jpg); }
}
@media
/* larger retina device, triggered immediately,
so mid-size image is not needed */
only screen and (min-device-width : 768px) and
(-webkit-min-device-pixel-ratio: 1.5),
only screen and (min-device-width : 768px) and
( min-device-pixel-ratio: 1.5) {
/* no-multiplebgs fallback is above */
.multiplebgs .bg-image-sedona.hd {
background-image:
/* high-res on top (color) */
url(img/[email protected]),
/* mobile-first "lowsrc" on bottom (b&w) */
url(img/photo-sedona_512x320.jpg); }
}
/* waits until everything is loaded, not just DOM is ready */
$(window).load(function() {
$('.bg-image').addClass('hd');
});
/* "lazy loads" when .bg-image appears in viewport -
http://imakewebthings.com/jquery-waypoints/ */
$('.bg-image').waypoint(function(direction) {
if (direction === 'down') {
$(this).addClass('hd');
}
}, { offset: 'bottom-in-view', triggerOnce: true });
/* other offsets: '100%' (image top at viewport bottom),
'125%' (just beyond the viewport, about to scroll in) */
总结
理想情况下,我希望看到这种方法以更自动化的方式工作,例如 picturefill.js,但从移动优先 img
而不是 data-src
属性进行扩展。你怎么看?您可以查看更多内容的源代码,在 CodePen 上查看所有演示,或者 在此处下载示例文件。如果您有任何问题、评论或更正,请给我留言:parker@parkerbennett.com.
* IE8 不支持多重背景,但如果您能为图像声明宽度和高度,则可以使用 Nicolas Gallagher 的 这种伪元素方法 来实现类似的功能。
在最近的一个项目中,我使用了针对移动设备的小型图像,而不是透明的“代理”img。然后在更大屏幕上,我隐藏了图像并显示了一个更大的背景图像。虽然这很简单,但它没有考虑针对视网膜显示器的其他图像。
我发现没有“完美”的方法来做到这一点真是令人沮丧。每种方法都以某种方式很糟糕,我想,这取决于找到最不糟糕的方法。
如果我没弄错,您不能使用媒体查询来隐藏和显示低分辨率、高分辨率和高 DPI 吗?
我认为有一个解决方案,但奇怪的是,它在黑莓上有效,但在 iPhone 上却直接显示出来!……我不确定您使用的是哪个浏览器,虽然我使用的是 Opera 移动版,但在我的 iPhone 上,我也使用的是 Operate 移动版,但由于某种奇怪的原因,它弄乱了所有内容!
Gareth,如果您使用的是 iPhone,是 4、4S 还是 5?谢谢
这是一种有趣的技术,但它确实意味着您要加载多个资源。如果您在视网膜 MBP 上连接到宽带连接,这很好,但是如果您在视网膜 iPhone 或其他高分辨率移动设备上,而您不在家,那么您会增加下载的数据量。
这绝对是应该避免的事情。谁愿意浪费数据? 某个地方一定有人拥有完美的解决方案,却一直隐瞒着我们。
这就是整个情况最疯狂的地方。响应式图片是以下因素之间的平衡:
这就是让它成为野火的原因。
如果设置了最小宽度和最大宽度值,是否只会加载一个文件?
如果我的表述不够清晰,我道歉,但我认为可以使用媒体查询来减少不必要的下载。例如,我们可以使用 min-device-width 和 min-device-pixel-ratio 来单独定位视网膜 iPhone。这样它们就能获得中等尺寸的图像,而不是巨大的图像。
没有完美的解决方案,只有经过考虑的折衷方案。这是我通常的分类:
尽可能使用矢量:图标字体或 SVG。
UI 元素是精灵图:1x 和 2x 版本(视网膜的媒体查询)。
对于较小的图像,在宽度为 512px 或更小的位置显示,我使用一个单一的流体 img,其中实际文件宽度为 1.5 倍——因此不超过 768px(src 高度和宽度在显示的最大宽度或按比例更大的位置声明)。这是一个在视网膜上看起来相当不错且易于维护的折衷方案。
对于更大的图像,我会开始考虑背景图像和媒体查询:主页上的大型背景图像,在那里看到文本渲染然后等待图像在下面加载会显得笨拙。摄影师或建筑师的网站,他们希望在视网膜 iPad 上炫耀。即使如此,我倾向于将内容保持在 1.5 倍而不是 @2 倍,图像宽度不超过 2048px。
对于这些更大的图像,我认为快速看到 *一些* 内容会更好,让用户可以扫描页面,而不是看到“加载”图形,因此有了“lowsrc”的想法。一旦我决定显示这个移动优先图像(大约 25K),使用多个背景来使过渡更流畅就不会 *增加* 带宽——在获取更大图像并将其加载在其上时,继续显示已经缓存的图像。使用这个“lowsrc”图像作为我创建 div 高度的“代理”也不会增加带宽消耗。
正如 Dave Rupert 指出,92.8% 的“移动”屏幕都为 @1.5 倍或更高,因此在无法轻松考虑带宽的情况下,我们必须决定哪种方法可以提供最佳体验,以及我们愿意付出多少努力才能实现这一目标。我还没有尝试过,但我认为 SASS 混合在此处会很有帮助。
谢谢
数据浪费是一个问题,但时间浪费也是另一个问题。加载多个资源需要更多时间,导致响应速度变慢,并破坏用户体验。
我同意 Alex McCabe 关于增加数据和请求的说法。
虽然这在台式机上看起来不错(从示例来看确实如此),但在数据套餐和移动设备上却非常糟糕。
使用“加载”图像是一个选项——用户期望在那里看到一些东西。这个图像可以设置为每个正在加载图像的容器的缩放背景图像。
我会选择服务器端选项来执行此操作。
不过这是一篇很棒的文章,感谢您。
我认为 LOWSRC 在 50 年代不可用。在那个年代,互联网还没有发明出来。
讽刺 / 挖苦 + 互联网 = 帖子失败
我认为这很棒——绝对是一个朝着有趣方向迈出的步骤。
天啊,响应式图片真是个难题。我通常只遵循 1.5 倍规则(其中 1.5 倍大小远小于 2 倍的文件大小,但 *在视觉上* 非常接近 2 倍(因为视网膜的像素数量超出了人眼在平均观看距离所能分辨的范围)。
显然这对于照片来说不是一个选择,但我一直都在任何机会使用 SVG(例如,在我新业务网站上的徽标)。文件本来就很小,即使在视网膜上全屏显示也能保持完美清晰度。但后来我发现 SVG 中包含了来自 AI 的隐藏图层,我将大小缩减到了原来的三分之一!我导出了 PNG 回退,结果它们的大小是原来的 4 倍(而且我甚至没有导出非常大的版本)。
我对 SVG 支持感到惊讶。基本上,IE8 是你真正需要关注的唯一浏览器。
别忘了在服务器上使用 gzip 压缩。文件大小更小!我也一直都在任何机会使用 SVG 图像。
@ Chris:一个人 *总是* 需要艺术指导 :)
从技术角度来说,我对这种技术感到矛盾。我不喜欢为此方法重复请求次数和额外的数据传输。而且请记住,即使 3G 也并非无处不在。有时我在麦迪逊外面,网络信号简直是 #@^&&$%%$%$#@^&*$^&#!!!
我曾经思考过画廊中低分辨率缩略图的必要性,认为用户最终会看到所有图像,那么为什么要将缩略图添加到页面的重量中呢……事实是,并非所有用户(实际上很少)会在一次坐下来浏览整个画廊。换句话说,缩略图可以实现快速导航,并可能通过链接/.js 实现选择性加载。这是一个相反的情况,如果我们谈论的是背景图像和图形(与文本相关的图像)、头像等。每个用户都会 *浏览* 网站的背景、图形和头像。因此,虽然它确实掩盖了“慢连接效果”,但对于“非可选”内容来说,这似乎是一种昂贵的实现方式。
我必须同意,这似乎更像是浪费时间,而不是创造明显更好的体验。我认为我比普通用户更有耐心,所以如果我有一点点兴趣,我会等待图像加载。但如果真的太久了,我会离开。但它确实是一个值得研究的东西。在这里看到不同的意见!感谢您的阅读。
我不明白在响应式网站中执行此分层操作的意义。您可以使用媒体查询加载所需的尺寸。闪烁只会出现在更改浏览器大小的情况下。普通用户在浏览您的内容时不会这样做。但对于视网膜支持,它可能是一种提前加载图像的解决方案。是的,额外的数据传输当然是一个缺点……
我刚在一个响应式网站上工作,使用了 Scott Jehl 的 polyfill,并回退到移动尺寸的照片,我非常喜欢它。使用 WordPress 处理所有图像调整大小,并编写一个快速函数来处理 html 输出,它非常易于使用。
直到后来我才想到的一件事,我认为这个解决方案也会遇到这个问题,那就是图像搜索。由于标记中唯一提到的内容是移动回退,这意味着 Google 在抓取页面时只能找到这个图像吗?因此,如果我是一个新闻网站,写了一篇关于某件事的文章,并且有一个漂亮的高分辨率图像,它只作为 CSS 文件中的背景存在,那么用户在 Google 上搜索高分辨率图像时,能够找到它吗?
我知道这只是一个非常小的用例,但它在我上一个项目发布后出现,我还没有找到答案。
每当我想到响应式设计,我都会想到 IE8,它会浪费时间,因为要处理不同的数据源。我一直使用最简单、最快的方案,这样我以后可以根据需要升级网站。
目前,响应式环境中的图像似乎非常令人头疼。还没有看到真正有效或长期可持续的“解决方案”。
我认为目前我将坚持使用单个图像,这些图像尽可能地令人满意。
1.5 倍或 2 倍图像很好,但有多少人使用视网膜屏幕?这可能只是整体中的一小部分。
这是一次关于 CSS 技术的有趣旅程,但从性能的角度来看,这真是一个糟糕的想法。不仅因为重复请求,还因为在样式表完全解析并且布局发生之前,不会发出任何图像请求。在现代浏览器中,使用前瞻性预解析器,背景图像加载的时间远比标记中的 img 晚。尝试在慢速连接上使用任何复杂的(30k+)样式表在任何复杂的(80k+)页面上,您会感觉到布局“动画”到视图中的痛苦。这些不是我们正在寻找的机器人。(对于运行 PHP 的服务器,Adaptive Images 仍然是我认为 *最棒* 的东西。)
http://adaptive-images.com/
来自他们的网站:“AI 检查用户代理字符串:如果找到“移动”,它将发送 $resolutions 中定义的最低分辨率,否则它将假设您使用的是大型设备,并提供最高分辨率。”
这就是 Dave Rupert 所要表达的意思:如果 93% 的移动设备都为高 dpi,那么“移动”标识是否还与 1 倍图像相关联?我们能否调整 Adaptive Images 以向视网膜移动屏幕提供中等尺寸、更高分辨率的图像?
很棒的文章和技巧,但我认为它有点过度设计了。
IMHO Unveil.js 是一个非常简单的方法,可以用来懒加载图像并有条件地为具有视网膜显示屏的设备提供高分辨率图像。
<img src="loader.gif" data-src="img.jpg" data-src-retina="img-retina.jpg" />
$(function() { $("img").unveil(); });
我写了这个脚本,所以它可能看起来有点自命不凡,但我在这里放了链接,因为我真的相信它是一个非常好的、简单的解决方案。
luis-almeida.github.io/unveil
这在很多情况下都是一个很棒的解决方案,Luis - 干得漂亮!(在你的 github 页面 上,我注意到你的 noscript 示例两次声明了一个 src:
img src="bg.png" src="img1.jpg
。如果只有这能工作,我就不必费这么大劲来模拟“lowsrc”了!)我用 unveil.js 做了一个 CodePen,但它显示了一个“lowsrc”img 而不是“loading.gif”。如果你不需要使用背景图像(例如,为了裁剪灵活性,或者,你知道,为了背景),它绝对更简单。
然而,我担心的一点是,仅仅根据屏幕密度来触发。如果 screensiz.es 是对的,93% 的移动设备的像素密度都高于或等于 150%,但大多数不需要为它们的小屏幕使用桌面大小的视网膜图像 - 它们可以用 768-864px 获得不错的效果。使用 unveil.js,没有中间地带。
你可以使用条件标签来定位任何移动设备。
http://wpsites.net/web-design/conditional-tags-mobile-devices/
感谢 Brad 强调了 WordPress 中的这一功能。有代码片段或示例说明它是如何工作的吗?对我来说,理想的情况是,向移动视网膜屏幕提供一个中等大小的高分辨率 img src,向其他视网膜屏幕提供最高分辨率的 img src。
或者,我们可以抛弃所有这些图像切换技巧,转而使用一张图像来统治一切。
http://blog.netvlies.nl/design-interactie/retina-revolution/
它不适用于 PNG,而且它不涵盖艺术方向,但除此之外,我发现它运行得很好。我目前正在重新设计一个摄影社区网站(想想就觉得好笑),新版本将使用这种技术。图像文件大小更小,但像素却多了 4 倍。它真的有效。我认为在其他任何事情之前都应该考虑这种技术,因为它避免了很多复杂性。此外,我认为任何需要多个图像的解决方案都有问题,因为网络上的大部分内容都是由可能不支持它的 CMS 提供的。
WordPress 已经根据你的媒体设置生成了 4 种不同大小的图像。
添加更多自定义大小并使用特定的移动条件标签来使用它们并不难。
看起来是个很酷的想法,但我不喜欢在 html 中定义一半的图像,而在 css 中定义另一半。
非常有趣。必须得研究一下。
这里有一个自适应图像的替代解决方案:http://litesite.org/holygrail/stage2/