响应式图片:当图像渲染大小与屏幕宽度的比例未知时

3
我有一个CMS(TYPO3),目前输出普通的<img>标签。为了使其响应式,我想添加不同大小版本的图像的srcset属性。
这就带来了问题:使用sizes属性,我可以告诉浏览器在选择图像版本时要假定什么渲染尺寸。我可以绝对地给出渲染尺寸(以pxem等形式)或作为视口宽度的百分比(以vw形式)。但是我不能告诉浏览器假定图像将具有其父元素的宽度(以%形式)。
如果我事先知道父元素的宽度,我可以将其添加到sizes属性中,如果需要,在父元素大小由于响应式布局(如Bootstrap列)而更改时,可以加入一些媒体条件。
可惜,我正在扩展一个CMS,无法控制布局或内容,例如:
- 页面容器宽度 - 列数 - 设计断点 - 列填充和间距宽度 - 任何列嵌套在列内 - 任何其他填充
收集所有这些信息 - 仅仅为了找出图像父级有多大 - 几乎是不可能的。
有没有想法,如何获得响应式图像,即使我在服务器端不知道最终图像渲染宽度(父宽度)(无论是以px还是vw为单位)也能提供理想大小?
尽管我更喜欢静态解决方案,但我也愿意接受基于JavaScript的想法,只要它们具有良好的回退机制。 编辑:我的问题归结为:是否有一些机制可以替换sizes="100%",而这并不是标准的"为避免混淆,它将相对于什么"编辑: 我的当前解决方案是基于JS的:
// this, plus some code to prevent image loading before this is executed
$('img[srcset]').each(function() {
  $(this).attr('sizes', $(this).parent().width() + 'px');
})

我希望不必依赖JS来完成这个任务。

窗口大小始终由浏览器知道。您无法获得静态解决方案,响应式Web开发涉及动态(未知和变量)。使用百分比表达图像的宽度,并使用“auto”来保持高宽比。 - Rafael
@Rafael 这不是关于 CSS 样式(如 width:100%)的问题,而且我所说的“静态”只是指“没有 JavaScript”。我想要得到一个适合的图像版本(不太大也不太小)。但是,只有当我知道我的图像将有多大,无论是以 px 还是 vw 为单位时,才能使用 sizes - Josta
不,你错了,这与样式有关。先生,pxvw单位还在哪些地方发挥作用呢? - Rafael
我的建议是忘记静态生成多个尺寸的媒体,以及仅使用CSS的解决方案。花点时间设置您的媒体服务器,使其能够返回动态调整大小的图像,并具有适当的缓存等功能,并使用JS解决方案,该解决方案将请求由设备像素比缩放的父级的确切宽度的图像。例如,可以使用以下内容:https://github.com/ericuldall/nginx-dynamic-resize 还有一些提供此服务的服务,但其中一些需要后端支持(签名URL等)。 - user234932
如果您在nginx后面服务 TYPO3 CMS,则可以实现此目标。它不需要PHP,因为可以使用nginx扩展和一些客户端代码来完成。基本思路是使用一些较低或中分辨率的图像(不需要JS也不会显得太糟糕),然后启动一些JS来微调结果。这当然不是一个轻量级的解决方案,但如果网站主要以媒体为导向,那么这是值得投资的,并且随着时间的推移可以大大减少总载荷。 - user234932
显示剩余4条评论
1个回答

2
两个选项:

使用srcset

您的使用情况正是srcset设计的目的。 <source>图像大小事先已知;容器大小在运行时已知;您可以像CSS @media查询一样定义像素范围,以告诉浏览器为给定的容器大小使用哪个<source>

媒体查询相对于视口而不是容器(与CSS媒体查询一样),因此您需要进行一些计算,以确定要针对相对于该视口的容器预期大小使用哪个图像。 (我认为这种复杂性的原因是您还可以根据屏幕密度设置图像,但请不要引用我。)

这个想法是在外部相对于容器设置图像大小,然后使用srcset选择适合该容器的预期大小的图像,考虑到该视口大小。

由于StackOverflow的代码片段将示例绘制在固定尺寸的框架内,因此这使得这个问题很难看到。你只能看到当你调整div大小时图像源不会改变,可能并不是最令人信服的演示。如果要尝试视口调整,您需要将代码复制到一个新窗口中。

.container {
  border: 1px solid;
  width: 50%;
  height: 150px;
  resize: horizontal;
  overflow:auto;
}
<div class="container" resizable>
  <picture>
    <source media="(max-width: 100px)" srcset="http://via.placeholder.com/100x150">
    <source media="(max-width: 200px)" srcset="http://via.placeholder.com/200x150">
    <source media="(max-width: 300px)" srcset="http://via.placeholder.com/300x150">
    <source media="(max-width: 400px)" srcset="http://via.placeholder.com/400x150">
    <source media="(max-width: 500px)" srcset="http://via.placeholder.com/500x150">
    <source media="(max-width: 600px)" srcset="http://via.placeholder.com/600x150">
    <source media="(max-width: 700px)" srcset="http://via.placeholder.com/700x150">
    <source media="(max-width: 800px)" srcset="http://via.placeholder.com/800x150">
    <img src="http://via.placeholder.com/10x10" width="100%">
  </picture>
</div>

浏览器支持目前不完全(像往常一样,IE和Edge是罪魁祸首),但存在polyfills

使用background-image

...可以使用媒体查询进行更改。

@media screen and (min-width: 501px) {
  .container {
    background-image: url('big.jpg');
  }
}
@media screen and (max-width: 500px) {
  .container {
    background-image: url('small.jpg');
  }
}

这是普遍支持的,但需要您的CMS能够生成HTML和CSS规则。

注意:实验性解决方案,非标准。 - Rafael
1
它可以追溯到2014年,包含在当前的HTML5工作标准http://w3c.github.io/html/semantics-embedded-content.html#the-picture-element中,并且得到了大多数主流浏览器的支持,在其余浏览器中进行了填充。 - Daniel Beck
@DanielBeck 谢谢你的回答!不过我有一个问题:两种解决方案中的 @media 宽度是指屏幕宽度,而不是容器宽度,对吗?由于我完全不知道图片显示大小与屏幕宽度的关系(不知道列数等),所以无法为媒体查询分配任何合理的图片版本。我的问题归结为:是否有一种机制可以替代 sizes="100%",该机制不在标准中 "为了避免混淆" - Josta
@Josta 抱歉,我有些粗心了,你说的关于视口和容器的事情是正确的。我已经更新了答案来详细说明。 - Daniel Beck
@DanielBeck 对于更新的答案:感谢提供代码片段。它表明浏览器仅通过屏幕大小选择版本。如果我事先知道特定媒体范围内容器的宽度,我可以使用它(例如,如果容器在0-500px范围内占据50%的屏幕宽度,则可以使用<source media =”(max-width: 500px)”srcset =“http://via.placeholder.com/250x150”>)。不幸的是,我根本不知道容器宽度的任何信息。从您的答案中我了解到,在这种情况下,我无能为力? - Josta

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