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

时间:2022-11-03 22:40:41

I'm using a CSS grid system which is based upon percentages. I have a grid with 4 columns, each 25% of the page's total width. I output my image tags inside of each "25% cell" like this:

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

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

As the browser resizes, the images also resize to fill in 100% of each 25% cell. The browser picks a height, as if I had put "height:auto" (which is implicit when omitted).

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

Now I want to add lazy loading capability to this. The problem is that before the images are loaded, their height on the page is unknown. The browser has to download the image & observe its aspect ratio, and calculate a height for it. Prior to this, all the images have a height of 1px. Since every image has a height of 1px, they are all considered as "within the viewport" and are immediately loaded.

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

Currently I have a proof of concept where prior to outputting the img tag, I calculate the images aspect ratio on the server, and output in a data attribute:

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

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

Then, upon the event "document ready", I loop through every image and set a fixed 'height' value in pixels prior to lazy loading:

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

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

This seems to be working, in the sense that it no longer loads all the images at the same time, but only loads images as I scroll.

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

However, it seems like it could cause new problems, like the images becoming stretched as the user resizes the browser. I would have to switch the 'height' back to 'auto' when a callback fires for lazy loading having completed. That would take care of images the user sees - but the images below the fold would still have an improper 'height' value upon the browser being resized. Every time the browser is resized, I would have to iterate all images that were previously below the fold, measure their updated width, read their aspect ratio, and update the new height, and then retrigger lazy loading to handle anything that is now above the fold. If I don't do this, loading could be triggered too early or too late due to those images having the wrong height value.

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

My question is, is there any other ways to lazy load images with unknown heights, other than the exact method I've described here, and what ramifications would this have? Is there any downside to my method, other than it being a pain to program?

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

3 个解决方案

#1


5  

I had a similar problem recently, combining Isotope with Lazy Load in a responsive layout. Isotope determines the layout based upon the width and height of the images when the page is loaded, so initially, the items were all overlapping because Isotope wasn't calculating the correct size.

我最近遇到了类似的问题,将Isotope与Lazy Load结合在一个响应式布局中。当页面加载时,Isotope根据图像的宽度和高度确定布局,因此最初,项目都是重叠的,因为Isotope没有计算正确的大小。

To make sure the placeholder items were saving the space, I used the padding-bottom trick you mentioned: http://thisisthat.co.uk/notebook/2013-10-07-lazy-loading-responsive-images (Though it may have not been that exact post.) Here's my markup:

为了确保占位符项保存空间,我使用了你提到的填充底部技巧: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" width="1000" height="662">
            </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>

That's probably more involved than you need (as the entire .portfolio-text div is an overlay which has quite a bit of styling going on). The real key was just in calculating the bottom padding based upon the width and height of the image (which I did in the template with PHP) and setting that as the padding-bottom of the item that I wanted to save the space.

这可能比你需要的更多(因为整个.portfolio-text div是一个叠加,它有相当多的样式)。真正的关键在于根据图像的宽度和高度计算底部填充(我在PHP模板中做了)并将其设置为我想要保存空间的项目的填充底部。

#2


4  

Even cleaner:

  1. Set height and width of images to an arbitrarily-large number (like 2000px x 1000px)
  2. 将图像的高度和宽度设置为任意大的数字(如2000px x 1000px)

  3. Apply the following CSS to each of the desired images (perhaps via a shared class):
    max-width: 100% and height: auto
  4. 将以下CSS应用于每个所需图像(可能通过共享类):max-width:100%和height:auto

  5. Smile wide :)
  6. 微笑宽:)

Credit for this approach goes to Github user dryabove, given in this Github issue

在Github问题中给出了Github用户干预的信用

#3


2  

Here's the more elegant solution, from the comments. It still requires writing the aspect ratio server side, but with a wrapper div:

这是评论中更优雅的解决方案。它仍然需要编写纵横比服务器端,但使用包装器div:

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

Then with JS I give the wrapper div a padding-bottom:

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

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

This gives the div the exact dimensions that the img will eventually consume. I then use the following LESS to load the image within the area the padding consumes:

这给div提供了img最终消耗的确切尺寸。然后我使用以下LESS在填充消耗的区域内加载图像:

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

#1


5  

I had a similar problem recently, combining Isotope with Lazy Load in a responsive layout. Isotope determines the layout based upon the width and height of the images when the page is loaded, so initially, the items were all overlapping because Isotope wasn't calculating the correct size.

我最近遇到了类似的问题,将Isotope与Lazy Load结合在一个响应式布局中。当页面加载时,Isotope根据图像的宽度和高度确定布局,因此最初,项目都是重叠的,因为Isotope没有计算正确的大小。

To make sure the placeholder items were saving the space, I used the padding-bottom trick you mentioned: http://thisisthat.co.uk/notebook/2013-10-07-lazy-loading-responsive-images (Though it may have not been that exact post.) Here's my markup:

为了确保占位符项保存空间,我使用了你提到的填充底部技巧: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" width="1000" height="662">
            </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>

That's probably more involved than you need (as the entire .portfolio-text div is an overlay which has quite a bit of styling going on). The real key was just in calculating the bottom padding based upon the width and height of the image (which I did in the template with PHP) and setting that as the padding-bottom of the item that I wanted to save the space.

这可能比你需要的更多(因为整个.portfolio-text div是一个叠加,它有相当多的样式)。真正的关键在于根据图像的宽度和高度计算底部填充(我在PHP模板中做了)并将其设置为我想要保存空间的项目的填充底部。

#2


4  

Even cleaner:

  1. Set height and width of images to an arbitrarily-large number (like 2000px x 1000px)
  2. 将图像的高度和宽度设置为任意大的数字(如2000px x 1000px)

  3. Apply the following CSS to each of the desired images (perhaps via a shared class):
    max-width: 100% and height: auto
  4. 将以下CSS应用于每个所需图像(可能通过共享类):max-width:100%和height:auto

  5. Smile wide :)
  6. 微笑宽:)

Credit for this approach goes to Github user dryabove, given in this Github issue

在Github问题中给出了Github用户干预的信用

#3


2  

Here's the more elegant solution, from the comments. It still requires writing the aspect ratio server side, but with a wrapper div:

这是评论中更优雅的解决方案。它仍然需要编写纵横比服务器端,但使用包装器div:

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

Then with JS I give the wrapper div a padding-bottom:

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

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

This gives the div the exact dimensions that the img will eventually consume. I then use the following LESS to load the image within the area the padding consumes:

这给div提供了img最终消耗的确切尺寸。然后我使用以下LESS在填充消耗的区域内加载图像:

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