加载背景图片后淡入显示(无需使用jquery),同时仍然使用媒体查询在不同屏幕尺寸下替换图片。

11

我整晚都在阅读,似乎找不到任何关于如何最好地做这件事的具体答案。我知道有两种方法是行得通的:

用于图片加载淡入的方法:

使用图像包装器和<img>标记,如下所示:

<div class="imageWrapper">
    <img src="img.jpg" alt="" onload="imageLoaded(this)">
</div>

并且 CSS 看起来像这样

.imageWrapper {
  opacity: 0
}

.loaded {
  opacity: 1
}

然后在您的js文件中添加类似以下内容:

var imageLoaded = (img) => {
    var imgWrapper = img.parentNode;
    imgWrapper.className += ' loaded';
}

根据屏幕大小加载不同的图像

@media screen only and (min-device-width: 0) and (max-device-width: 450px) {
  .backgroundImage {
    background: url('small_background_image.jpg');
  }
}

@media screen only and (min-device-width: 451px) and (max-device-width: 1024px) {
  .backgroundImage {
    background: url('medium_background_image.jpg');
  }
}

@media screen only and (min-device-width: 1025px) {
  .backgroundImage {
    background: url('large_background_image.jpg');
  }
}

我的问题

我可以单独完成这两件事情 (加载图像时淡入,当浏览器检测到较小的屏幕尺寸时更改背景图片),但我似乎找不到同时包含 这两者 的解决方案。 我需要能够为不同的屏幕尺寸指定不同的图像,并能够检测该图像何时加载并淡入。

有什么好方法吗?

我的解决方案:

最终,我使用了guest271314答案的变化来加载正确的图像。 这适用于所有浏览器的最新版本,而且非常容易实现。

首先,我在开头的 <body> 标记右下方放置一个内联脚本,以便如果浏览器加载我的js文件速度很慢,它仍然可以立即加载图像。

<body>
    <script>
        function imageLoaded(img) {
            if ((" " + img.className + " ").indexOf(" "+'loaded'+" ") > -1 ) {
            return true;
            } else {
                img.className += ' loaded';
            }
        }
    </script>

然后我有一个<picture>标签,就像这样:

<picture class="backgroundContainer">
    <source srcset="img-1024.jpg" media="(min-width: 0px) and (max-width:1024px)">
    <source srcset="img-1920.jpg" media="(min-width: 1025px) and (max-width: 1920px)">
    <source srcset="img-2560.jpg" media="(min-width: 1921px)">
    <img class="backgroundImage" onload="imageLoaded(this)" src="img-1920.jpg" alt="">
</picture>

我的Sass看起来是这样的:

@keyframes fadeIn
  0%
    opacity: 0
  100%
    opacity: 1

.backgroundContainer
  width: 100%
  height: 100%

.backgroundImage
  opacity: 0

.loaded
  opacity: 1
  animation: fadeIn 3s

这使得浏览器(不论速度)能够等待正确大小的图像加载完毕,然后在3秒内淡入显示。对于旧版浏览器,它会使用<img> 标签提供一个安全的备用方案。


我可以单独完成这两件事,但似乎找不到一个同时包含它们的解决方案。你遇到了哪个部分的问题? - guest271314
我编辑了我的原始帖子以进行澄清。 - shan
请查看针对“编辑#2”描述的问题的新答案。 - guest271314
4个回答

4
我需要能够在加载背景图像时淡入,或者根据屏幕大小在加载之前更改标记的src,或者使用我不知道的方法结合这两个事情做些事情。 您可以使用css content属性,将值设置为url()函数及图像URL以显示图像,或更改所显示的图像源;css动画、@keyframes可用于在源图像加载时淡入背景图像。
<picture class="backgroundImage"></picture>

css

.backgroundImage {
  opacity: 0; /* set `opacity` to `0` */
  width: 50px;
  width: 50px;
  content: url(http://lorempixel.com/50/50/technics); /* set intial image */
  animation-name: fadein;
  animation-iteration-count: 1;
  animation-duration: 2500ms;
  animation-fill-mode: both;
}

@keyframes fadein {
  to {
    opacity: 1; /* fade in image */
  }
}
/* set media queries */
@media screen only and (min-device-width: 0) and (max-device-width: 450px) {
  .backgroundImage {
    opacity: 0; /* set `opacity` to `0` */
    content: url(http://lorempixel.com/50/50/cats); /* set image */
    animation-name: first;
  }
  @keyframes first {
    to {
      opacity: 1; /* fade in image */
    }
  }
}

@media screen only and (min-device-width: 451px) and (max-device-width: 1024px) {
  .backgroundImage {
    opacity: 0; /* set `opacity` to `0` */
    content: url(http://lorempixel.com/50/50/city); /* set image */
    animation-name: second;
  }
  @keyframes second {
    to {
      opacity: 1; /* fade in image */
    }
  }
}

@media screen only and (min-device-width: 1025px) {
  .backgroundImage {
    opacity: 0; /* set `opacity` to `0` */
    content: url(http://lorempixel.com/50/50/sports); /* set image */
    animation-name: third;
  }
  @keyframes third {
    to {
      opacity: 1; /* fade in image */
    }
  }
}

jsfiddle https://jsfiddle.net/yayd5Lma/3


嗯,你能加上一个简短的例子吗?我很难确定如何准确地构建它。 - shan
1
@shanling 包含在 css 中的注释描述了方法。您可以调整 jsfiddle 中的结果窗口以查看效果。 - guest271314
哦,很有趣。你能否对“content”属性中设置的图片进行样式化呢?它是动态加载图片吗?我正在使用Chrome DevTools检查网络标签,似乎只有在屏幕变化以适应不同的媒体查询时才会获取这些图片。使用CSS“content”属性通常会具备这种功能吗? - shan
1
是的,可以为<picture>元素设置样式。是的,只有在媒体查询条件为真时才会请求图像,然后如果缓存未禁用,则应缓存该图像。请参见content<picture>是否可能在CSS内容中显示.html文档或.html片段? - guest271314
这个像魔法一样奏效。一旦加载完毕,淡入效果就像应该的那样只会加载正确的图像。从未听说过可以像这样使用content属性,非常感谢! - shan
显示剩余7条评论

1
编辑#2:目前进展的更新fiddle: https://jsfiddle.net/c5hy0g8r/11 问题仍然是在网络条件较差的情况下,使用CSS“content”属性仍将像图像一样分块加载。 onload =“imageLoaded(this)”的常规方法将无法在其内容由CSS content生成的img或picture上工作:url('img.jpg')属性。
您可以使用< picture >元素,其中包含一个或多个< source >元素,其srcset属性设置为应在< img >中显示的图像源,当相应的{{link2:media}}属性设置为有效的媒体查询列表与环境匹配时。

<img>元素的onload事件中,将父元素的opacity设置为0;使用requestAnimationFrame()动画显示元素的opacity01

<!DOCTYPE html>
<html>

<head>
  <style>
    .imageWrapper {
      opacity: 0;
    }
  </style>
</head>

<body>

  <picture class="imageWrapper">
    <source srcset="http://lorempixel.com/50/50/technics" media="(min-width: 0px) and (max-width:150px)">
    <source srcset="http://lorempixel.com/50/50/city" media="(min-width: 151px) and (max-width: 300px)">
    <source srcset="http://lorempixel.com/50/50/nature" media="(min-width: 301px) and (max-width:450px)">
    <source srcset="http://lorempixel.com/50/50/sports" media="(min-width: 451px) and (max-width:600px)">
    <source srcset="http://lorempixel.com/50/50/animals" media="(min-width: 601px)">
    <img width="50px" height="50px" onload="imageLoaded(this)" src="null" alt="">
  </picture>
  <figcaption></figcaption>
    <script>
    const num = 1/60;
    var fx = null;
    var caption = document.querySelector("figcaption");
    var imageLoaded = (img) => {
      caption.innerHTML = "";
      cancelAnimationFrame(fx);
      var n = 0;
      var imgWrapper = img.parentNode;
      imgWrapper.style.opacity = 0;
      fx = requestAnimationFrame(function animate() {
        console.log(n);
        if (n < 1) {
          n += num;
          imgWrapper.style.opacity = n;
          fx = requestAnimationFrame(animate);
        } else {
          caption.innerHTML = img.currentSrc.slice(img.currentSrc.lastIndexOf("/") + 1);
          console.log("complete");
        }
      })
    }
  </script>
</body>
</html>

plnkr http://plnkr.co/edit/RWSc6J3kUUJPUZsrpagi?p=preview

plnkr http://plnkr.co/edit/RWSc6J3kUUJPUZsrpagi?p=preview


我最终使用了<picture>标签,一切都按预期工作。我有一个稍微简单的方法,我将在我的原始帖子中进行编辑。谢谢! - shan

0
你应该使用CSS3动画来实现这个目的。
@-webkit-keyframes fadeIn {
            0% {opacity: 0;}
            100% {opacity: 1;}
         }

         @keyframes fadeIn {
            0% {opacity: 0;}
            100% {opacity: 1;}
         }

         .fadeIn {
            -webkit-animation-name: fadeIn;
            animation-name: fadeIn;
         }

是的,我可以用CSS3动画使任何东西淡入,但是我需要在图像完全加载后才能淡入,这就是我的问题。 - shan

0

我会在我的标记中加载所有图像,并为媒体查询分配类:

<div class="imageWrapper">
    <img class="small" src="img_small.jpg" onload="imageLoaded(this)">
    <img class="med" src="img_med.jpg" onload="imageLoaded(this)">
    <img class="large" src="img_large.jpg" onload="imageLoaded(this)">
</div>

然后使用媒体查询根据需要隐藏它们:

@media screen only and (min-device-width: 0) and (max-device-width: 450px) {
  .imageWrapper .large, .imageWrapper .medium { display: none; }
}

@media screen only and (min-device-width: 451px) and (max-device-width: 1024px) {
  .imageWrapper .large, .imageWrapper .small { display: none; }
}

@media screen only and (min-device-width: 1025px) {
  .imageWrapper .small, .imageWrapper .medium { display: none; }
}

好的,这将加载所有图像,而不仅仅是屏幕大小所需的图像,并且如果我没有弄错的话,这将打败媒体查询的初衷。 - shan
这会如何消除对媒体查询的需求? - But those new buttons though..
我的问题涉及仅加载正确的图像。这将加载所有三个图像,然后隐藏除正确图像外的所有内容,从而为我的网站添加大量加载时间。使用媒体查询,您可以让浏览器根据屏幕大小决定要加载哪个图像,并且它只会加载该特定图像,绕过其他两个图像。您提供的这个媒体查询不会阻止其他两个图像的加载。 - shan
这是正确的,但它会根据屏幕大小显示正确的图像,并且还会淡入淡出,就像你想要的那样。我想这确实取决于你正在使用的图像大小,但加载2个额外的图像可能并不是什么大问题。无论如何,这回答了你的问题 - 也许不是你想要的,但它应该能够满足你的需求。 - But those new buttons though..

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