使用<picture>与srcset实现响应式图片时如何解决布局偏移(Layout Shift)问题?
我一直在尝试复刻一篇关于响应式图片的实用指南,但由于缺失图片高度,遇到了严重的布局偏移(Layout Shift)问题。当前使用的代码如下:
<picture> <source type="image/webp" media="(max-width: 599px)" srcset="{{ asset($assetespath.'/images/bg_crop_small.webp')}} 640w" sizes="100vw"> <source type="image/jpg" media="(max-width: 599px)" srcset="{{ asset($assetespath.'/images/bg_crop_small.jpg')}} 640w" sizes="100vw"> <source type="image/webp" media="(min-width: 600px) and (max-width: 899px)" srcset="{{ asset($assetespath.'/images/bg_crop_large.webp')}} 1200w" sizes="100vw"> <source type="image/jpg" media="(min-width: 600px) and (max-width: 899px)" srcset="{{ asset($assetespath.'/images/bg_crop_large.jpg')}} 1200w" sizes="100vw"> <source type="image/webp" media="(min-width: 900px)" srcset="{{ asset($assetespath.'/images/bg_large.webp 1920w, /assets/images/bg_small.webp')}} 1024w" sizes="100vw"> <source type="image/jpg" media="media (min-width: 900px)" srcset="{{ asset($assetespath.'/images/bg_large.jpg 1920w, /assets/images/bg_small.jpg')}} 1024w" sizes="100vw"> <img src="{{ asset($assetespath.'/images/bg_large.jpg')}}", alt="Exclusive street"> </picture>
请问有谁能提供改进代码的建议,以避免布局偏移?
解决方案:提前为图片预留宽高比空间
布局偏移的本质是浏览器加载图片前无法确定其高度,所以我们需要通过CSS或HTML属性告诉浏览器图片的宽高比例,让它提前预留出对应空间。这里有几个实用的方法:
1. 给 <img> 标签添加原生宽高属性(最简单的现代方案)
现代浏览器会利用 <img> 的 width 和 height 属性计算宽高比,即使你用CSS设置了 width: 100%,浏览器也会自动算出对应的高度并预留空间。
修改你的 <img> 标签,添加实际的宽高值(比如你的大图是1920x1080,就填对应的数值):
<img src="{{ asset($assetespath.'/images/bg_large.jpg')}}" alt="Exclusive street" width="1920" height="1080">
然后加上CSS确保图片自适应:
img { width: 100%; height: auto; }
2. 使用CSS aspect-ratio 属性(现代浏览器优先推荐)
如果你的图片有固定的宽高比(比如16:9、4:3),可以直接用CSS的 aspect-ratio 属性给图片容器设置比例,这样容器会提前占据对应空间,图片加载后直接填充进去。
比如你的图片是16:9比例,给 <picture> 套一个容器:
<div class="responsive-image-container"> <picture> <!-- 你的source标签保持不变 --> <img src="{{ asset($assetespath.'/images/bg_large.jpg')}}" alt="Exclusive street"> </picture> </div>
对应的CSS:
.responsive-image-container { width: 100%; aspect-ratio: 16/9; /* 根据你的实际图片比例调整 */ } .responsive-image-container img { width: 100%; height: 100%; object-fit: cover; /* 可选,保持图片比例填充容器 */ }
3. 传统的 padding-top 占位技巧(兼容旧浏览器)
如果需要兼容不支持 aspect-ratio 的旧浏览器(比如IE),可以用padding-top的百分比技巧,百分比是基于容器宽度计算的,刚好可以用来模拟宽高比。
比如16:9的比例,计算方式是 (9/16)*100 = 56.25%:
<div class="responsive-image-container"> <picture> <!-- 你的source标签保持不变 --> <img src="{{ asset($assetespath.'/images/bg_large.jpg')}}" alt="Exclusive street"> </picture> </div>
CSS:
.responsive-image-container { position: relative; width: 100%; height: 0; padding-top: 56.25%; /* 16:9的比例,根据实际比例调整 */ } .responsive-image-container picture, .responsive-image-container img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; }
另外,注意你代码里的一个小错误:最后两个 <source> 的 media 属性写错了,多了个 media 关键字,修正后应该是:
<source type="image/webp" media="(min-width: 900px)" srcset="{{ asset($assetespath.'/images/bg_large.webp')}} 1920w, {{ asset('/assets/images/bg_small.webp')}} 1024w" sizes="100vw"> <source type="image/jpg" media="(min-width: 900px)" srcset="{{ asset($assetespath.'/images/bg_large.jpg')}} 1920w, {{ asset('/assets/images/bg_small.jpg')}} 1024w" sizes="100vw">
这样修改后,浏览器就能提前预留出图片的空间,彻底解决布局偏移问题啦!
内容的提问来源于stack exchange,提问作者nitro29

