相对于包含 div 的 img 宽度

32

原问题

在css中,计算图像相对于其包含的div的宽度最有效的方法是什么?

我有以下代码片段,将#image1.width设置为相对于其父元素宽度的百分比。我使用百分比,因为我需要在屏幕大小调整时,使图像与其父级按比例缩放。

div {
  position: relative;
  display: block;
  width: 100%;
}

#image1 {
  position: absolute;
  left: 10%;
  top: 10%;
  width: 29.43%;
  height: auto;
}

#under {
  width: 100%;
}
<div>
  <img id="image1" src="http://placehold.it/206x115">
  <img id="under" src="http://placehold.it/700x300/ff00f0/ffffff">
</div>

目前它按预期工作,除了我必须手动计算每张图片的宽度百分比。例如:

#image1 dimensions == 206x115
#under dimensions == 700x300
new #image1.width % == #image1.width / #under.width == 206/700 == ~29.43%

我想知道是否有类似calc()方法可以实现以简化/加快这个过程?

我本来打算使用width: calc(100% / 700),但是当屏幕大小改变时,这显然行不通。



目标

再次强调,#under图像必须根据屏幕大小缩放,而#image保持比例。

我希望自然的图像比例互相保持一致(即一个图像是另一个图像的四分之一大小,在所有浏览器宽度下都应该保持如此)。

注意:为了实现这一点,html可以以任何方式重新配置。

目标浏览器:Chrome、Firefox、Edge。



发放赏金后评审

评价@Obsidian Age的答案(第一轮赏金结束于31.03.17):

不幸的是,@ Obsidian Age的答案不正确--它接近但不完全正确,我只是想在这里澄清...以下是他的回答中的一段摘录...请注意,我认为这是一个好答案,只是澄清为什么它还没有被接受:

:root {
  --width: 90vw; // Must be viewport-driven
}

#image1 {
  width: calc(var(--width) / 3); // The 3 can be replaced with any float
}

如果设置--width: 90vw,那么如果bodydiv设置了max-width会发生什么?考虑到viewport-scaling,这在所有设备上计算起来也非常困难。

#image1 { width:calc(var(--width) / 3); } 这相当于 calc(90vw / 3),即30vw,它将等于图像宽度的30%。但我们如何计算要除以的数字呢?嗯,回到最初的问题...width:calc(var(--width) * calc(206/700*100)); 这就是为什么我没有接受这个答案的原因。


206/700 的逻辑是什么? - Hemant
@匿名用户,我已经编辑了这个问题,希望能为您澄清。 - Zze
你的HTML是如何生成的?你可以像medium.com一样预设图像大小。 - Stubbies
@Erevald,不幸的是它并没有被生成,只是老式的 html - Zze
我不确定我理解您的问题。您想要达到什么百分比?您是否希望保留自然图像比率(即,在所有浏览器宽度下,一个图像是另一个图像大小的四分之一)?还是只是想找到一种方法在允许较小的图像增长的同时保持其比率? - David Mann
@DavidMann:“您是否希望自然图像比例在彼此之间保持不变(即,一个图像的大小是另一个图像的四分之一,在所有浏览器宽度下都将保持不变)”- 这是正确的。 - Zze
5个回答

11

CSS不支持父级选择器。虽然你不能直接用CSS将一个元素相对于其父级定位,但是你可以使用纯CSS设置一个变量,让两个元素都使用它:

:root {
--width: 90vw; // Must be viewport-driven
}

现在您可以将此变量用作父元素的(固定)宽度以及子元素的计算驱动宽度:

#under {
  width: var(--width);
}

#image1 {
  width: calc(var(--width) / 3); // The 3 can be replaced with any float
}

请注意,变量必须是固定单位或相对于视口。如果它是基于百分比的,#under#image1都将以各自的父级为基础确定其宽度。为了使此功能具有响应性,它必须基于视口。

:root {
--width: 90vw;
}

div {
  position: relative;
  display: block;
  width: 100%;
}

#image1 {
  position: absolute;
  left: 10%;
  top: 10%;
  width: calc(var(--width) / 3);
  height: auto;
}

#image2 {
  position: absolute;
  left: 25%;
  top: 10%;
  width: 10%;
  height: auto;
}

#under {
  width: var(--width);
}
<div>
  <img id="image1" src="http://placehold.it/206x115">
  <img id="under" src="http://placehold.it/700x300/ff00f0/ffffff">
</div>

我也创建了这个JSFiddle,在那里你可以看到当视口调整大小时两个元素都会缩放。

希望这可以帮到你! :)


这是一个非常有趣的答案。你有什么补充意见可以使其变得响应式吗?因为我需要#under随着屏幕大小进行缩放...由于在calc()中不能使用%作为除数。 - Zze
正确;我刚意识到这一点。您需要在变量中使用基于视口的宽度,并以此为基础构建两个元素。我刚刚更新了我的答案来涵盖这一点 :) - Obsidian Age
你所做的就是将#under的宽度设置为90%,将#image1的宽度设置为30%。 - Robby Cornelissen
1
@RobbyCornelissen 是正确的,不幸的是这并没有解决问题。 - Zze
--(在 --width 中)表示什么? - Caramiriel
1
@Caramiriel - 这表示它是一个CSS变量,因此您可以在多个不同的属性中使用相同的值 :) - Obsidian Age

6

我知道这个问题要求使用纯CSS解决方案,但我的理解是不能使用JavaScript。

基于此,这里提供一种使用嵌入式SVG的解决方案:

<svg xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="100%" viewBox="0 0 700 300">
     
  <image x="0" y="0"
      xlink:href="http://placehold.it/700x300/ff00f0/ffffff"/>
  <image x="10%" y="10%"
      xlink:href="http://placehold.it/206x115"/>
</svg>


你的理解是正确的 - 这在Chrome中完美运行,但似乎在其他浏览器中无法工作,有什么想法吗?如果它在其他浏览器中也能正常工作,这绝对是最佳选择。 - Zze
4
似乎<image/>元素的自适应大小是SVG 2.0的一个功能,但不幸的是目前大多数浏览器还不支持它。你需要在2019年之前使用这个功能吗? :) - Robby Cornelissen
哈哈,呃,是啊,最好是这样。唉,太令人失望了,看起来非常有前途! - Zze
你最近有没有关于这个问题的任何启示?像你的方法正是我想要的! - Zze
如果您不介意添加图像的实际尺寸,那么现在这个已经可以工作了:演示。不确定这是否回答了您的问题?至少它可以节省您计算百分比的时间。 - Just a student
@Justastudent 也许你应该发布一个答案。 - Zze

5

我认为最好的方法是消除宽度并使用比例来适应div,但问题在于比例变换不接受任何单位值,如%或px或vw / vh!

.common_img_class{
    transform: scale(calc(100vw/700)); /* this won't work! */
    /* Idea here is to let image take is original width & then scale with respact to base image scaling. Base image scaling is detenined by *window_width/base_image _width*, here base image width is 700 as per you example */
}

因此我能想到的第二种方法是消除手动计算百分比。使用calc()函数来代替您进行计算。
#firsrt_img{
    width: calc((206/700) * 100%);
}
#second_img{
    width: calc((306/700) * 100%);
}

在这里,您仍然需要为所有内容编写宽度,但至少可以避免百分比或比例计算。

注意:
如果有人能够提供第一种方法的帮助,他们的意见将受到欢迎。


感谢您的回答,我正在尝试寻找更多向您的第一个建议的解决方案,因为手动键入所有算法实际上与我的当前解决方案相同,只是在使用大量图像时可能会产生性能影响。 - Zze

2
如果您愿意使用Bootstrap(https://getbootstrap.com/examples/grid/),可以尝试以下方法:
<!-- parent -->
<div class="row">
  <div class="col-md-6">
    <img src="">
    <!-- this is 50% of the screen for min 992px, a full line otherwise -->
  </div>
  <div class="col-md-3">
    <img src="">
    <!-- this is 25% of the screen for min 992px, a full line otherwise-->
  </div>

  <div class=col-md-3>
    <img src="">
    <!-- this is 25% of the screen for min 992px, a full line otherwise -->
  </div>
</div>

你可以按照自己的需求进行分组,但需要记住数字总和必须为12(例如,6+3+3)。使用col-md-12可以实现100%宽度效果。此外,md中缀只是在将所有元素放在同一行和堆叠元素之间切换的多个选项之一。你可以查看http://getbootstrap.com/css/#grid获取更多详细信息和几个示例。

我熟悉Bootstrap,但如何实现约29%的宽度?据我所知,这是不可能的。 - Zze
不确定是否有效,但您可以尝试嵌套来实现此效果。例如: <div class="row"> <div class="col-md-6"> <!--50% --> <div class="row"> <div class="col-md-4"> <!--其父级的33%,即50%的33%--> <img> </div> </div> .... </div> - andreifin

1

Robby Cornelissen答案启发,这里提供了一种目前在目标浏览器中可行的方法。它唯一的缺点是图像的尺寸必须在HTML(实际上是SVG)中显式地指定。

这是一个演示

<svg xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="100%" viewBox="0 0 700 300">
     
  <image x="0" y="0" width="700" height="300"
      xlink:href="http://placehold.it/700x300/ff00f0/ffffff"/>
  <image x="10%" y="10%" width="206" height="115"
      xlink:href="http://placehold.it/206x115"/>
</svg>


这种方法与Robby的答案类似,使用SVG image元素。当没有显式指定尺寸时,该元素目前默认为零宽度和高度,但在SVG 2中将会改变。这意味着一旦浏览器支持SVG 2,我们可以设置width="auto" height="auto"而不必指定图像尺寸。

‘image’元素上widthheight的值auto是根据引用图片的固有尺寸和长宽比,按照CSS 默认尺寸算法计算得出。
SVG 2 Editor's Draft, § 7.8. Sizing properties: the effect of the ‘width’ and ‘height’ properties. Accessed 2017-12-18.


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