延迟加载“响应式”图像(未知高度)

Posted

技术标签:

【中文标题】延迟加载“响应式”图像(未知高度)【英文标题】:Lazy loading with "responsive" images (unknown height) 【发布时间】:2014-06-18 11:21:36 【问题描述】:

我正在使用基于百分比的 CSS 网格系统。我有一个有 4 列的网格,每列占页面总宽度的 25%。我在每个“25% 单元格”中输出我的图像标签,如下所示:

<img src="foo.jpg" style="max-width:100%" />

随着浏览器调整大小,图像也会调整大小以填充每个 25% 单元格的 100%。浏览器选择一个高度,就好像我放了“height:auto”(省略时是隐含的)。

现在我想为此添加延迟加载功能。问题是在加载图像之前,它们在页面上的高度是未知的。浏览器必须下载图像并观察其纵横比,并为其计算高度。在此之前,所有图像的高度均为 1px。由于每张图片的高度为 1px,因此它们都被视为“在视口内”并立即加载。

目前我有一个概念证明,在输出 img 标签之前,我在服务器上计算图像的纵横比,并在数据属性中输出:

<img src="foo.jpg" style="max-width:100%" data-aspect="1.7742" />

然后,在“文档准备就绪”事件发生时,我循环遍历每个图像并在延迟加载之前设置一个固定的“高度”值(以像素为单位):

$('img').each(function() 
        var img = $(this);
        var width = img.width();
        var ratio = img.data('aspectratio');
        var height = width / ratio;
        $(this).css('height', height+'px');
    );

这似乎有效,因为它不再同时加载所有图像,而是仅在我滚动时加载图像。

但是,它似乎可能会导致新的问题,例如当用户调整浏览器大小时图像会被拉伸。当延迟加载完成后触发回调时,我必须将“高度”切换回“自动”。这将处理用户看到的图像 - 但是在调整浏览器大小时,折叠下方的图像仍然具有不正确的“高度”值。每次调整浏览器的大小时,我都必须迭代所有以前在折叠之下的图像,测量它们的更新宽度,读取它们的纵横比,并更新新的高度,然后重新触发延迟加载以处理现在高于折叠的任何图像折叠。如果我不这样做,由于这些图像的高度值错误,加载可能会过早或过晚触发。

我的问题是,除了我在这里描述的确切方法之外,还有其他方法可以延迟加载未知高度的图像,这会产生什么后果?除了编程很痛苦之外,我的方法有什么缺点吗?

【问题讨论】:

看起来你已经成功了。浏览器只有两种方法可以知道图像的尺寸:下载它,或者在 html 中指定它。 感谢您的保证。我还设想了某种方法,它会延迟加载前 4 个图像,然后阻塞直到它们完成,然后重新评估剩余图像的新位置。唯一的问题是阻塞会比没有延迟加载更慢。 好的,我刚刚意识到填充底部的技巧:thisisthat.co.uk/notebook/…andmag.se/2012/10/… 写下来作为你自己问题的答案。 你能把计算比例的代码放在服务器上吗?这是我找到的解决这个问题的最佳解决方案,但找不到如何做到这一点 【参考方案1】:

如果您有带有 heightwidth 道具(WordPress 的默认值)的图像,并在 src 中加载了 1x1px gif - 默认情况下在某些插件中(看着你 - wp-smush)然后只需插入这个小在你的脚本中 docready 事件上的野兽,它会在延迟加载时自动修复令人讨厌的图像垂直跳跃,我知道这是旧帖子,但这是我相信现代 js 解决方案:

$('.lazyload').each(function(i,j)
    var h = $(j).attr( 'height' );
    var w = $(j).attr( 'width' );
    var at = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 $w $h'%3E%3C/svg%3E`;
    $(j).attr('src', at);
);

【讨论】:

【参考方案2】:

更干净:

    将图像的高度和宽度设置为任意大的数字(如2000px x 1000px) 将以下 CSS 应用于每个所需的图像(可能通过共享类):max-width: 100%height: auto 微笑:)

这种方法的功劳归于 Github 用户dryabove,在this Github issue中给出

【讨论】:

如果你想要一个占位符图像,例如带有微调器的灰色背景(或者您希望您的设计保持正确的结构而不可见),并且图像尚未加载,height: auto 将不起作用。没有图片信息可以计算出正确的高宽比... 我在 Chrome 57 上运气不佳。图像垂直重叠无法预测。 如果图片比例不一样,这将不起作用【参考方案3】:

这是来自 cmets 的更优雅的解决方案。它仍然需要编写纵横比服务器端,但带有包装器div

<div class="lazy"><img src="foo.jpg" style="max-width:100%" data-aspect="0.75" /></div>

然后用 JS 我给包装器 div 一个 padding-bottom:

$('div.lazy').livequery(function() 
    var c = $(this);
    var r = c.data('ar');
    c.css('padding-bottom', r * 100 + '%');
);

这为div 提供了img 最终将消耗的确切尺寸。然后,我使用以下 LESS 将图像加载到 padding 消耗的区域内:

div.lazy 
  max-width:100%;
  position:relative;
  img 
    position:absolute;
    width:100%;
    height:100%;
  

【讨论】:

【参考方案4】:

我最近遇到了类似的问题,将Isotope 与Lazy Load 组合在响应式布局中。 Isotope 在页面加载时根据图像的宽度和高度确定布局,因此最初,由于 Isotope 计算的尺寸不正确,所有项目都是重叠的。

为了确保占位符项目节省空间,我使用了您提到的 padding-bottom 技巧:http://thisisthat.co.uk/notebook/2013-10-07-lazy-loading-responsive-images(虽然它可能不是那个确切的帖子。)这是我的标记:

    <article class="portfolio-item">
        <a class="portfolio-link" href="img/gallery/thumb90.jpg" style="padding-bottom: 66.2%">
            <div class="portfolio-image-wrapper">
                <img class="portfolio-image" data-original="img/gallery/thumb90.jpg"  >
            </div>
            <div class="portfolio-text">
                <h1 class="portfolio-item-name">
                    <span href="#" class="icon" data-icon="e"></span>
                    <span class="portfolio-item-tags">Bridals</span>
                </h1>
            </div>
        </a>
    </article>

这可能比您需要的更多(因为整个 .portfolio-text div 是一个叠加层,其中包含相当多的样式)。真正的关键是根据图像的宽度和高度计算底部填充(我在模板中使用 php)并将其设置为我想要节省空间的项目的填充底部。

【讨论】:

我们实际上在某些页面上也使用了同位素!当图像仍在加载时,我每 1000 毫秒触发一次间隔,告诉同位素重绘。这样,图像在加载时会不断移动,而不是加载到 1 个大列中,然后在最后排列。您是否也这样做,您能否详细说明将它们“粘合”在一起的 JS 如何为您的项目工作?

以上是关于延迟加载“响应式”图像(未知高度)的主要内容,如果未能解决你的问题,请参考以下文章

延迟加载功能模块中未知的 Angular http 客户端拦截器

延迟加载

在 iPhone 中延迟加载图像的最佳方式是啥?

带有语义标记的延迟图像加载

使用 LazySizes 延迟加载轮播图像

使用 JavaScript 延迟加载图像的工作原理是啥?