探索 object-fit

在 Web 文档中,一个常见的问题涉及在同一位置显示不同大小的图像(或视频)。也许您正在编写一个接受用户提交的动态图库应用程序。您无法保证每个人都会上传完全相同纵横比的图像,那么您该怎么办呢?

让纵横比失真以适应包含的替换元素几乎总是看起来很糟糕。并且每次动态裁剪或调整大小可能比您能够实现的工作量更大。(例如,也许您正在使用 CMS 并且没有权限编辑除页面内容以外的任何内容。)

CSS 图像值和替换内容 模块提供了名为 object-fit 的属性,它解决了此类问题,以及 object-position 属性,它设置元素内部内容的水平和垂直位置。

这些元素在现代浏览器中获得了不错的支持(IE 除外)。在本文中,我们将介绍一些如何使用它们的示例。

注意:object-fit 可用于 SVG 内容,但也可以通过在 SVG 本身设置 preserveAspectRatio="" 属性来实现相同的效果。

object-fit 和 object-position 如何工作?

您可以成功地将 object-fit 应用于任何替换元素,例如

img {
  height: 100px;
  width: 100px;
  object-fit: contain;
}

object-fit 的五个可能值为:

  1. contain:内容(例如图像)将调整大小,使其在保留固有纵横比的情况下完全显示,但仍适合元素设置的尺寸。
  2. fill:内容将扩展以完全填充为元素设置的尺寸,即使这会破坏其纵横比。
  3. cover:保留内容的纵横比,但更改宽度和高度,以便内容完全覆盖元素。较小的一个被设置为完全适合元素,较大的一个超出元素并被裁剪。
  4. none:完全忽略元素上设置的任何高度或宽度,而只使用替换元素内容的固有尺寸。
  5. scale-down:内容的大小如同指定了 nonecontain,以哪一个会导致更小的替换元素大小。

object-position 的工作方式与 background-position 对背景图像的工作方式完全相同;例如

img {
  height: 100px;
  width: 100px;
  object-fit: contain;
  object-position: top 70px;
}

百分比有效,但它们实际上是针对可用空间的剩余部分计算的——元素的宽度与替换内容的最终渲染宽度之间的差值。因此,object-position: 50% 50%(默认值)将始终精确地居中替换元素。此外,object-position: 0% 0% 始终表示与左上角对齐object-position: 100% 100%始终表示与右下角对齐,等等。

关键字 topcenterright 等实际上只是 0%、50%、100% 等的便捷别名。

注意:您可以在我们的基本示例页面中看到一些 object position 示例

不同 object-fit 值的效果

以下代码示例显示了不同 object-fit 值的效果。

使用 object-fit: contain 为图像添加黑边

有时被称为黑边,在某些情况下,您需要保留页面上图像的纵横比,但使其适合同一区域。例如,您可能有一个内容管理系统,允许您上传电子商务网站上的产品或图像库的图像,以及许多不同的内容作者。他们可能会上传大致正确大小的图像,但尺寸并不总是精确的,并且您希望将每个图像都放入相同数量的空间中。

具有纵横比偏移的图像通常看起来很糟糕,因此您可以使用 object-fit: contain 为其添加黑边(object-fit: contain 示例)。

img {
  width: 480px;
  height: 320px;
  background: black;
}

.contain {
	object-fit: contain;
}

使用 object-fit:cover 裁剪图像

另一种解决方案是保持纵横比,但将每个图像裁剪到相同大小,使其完全覆盖 <img> 元素,任何溢出都将被隐藏。这可以使用 object-fit:cover 轻松完成(object-fit: cover 示例)。

.cover {
  object-fit: cover;
}

使用 object-fit: fill 覆盖视频的纵横比

反过来,也可以获取视频并强制其更改纵横比。也许您的一些内容编辑器的视频具有错误的纵横比,并且您希望在一次简单的操作中快速修复它们?

以以下视频图像为例

具有错误纵横比的视频如果将其嵌入到具有以下内容的页面中

<video controls="controls" src="windowsill.webm"
    width="426" height="240">
  …
</video>

它看起来会很糟糕:视频会显示为黑边,因为 <video> 元素始终尝试保持源文件的固有纵横比。我们可以通过应用 object-fit: fill 来解决此问题(object-fit: fill 示例)。

.fill {
  object-fit: fill;
}

这会覆盖视频的固有纵横比,强制其完全填充 <video> 元素,以便正确显示。

有趣的过渡效果

object-fitobject-position 与 CSS 过渡结合使用可以为图像或视频库带来一些非常有趣的效果。例如

.none {
  width: 200px;
  height: 200px;

  overflow: hidden;
  object-fit: none;
  object-position: 25% 50%;
  transition: 1s width, 1s height;
}

.none:hover, .none:focus {
	height: 350px;
	width: 350px;
}

仅显示图像的一小部分,并且当元素聚焦/悬停时,它会增长以显示更多图像(object-fit: none 示例)。

这是因为通过在 <img> 上设置 object-fit: none,我们使内容完全忽略之前设置的任何宽度和高度,并溢出元素的侧面。然后,我们使用 overflow: hidden 来裁剪任何溢出的内容。然后使用过渡在悬停/聚焦时平滑地增加 <img> 元素的大小,从而显示更多图像。

图库示例

为了展示 object-fit 的稍微更实际的用法,我们创建了一个 图库示例

an image gallery showing sixteen pictures in a four by four grid

an image gallery showing one large image

16 张图像通过 XHR 加载,并作为 ObjectURL 插入到图像中。

for(i = 1; i <= thumbs.length ; i++) {
  var requestObj = 'images/pic' + i + '.jpg';
  retrieveImage(requestObj,i-1);
}

function retrieveImage(requestObj,imageNo) {
  var request = new XMLHttpRequest();
  request.open('GET', requestObj, true);
  request.responseType = 'blob';

  request.onload = function() {
    var objectURL = URL.createObjectURL(request.response);
    thumbs[imageNo].setAttribute('src',objectURL);
    thumbs[imageNo].onclick = function() {
      ...
    }
  }

  request.send();
}

依次为每个图像提供一个 onclick 处理程序,以便在单击时,图像以全尺寸显示,填充屏幕(最初在 CSS 中设置为 display: none; 的主图像将被赋予 blowup 类,这使得它显示并填充整个屏幕;然后将主图像的 src 设置为与单击的缩略图相同的 ObjectURL)。

thumbs[imageNo].onclick = function() {
  mainImg.setAttribute('src',objectURL);
  mainImg.className = 'blowup';
  for(i = 0; i < thumbs.length; i++) {
    thumbs[i].className = 'thumb darken';
  }
}

单击全尺寸图像会使其再次消失。

mainImg.onclick = function() {
  mainImg.className = 'main';
  for(i = 0; i < thumbs.length; i++) {
    thumbs[i].className = 'thumb';
  }
}

所有调整大小都是使用百分比完成的,以便无论屏幕尺寸如何,网格都保持比例。

body > div {
  height: 25%;
}

.thumb {
  float: left;
  width: 25%;
  height: 100%;
  object-fit: cover;
}

注意:所有缩略图都已设置为 tabindex="0" 以便通过 Tab 键聚焦(您可以通过在任何元素上设置 tabindex="0" 来使其出现在页面 Tab 顺序中),并且使全尺寸图像出现的 onclick 处理程序已与 onfocus 处理程序一起使用,以提供基本的键盘可访问性。

thumbs[imageNo].onfocus = function() {
  mainImg.setAttribute('src',objectURL);
  mainImg.className = 'blowup';
  for(i = 0; i < thumbs.length; i++) {
    thumbs[i].className = 'thumb darken';
  }
}

巧妙的部分在于 object-fit 的用法。

  1. 缩略图:这些缩略图已设置 object-fit: cover,以便所有图像缩略图都将以相同的大小、正确的纵横比显示,但裁剪的量不同。这看起来相当不错,并且在您调整窗口大小时会产生不错的效果。
  2. 主图像:此图像已设置 object-fit: containobject-position: center,以便它能够以完整、正确的纵横比和尽可能大的尺寸显示。

关于 Chris Mills

Chris Mills 是 Mozilla 的高级技术作家,他撰写有关开放式 Web 应用、HTML/CSS/JavaScript、A11y、WebAssembly 等方面的文档和演示。他喜欢捣鼓 Web 技术,并在会议和大学偶尔发表技术演讲。他曾为 Opera 和 W3C 工作,喜欢演奏重金属鼓和饮用好啤酒。他和他的妻子以及三个可爱的孩子住在英国曼彻斯特附近。

更多 Chris Mills 撰写的文章…


15 条评论

  1. thinsoldier

    是否有此功能的合适 polyfill?

    我很久以前就需要这个功能了。

    多年来不得不使用这个垃圾 http://thinsoldier.github.io/externals/centerimage/example.html

    2015 年 2 月 10 日 14:12

    1. Potch

      我粗略地搜索了一下 Google,找到了这个:https://github.com/anselmh/object-fit

      在本地测试了一下,它似乎在 Firefox 正式版上运行良好。希望这有帮助!

      2015 年 2 月 11 日 12:48

      1. jperrier@mozilla.com

        我在 MDN 文档中添加了:https://mdn.org.cn/en-US/docs/Web/CSS/object-fit#See_also
        ;-)

        2015 年 2 月 15 日 02:20

  2. Montoya

    这太棒了!感谢 MDN!

    2015 年 2 月 12 日 11:14

  3. NoOne

    Polyfill
    https://github.com/anselmh/object-fit

    2015 年 2 月 13 日 07:45

  4. Scott Rod

    我相信 Chris Mills 几年前写过一篇类似的文章。直到 2015 年才看到可接受的浏览器支持!IE 缺乏支持并不让我感到意外。

    不幸的是,caniuse 指出 Safari 不支持 object-position。

    我尝试过/使用过 JS 插件解决方法,但这些方法从未令人满意,尤其是在响应式调整大小是既定事实的情况下。

    终于松了一口气…

    2015 年 2 月 16 日 08:05

    1. Chris Mills

      是的,我们终于朝着正确的方向前进。而且你说得对,我的 dev.opera.com 文章现在有点旧了 ;-)

      2015 年 2 月 20 日 03:56

  5. Anselm Hannemann

    大家好。我是 polyfill 的作者,虽然它确实完成了它的工作并提供了 polyfill,但如果应用于太多元素(在非 WebKit 浏览器上),性能可能会很糟糕。这是因为我需要解析整个 CSS,而 Gecko 和 Trident 都非常糟糕。
    因此,我建议在将其应用于许多元素之前明智地对其进行测试。如果明智地使用它,它确实可以很好地工作,我希望它有助于在旧版和不支持的浏览器上提供该技术的 polyfill。

    2015 年 2 月 18 日 00:48

  6. David

    你好 Chris,大家好……可能是一个非常常见的问题,答案可能会帮助很多人,包括我{我重新爱上了设计}……如何在不需要返回缩略图的情况下为图片添加一些效果和滚动?
    我当然会自己寻找合适的解决方案,因为我非常固执,但一点帮助不会损害我的自尊,反而会让我心怀感激……

    干杯!

    2015 年 2 月 18 日 10:35

    1. Daniel Holbert

      嗨 David,

      听起来您是在问如何修改演示以让您在不缩小的情况下切换照片,对吗?我预计您可以设置其他按钮(或侦听键盘按下,如左箭头/右箭头),并有效地组合 cmills 的两个“onclick”函数(隐藏当前显示的图像并放大下一张图像)。

      2015 年 2 月 19 日 10:27

      1. Chris Mills

        没错——感谢 Daniel 的回复!是的,添加几个屏幕上的按钮或键盘事件处理程序来左右移动放大的照片以在它们之间滚动会很简单。查看源代码以及我如何实现 onclick 处理程序。您可以将类似的处理程序附加到元素以创建一个移动放大图像的按钮,或者使用 body.onpress 事件处理程序来使用键盘控制。

        2015 年 2 月 20 日 03:54

  7. zcorpan

    实际上,Gecko(和 Blink/WebKit)中的实现以及当前规范不允许 object-fit:cover 溢出。这只是 Presto 实现和旧规范(https://dev.opera.com/articles/css3-object-fit-object-position/)。

    2015 年 2 月 19 日 01:50

    1. Daniel Holbert

      正确——实际上没有任何内容*真正*溢出其替换元素的边界——它被裁剪了。我认为 Chris 知道这一点——文章的“object-fit:cover”部分标题为“使用 object-fit:cover 裁剪图像”,Chris 在该部分中说“任何溢出都将被隐藏”。

      (但也许你在说前面那部分,它在非常高的层面上介绍了各种“object-fit”的值?如果是这样,我同意在该部分将“and the larger overflows the element”替换为“and the larger overflows the element *and is cropped*”可能会更清晰。)

      2015年2月19日 10:25

      1. Chris Mills

        感谢你的评论,Simon。 ;-) 我会更新文章以阐明这一点,正如Daniel建议的那样。

        2015 年 2 月 20 日 03:56

        1. zcorpan

          谢谢。不过,仍然有一些关于设置 overflow: hidden; 的内容。:-)

          2015年3月4日 05:34

本文评论已关闭。