CSS背景图片是否有类似于srcset的等效属性?

133

img 标签使用 srcset 属性是实现响应式图片的好方法。是否有等效的语法可以在 CSS 的 background-image 属性中使用?

HTML

<img src="small.jpg" srcset="medium.jpg 1000w, large.jpg 2000w" alt="yah">

CSS

.mycontainer {
    background: url('what goes here?');
}
10个回答

106

image-set 是CSS的一个功能,表示根据不同的设备像素比选择不同的图像。我们应该在规范中添加类似的功能(根据资源的尺寸定义资源)。

目前所有主流浏览器(Firefox,Chrome,Safari,Edge)都已经实现,但需要使用 -webkit- 前缀。Safari仅支持 x 描述符。


1
有没有已知的优雅回退或支持此功能的polyfill? - Rachel Cantor
5
需要回退方案吗?毕竟只有现代设备才有视网膜屏幕,我认为是这样的。 - James Kemp
8
仅支持“x”描述符似乎毫无意义,我的意思是,在2x“视网膜”桌面上(5120w),背景图像可以是全宽度的,在移动设备上以2x(720w)查看时,这是任何移动设备都难以考虑作为网页背景的荒谬大小。即使使用max-width媒体查询提供几个不同的尺寸也没有太大帮助,因为它并不是背景容器的max-width,而是屏幕的max-width。 - Chris Seufert
2
如果浏览器不支持image-set,那么应该使用什么备选方案? - O.MeeKoh
1
根据文档,我们必须在image-set()调用的那一行之前指定background-image: url("");。@O.MeeKoh - quinqui

33

另一个更为可靠的方法是,将背景图像的特性和选项适应具有srcset属性的图像。

要实现这一点,将图像设置为宽度:100%;高度:100%;并且object-fit:cover或contain。

以下是示例:

.pseudo-background-img-container {
  position: relative;
  width:400px;
  height: 200px;
}
.pseudo-background-img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
<div class="pseudo-background-img-container">

<img class="pseudo-background-img" src="https://cdn3.tnwcdn.com/wp-content/blogs.dir/1/files/2016/12/Keep.jpg" srcset="https://cdn0.tnwcdn.com/wp-content/blogs.dir/1/files/2016/12/Keep.jpg 640w, https://cdn0.tnwcdn.com/wp-content/blogs.dir/1/files/2016/12/Keep-280x175.jpg 280w, https://cdn0.tnwcdn.com/wp-content/blogs.dir/1/files/2016/12/Keep-432x270.jpg 432w, https://cdn0.tnwcdn.com/wp-content/blogs.dir/1/files/2016/12/Keep-216x135.jpg 216w" sizes="(max-width: 640px) 100vw, 640px">

</div>

这可能不适合所有人,但我想它将在没有任何JavaScript解决方法的情况下获得大多数所需的结果。


1
src-set不考虑图像宽度,只考虑object-fit: cover的容器宽度。这意味着,如果您的img标签最终宽度为50px但高度为1000px,则加载满足50px宽度的图像并被拉伸以适应,可能从240w缩放到1500px宽度,仅显示50px切片。 - Chris Seufert
1
我不知道 object-fit: cover; 的存在,谢谢。对我来说,这是使我的代码工作的最后一步(我正在使用 <picture> 标签)。 - Gendrith
我认为这并不是 OP 想要的答案,但确实是一个完全可行的解决方案。一开始我也会推荐同样的方法。 - kissu

27

我很确定:

background: -webkit-image-set( url('path/to/image') 1x, url('path/to/high-res-image') 2x );

这个过程是一样的。浏览器会检查图片,选择最适合的并使用它。


2
在2018年,这仍然不被所有浏览器支持。https://caniuse.com/#search=image-set - BadHorsie
3
2021年仍然没有完全支持…… - Rick Kukiela
3
@HashimAziz - 如果你只使用了密度声明,并且接受使用前缀(如果你使用自动前缀生成器的话,这不是什么大问题),那么可以。我的问题在于,“density”选项远不如使用带有“sizes”属性的###w声明好,后者更适合定义浏览器何时以及在哪里使用哪个图像。 - Rick Kukiela

12

您可以使用媒体查询来实现您的目的。它很简单,只需这样:

.mycontainer {    
    background-image:url("img/image-big.jpg"); // big image   
}

@media(max-width: 768px){
   .mycontainer {
        background-image:url("img/image-sm.jpg"); // small image  
    }
}

我认为它可以在支持媒体查询的每个浏览器上运行;)


2
如果您的图像设置为覆盖容器,则不考虑将图像拉伸以适应高度,该框可能是本机图像宽度的2或3倍,因此在质量上看起来非常差。 - Chris Seufert
媒体查询并不是同一件事情。 - Rick Kukiela
1
进一步阐述我的上一个评论。如果您可以只使用媒体查询来完成所有这些操作,那么甚至没有理由使用srcset或picture元素。当然,您可以交换背景图像,但是srcset和picture允许您指定各种图像格式,并根据媒体查询提供容器宽度上下文(例如,>1024是140rem的侧边栏,在此之下是全宽),并且可以与1x / 2x密度结合使用,以让浏览器为上下文选择最佳图像。只使用媒体查询无法实现这一点。 - Rick Kukiela

11

如果需要一个polyfill,您可以使用具有srcset属性的img作为下载正确图片尺寸的机制,然后使用JS隐藏它并设置其父元素的background-image。

这里有一个例子: http://jsbin.com/garetikubu/edit?html,output

使用onload和将JS作为阻止脚本放置在<head>中非常重要。如果您将脚本放置得太晚(例如放在<body>的末尾),则可能会出现竞争条件,其中img.currentSrc尚未由浏览器设置。最好等待它被加载。

该示例允许您查看原始img的下载过程。您可以使用一些CSS轻松隐藏它。


有趣。我本来以为这样做会导致图像加载两次。然而,似乎并没有。 - Perry

5

根据CSSTricks的说法,从2021年开始应该使用image-set()来实现这个目的。

以下是它推荐的支持所有现代浏览器版本的完整代码片段:

.hero {
  /* Fallback */
  background-image: url("platypus.png");

  /* Chrome/Edge/Opera/Samsung, Safari will fallback to this as well */
  background-image: -webkit-image-set(url("platypus.png") 1x, url("platypus-2x.png") 2x);

  /* Standard use */
  background-image: image-set(url("platypus.png") 1x, url("platypus-2x.png") 2x);
}

这只对不同的屏幕分辨率有用,而不适用于不同的视口宽度。 - undefined

4

使用<picture>元素的类似解决方案:这里是教程

教程示例:

我有疑问,如果我在较小的屏幕上使用同一张图片,我的图片的主要内容可能会变得太小。我想在不同的屏幕尺寸下显示不同的图片(更加聚焦于主要内容),但我仍然想根据设备像素比显示相同图像的不同资源,并且我想根据视口自定义图像的高度和宽度。

示例代码:

<picture>

<source media="(max-width: 20em)" srcset="images/small/space-needle.jpg 1x,
images/small/space-needle-2x.jpg 2x, images/small/space-needle-hd.jpg 3x">

<source media="(max-width: 40em)" srcset="images/medium/space-needle.jpg 1x,
images/medium/space-needle-2x.jpg 2x, images/medium/space-needle-hd.jpg 3x">

<img src="space-needle.jpg" alt="Space Needle">

</picture>

将'<picture>'元素定位为'absolute'或'fixed'。 如果需要,为其容器设置正确的z-index和position:relative。 - Piotr Ścigała
gdaniel: 准备好的JS解决方案:https://github.com/code-mattclaffey/picture-element-background-attachment 我刚刚测试了一下,它运行得非常好! - Piotr Ścigała
JS解决方案,通过<picture>元素加载图像,并将其显示为background-image:https://github.com/code-mattclaffey/picture-element-background-attachment - Piotr Ścigała

3

谢谢你的帮助,Martti。我已经将它与Webpack的responsive-loader很好地连接起来,得到了一个非常棒的解决方案。 - fredrivett

1

我曾经使用过Lazysizes插件中的bg-set功能来为背景图片添加响应式。它与srcset类似,根据设备宽度提供一组多个背景图片。

<div class="lazyload" data-bgset="image-200.jpg 200w, image-300.jpg 300w, image-400.jpg 400w" data-sizes="auto">

数据背景集中的图像是相同的,我们只提供指定宽度的图像,让浏览器可以选择使用。


1

根据@Weston的答案,我建立了一个更通用的jQuery解决方案,你可以基本上只需复制并粘贴JS和CSS,并专注于HTML部分 ;)

TL;DR - fiddle: https://jsfiddle.net/dpaa7oz6/

CSS

...以确保在加载时图像几乎不可见

.srcSet{
  position: fixed;
  z-index: 0;
  z-index: -1;
  z-index: -100;
  /* you could probably also add visibility: hidden; */
}

JS / jQuery

此脚本将遍历所有具有srcSet类的图像,并绑定load事件,该事件将以currentSrc(如果不支持,则为src)并将其作为background-image CSS放置在最接近的父元素中,该父元素具有bgFromSrcSet类。

但这还不够!因此,它还在window load事件上放置了一个间隔检查器,以测试是否已完成加载事件,如果没有,则触发它们。(img load事件通常仅在第一次加载时触发,在后续加载中,图像源可以从缓存中检索,导致img load事件被触发!

jQuery(function($){ //ON DOCUMENT READY
  var $window = $(window); //prepare window as jQuery object

  var $srcSets = $('.srcSet'); //get all images with srcSet clas
  $srcSets.each(function(){ //for each .srcSet do...
    var $currImg = $(this); //prepare current srcSet as jQuery object

    $currImg
      .load(function(){ //bind the load event
          var img = $currImg.get(0); //retrieve DOM element from $currImg

          //test currentSrc support, if not supported, use the old src
          var src = img.currentSrc ? img.currentSrc : img.src;

          //To the closest parent with bgFromSrcSet class,
          //set the final src as a background-image CSS
          $currImg.closest('.bgFromSrcSet').css('background-image', "url('"+src+"')");

          //remove processed image from the jQuery set
          //(to update $srcSets.length that is checked in the loadChecker)
          $srcSets = $srcSets.not($currImg);

          $currImg.remove(); //remove the <img ...> itself 
      })
    ;      

  });

  //window's load event is called even on following loads...
  $window.load(function(){    
    //prepare the checker
    var loadChecker = setInterval(function(){
        if( $srcSets.length > 0 ) //if there is still work to do...
            $srcSets.load(); //...do it!
        else
            clearInterval(loadChecker); //if there is nothing to do - stop the checker
    }, 150);  
  });

});

HTML

...可能看起来像这样:

<div class="bgFromSrcSet">
  <img class="srcSet"
       alt=""
       src="http://example.com/something.jpeg" 
       srcset="http://example.com/something.jpeg 5760w, http://example.com/something-300x200.jpeg 300w, http://example.com/something-768x512.jpeg 768w, http://example.com/something-1024x683.jpeg 1024w, http://example.com/something-1000x667.jpeg 1000w"
       sizes="(max-width: 2000px) 100vw, 2000px"
  >
  Something else...
</div>

注意:bgFromSrcSet 不应该被设置在 img 元素本身上!它只能被设置在 img DOM 父级树中的元素上。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接