半深入理解CSS3 object-position/object-fit属性

这篇文章发布于 2015年03月15日,星期日,22:35,归类于 CSS相关。 阅读 160917 次, 今日 8 次 36 条评论

 

一、春来了,春来了~

帘外雨潺潺,春意阑珊。

奇怪,为何我要写个诗段子呢?

或许是因为,本应是阳春三月的时候,依旧阴冷潮湿,丝丝凉意侵入小腿,无法让我无法安心整理思路。想明白了这点,我立马站起身来,打开房门,大步走出去!去外面呼吸新鲜空气…………吗?不是的,我是去卧室拿遥控器,开个空调,给屋子升个温,好安安心心写点东西。

恩,预热了数分钟,暖气呼啦啦出来了。恩,春来了,春来了~~可以安心干活,想些技术的东西,而不是无故冒出奇怪的古诗词了。

二、从哪里开始呢?

可以写的点很多,从哪里开始呢?

啊,先手标题入手吧。首先是“半深入”,标题这里之所以称为“半深入”,不是因为短,而是object-position/object-fit自己尚未在实际项目中实践过,没有足够多实践经验的认知都不能称为“深入理解”。按照以前的调调,我会变身成萧敬腾——不生产水,只是大自然的搬运工,然后文章名称就是“CSS3 object-position/object-fit简介”,就是把官方文档中文梳理一下。但是,本文,有很多自己加工过的产出、有很多个人理解在里面的。故,综上,名曰为“半深入”。

然后就是本文主角,一对兄弟,object-positionobject-fit。既然是主角,肯定要不断打小怪升级的,电视剧才刚开头,所以你别指望直接高潮,慢慢来。我们不妨先来了解下这里的object为何物?

要了解属性名上的这个的object, 我们还得从官方解释入手(英文):

The object-fit CSS property specifies how the contents of a replaced element should be fitted to the box established by its used height and width.

The object-position property determines the alignment of the replaced element inside its box.

小哥,你™在逗我呢!虽然我无法洞察英文的内心,但是,我还是认得英文的长相的啊!上面两段官方解释,除了属性本身,根本就没有object这个单词出现过,如何解释object为何物?

谁说解释“菊花”就一定要出现“花”的,这里的object实际上指的是replaced element(替换元素)(W3C, MDN)。

替换元素
不是所有元素都叫“替换元素”。在CSS中,“替换元素”指的是(引用自这里):

其内容不受CSS视觉格式化模型(中文释义参见这里)控制的元素,比如image, 嵌入的文档(iframe之类)或者applet。比如,img元素的内容通常会被其src属性指定的图像替换掉。替换元素通常有其固有的尺寸:一个固有的宽度,一个固有的高度和一个固有的比率。比如一幅位图有固有用绝对单位指定的宽度和高度,从而也有固有的宽高比率。另一方面,其他文档也可能没有固有的尺寸,比如一个空白的html文档。

CSS渲染模型不考虑替换元素内容的渲染。这些替换元素的展现独立于CSS。object, video, textarea, input也是替换元素,audiocanvas在某些特定情形下为替换元素。

使用CSS的content属性插入的对象是匿名替换元素。

也就是说,本文的object-positionobject-fit只针对替换元素有作用,也就是form表单家族控件系列,老牌劲旅img图片,HTML5新贵video视频等元素(据我测试,SVG元素貌似不支持,但可以作为src替换内容出现)。

三、为何需要object-position/object-fit?

从上面“替换元素”的官方解释可以看出,替换元素通常都有着固定的尺寸,比方说图片,例如,下面这张眼熟能详的图片:
眼熟能详的图片

其原本的尺寸是256*191像素。但是,现代web显示环境万变,响应式布局(适配移动端)、半响应布局(宽窄屏适配)以及传统流体布局(任意宽度水平自适应)都比较常见,结果,在处理这些有着固定尺寸的元素的时候,就会遇到障碍,例如,一个2:1的视频,永远要等比例充满容器,但是,随着浏览器宽度不一样,容器的长宽比有可能小于2,也有可能大于2. 你说,你能轻松实现小于比例2时候,视频宽度100%;比例大于2的时候,高度100%吗?

显然,很麻烦,最后大多还是使用JS去把控(如国际版QQ首页中的视频处理 – 浏览器宽度任意拉伸)。正是为了应对类似这样的响应式需求,object-fitobject-position才应运而生。

object-fitobject-position之间的关系有点类似于background-sizebackground-position,无论是,关系还是属性值,都有相似之处,因此,我们在理解object-fitobject-position的时候,可以或多或少映射background-sizebackground-position

四、object-fit理解

CSS3 background-size出现的比较早,大家应该知道其支持的一些值,除了数值之外,其还支持几个关键字,例如:cover, contain等。

object-fit也是类似的,但还是有些差异,具体有5个值:

.fill { object-fit: fill; }
.contain { object-fit: contain; }
.cover { object-fit: cover; }
.none { object-fit: none; }
.scale-down { object-fit: scale-down; }

每个属性值的具体含义如下(自己理解的白话文,官方释义见官网):

  • fill: 中文释义“填充”。默认值。替换内容拉伸填满整个content box, 不保证保持原有的比例。
  • contain: 中文释义“包含”。保持原有尺寸比例。保证替换内容尺寸一定可以在容器里面放得下。因此,此参数可能会在容器内留下空白。
  • cover: 中文释义“覆盖”。保持原有尺寸比例。保证替换内容尺寸一定大于容器尺寸,宽度和高度至少有一个和容器一致。因此,此参数可能会让替换内容(如图片)部分区域不可见。
  • none: 中文释义“无”。保持原有尺寸比例。同时保持替换内容原始尺寸大小。
  • scale-down: 中文释义“降低”。就好像依次设置了nonecontain, 最终呈现的是尺寸比较小的那个。

假设我们使用的原始图片是:
眼熟能详的图片

容器的尺寸是160*160, 背景色是灰蓝色,同时,测试的图片高宽设置为100%, 如下CSS代码:

.box { width: 160px; height: 160px; background-color: #beceeb; }
.box > img { width: 100%; height: 100%; }

结果,各个属性值的表现如下截图:

object-fit四个属性值效果截图

您可以狠狠地点击这里:CSS3 object-fit属性值表现Demo

大家可以对照示意感受下,什么是内容拉伸(fill),什么是内容全部都显示(contain),什么是容器没有留白(cover),什么是该多大就多大(none)。

新垣结衣,等等,等一下!scale-down呢?
新恒结衣 等一下

scale-down由于具有动态特性,所以要专门展示下。实际的替换内容表现是nonecontain最终尺寸小的那个属性值的表现。

例如,假设我们的容器可以resize拉伸,改变其尺寸,则当容器尺寸比256*191小的时候,也就是容器比替换内容(这里的图片)实际尺寸小的时候,效果跟contain一致,因为此时contain的实际展示尺寸比none小。如下截图示意:
scale-down和contain表现一致

但是,当容器尺寸拉伸到比图片实际尺寸还大的时候,则效果跟none一致,因为此时none的实际展示尺寸比contain小。如下截图:
scale-down和none效果一致

至此,我们大致了解了object-fit各个属性值的表现,你以为事情就结束了吗?不是,这只是个开始。

难道大家没有对图片的表现表示疑惑吗?

我明明图片设置了宽高都是100%, 为何貌似实际效果除了(fill),似乎都无视了这些声明呢?图片的实际尺寸变小或变大了,而不是根据容器实际尺寸走呢?

.box > img { width: 100%; height: 100%; }

你有没有这样的疑惑?有木有!!

如果有,恭喜你,来对地方了,这表明你还没有对一些概念认识清楚。

  1. img是个元素,且是个替换元素,这个通过上面的学习应该都知道;
  2. 一个图片,如果没有src,它依然是个替换元素,它在浏览器中的解析依然是正确的;
    <img>
  3. src指向的图片属于替换内容,注意,这个替换内容和这个img替换元素是壳子与内容的关系,两者是独立的。在CSS2.1时代,壳子的实际尺寸(如果没有CSS或HTML设置),则是跟随内容的实际尺寸,因此,网页加载的时候,我们会看到图片占据的高度从0到图片实际高度跳动的过程;如果壳子,也就是img有尺寸限制,则替换内容fill拉伸适应于 img替换元素的设定尺寸。总而言之,壳子与内容的尺寸永远是一样的。于是,我们就会误认为图片就是那个图片,唯一的存在,导致我们理解object-fit的特性表现出现了障碍。
  4. 在CSS3时代,object-fit的世界里,object-fit控制的永远是替换内容的尺寸表现,注意,是替换内容的尺寸表现,不是img替换元素。或者这么讲吧,我们对img设置:
    .box > img { width: 100%; height: 100%; }

    实际上是控制img这个元素的,这个壳子的尺寸是100%撑满容器。上面截图的5个示例的图片实际上都是100%拉伸与容器的;之所以实际的图片内容没有拉伸,是因为受object-fit控制,object-fit控制了src对应的替换内容的尺寸,或者包含,或者覆盖。

  5. 之所以object-fit:contain会透明留白,是因为我们没有对壳子img设置背景色,假设我们给壳子img增加个红色背景,大家就会明白我说的意思了:
    .box > img { width: 100%; height: 100%; background-color: #cd0000; }

    效果如下截图,会发现,原来的透明留白现在是红色背景,说明了什么?说明img替换元素和src替换内容是两个独立体。img替换元素受到了CSS 100%拉伸控制(所以红色背景充满容器),src替换内容也受到了object-fit展示控制。大家各司其职,没有什么覆盖冲突!

    背景色占据区域大小


你以为理解了object-fit的行为表现就结束了吗?才怪呢!

目前,IE浏览器并不支持object-fit

object-fit兼容性截图

object-fit好棒棒,我很喜欢,奴家很想要,怎么办?

有个Github上有个polyfill, 大家有兴趣可以围观下。

你以为知道了如何兼容处理就结束了吗?没错,结束了,我们看object-position.

四、object-position理解

object-position要比object-fit单纯的多,就是控制替换内容位置的。默认值是50% 50%,也就是居中效果,所以,无论上一节object-fit值为那般,图片都是水平垂直居中的。因此,下次要实现尺寸大小不固定图片的垂直居中效果,可以试试object-fit.

background-position类似,object-position的值类型为<position>类型值。也就是说,CSS3的相对坐标设定样式支持的。

例如替换内容一直定位在容器的右下角:

object-position: 100% 100%;

例如替换元素相对于右下角20px 10px地方定位:

object-position: right 20px bottom 10px;

上面的表现在FireFox 36浏览器下符合预期,Chrome没看明白:
FireFox 36下相对右下角定位坐标

因此,建议还是使用calc实现相对右下角定位:

object-position: calc(100% - 20px) calc(100% - 10px);

此时,Chrome浏览器的表现符合预期了:
Chrome浏览器下使用calc右下角定位

同样的,object-position也支持负值,特征表现与background-position一致。

由于比较单纯,就不展开了。若有兴趣,您可以狠狠地点击这里:CSS3 object-position使用示意Demo

五、object-position/object-fit其他应用

CSS真是个很奇怪的东西,发明它的目的可能是为了实现A效果,结果,为了实现A效果所外挂的一些特性往往被用来做其他事情。比方说float被大肆用来块状布局,我觉得object-position/object-fit也有这种趋势。

上一节提到了水平垂直居中实现,直接一行CSS声明就搞定,简单快捷,以后说不定会热。

还有就是下面要介绍的img sprites实现。

要实现img sprites,方法很多:
1. 外部尺寸容器overflow:hidden限制,里面<img>positionmargin定位。但这种方法需要2层标签,麻烦。
2. 其实,很早时候,我就尝试过使用clip实现img sprites效果,可以1层标签就搞定。但是,<img>元素必须position:absolute. 麻烦。

OK, 这里要介绍的img sprites实现不仅只需要一层<img>标签,而且没有其他条件限制,比之前的方法都要简单很多。因为从上面诸多示例可以看出,object-fit天生外挂剪裁功能。

img sprites我唯一想到的应用场景是当图片是动态图片的时候,例如首页的slide广告,大部分情况下img sprites是没有应用价值的,反而添乱。跟background sprites相比没有任何优势,至少我是没看出来的。

众人:那你还讲毛线?

应用场景千千万,万一什么时候此方法就跩了一把呢~~

我们不妨看下下面这个任意数字翻转效果:

我们使用的sprites图片是:
序号sprites

我们有如下CSS:

.num {
    width: 40px; height: 40px;
    object-fit: none;
    object-position: 0 0;
}
.num0 {  }
.num1 { object-position: 0 -40px; }
.num2 { object-position: 0 -80px; }
.num3 { object-position: 0 -120px; }
.num4 { object-position: 0 -160px; }
.num5 { object-position: 0 -200px; }
.num6 { object-position: 0 -240px; }
.num7 { object-position: 0 -280px; }
.num8 { object-position: 0 -320px; }
.num9 { object-position: 0 -360px; }

于是数字1对应的就是:

<img src="icons-ol.png" class="num num1">

于是数字2对应的就是:

<img src="icons-ol.png" class="num num2">

您可以狠狠地点击这里:object-position/object-fit img sprites与数字翻动demo

demo页面文本框你修改之后,图片数字会跟着变化,例如改成999~
文本框数值变化图片跟着变化

img sprites和background sprites就功能和能耗上而言,基本上是一致的。不过一个img图片,一个是img背景图片。前者兼容性有待提高。所以,目前来看,优势不明显。

六、春来了,春来了~

一开始春来了的时候,自己还是很兴奋的,以为春花秋月来了,但是,现实是春困秋乏。在暖暖的空调房里,硬是在办公椅上打了两个盹,分别在午饭后和晚饭后。

搞到写到现在才开始结尾,这效率……还不如去鱼塘抛两杆子~~

//zxx: 这里省略应该打盹时间吐槽的283个字……

最后,感谢阅读,欢迎交流!

(本篇完)

分享到:


发表评论(目前36 条评论)

  1. mayday说道:

    学到了

  2. Attide说道:

    完美解决我的问题,好文章 o( ̄▽ ̄)d

  3. 给图片上定位元素的问题说道:

    给图片上定位元素的 外层容器会动态固定,但是图片大小不同屏幕显示也不一样,但不会超出外层容器, 这个需求 单单用这个就不行了, 我的思路是用宽高比计算,但是定位上面的元素显示就会有问题, 这个怎么破? 请大佬解疑 拜谢!

  4. Adonia说道:

    我的妈呀!为什么没有早让我看到这篇文章???完美解决了我在iPhone 5s预览图片不能完全显示的问题,之前都没见人用到这个属性啊,大神牛逼!!!

  5. 小贝贝拉说道:

    看完之后,有2个疑问。 1.如果说替换元素和替换内容是完全独立的,如果给替换元素的宽高设置 200%,即使替换内容设置 object-fit: contain, img也会超出外层的div。所以这个object-fit 适应的是 替换元素的大小吗? 2. transform: scale 属性是作用于 替换元素还是替换内容呢? 因为从实验结果看 ,设置了scale之后, object-fit: contain 也没有起到预期效果

  6. 吱吱吱···说道:

    图片的宽高还是在固定啊,目前只能固定图片宽度,高度该如何设置不变形啊

  7. 使用calc实现相对右下角定位和上面用具体数值表示,没看到什么区别说道:

    上面的表现在FireFox 36浏览器下符合预期,Chrome没看明白:使用calc实现相对右下角定位和上面用具体数值表示,没看到什么区别???图片显示效果还是一样的,怎么就说chrome表现正常了呢

  8. 側臉西零C0说道:

    從台灣谷歌到小哥的文章,覺得很實用,也寫的很生動,感謝分享 <3

  9. saber酱说道:

    很好,原来 cover 这种样式 img 也能用了,很方便

  10. 11说道:

    完美的解决的我的问题 牛逼

  11. 能不能不要说一些和内容无关的话说道:

    很妨碍内容的直接获取,还容易分散精力,自以为有特色,实则让文正更加杂乱

    • 张 鑫旭说道:

      感谢提醒,话唠的冲动之后克制了很多。

    • 自己不喜欢就嘲讽,您算老几?说道:

      觉得这边文章的描述风格你不喜欢,直接关掉去看其他的就完事了.

      也有很多博客是你要的那种可以直接获取内容的风格,没必要在这里冷嘲热讽作者的写作风格, 有人喜欢有人不喜欢,没必要非得适应你这种人去改变风格.

      PS: 能不能不要留一些对作者没有意义的评论,建议或者对内容的观点有不同的看法都可以留出来,您这不属于建议

    • 你谁啊?资历很高么,这就飘了说道:

      还是鑫旭哥脾气好,换别人骂到令堂都不认识你。

    • 楼主他爹说道:

      你花时间回复别人就不分散精力了?傻逼逻辑,不看就滚,傻屌

    • 菜鸟说道:

      我倒觉得挺好的,但看纯技术容易看晕,加入一些吐槽有点意思

  12. CbdFocus说道:

    “`

    .numbox {
    width: 80px;
    height: 80px;
    }

    .num {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 0 0;
    -webkit-transition: object-position .25s;
    transition: object-position .25s;
    }

    .num0 { }
    .num1 { object-position: 0 11%; }
    .num2 { object-position: 0 22%; }
    .num3 { object-position: 0 33%; }
    .num4 { object-position: 0 44%; }
    .num5 { object-position: 0 55%; }
    .num6 { object-position: 0 67%; }
    .num7 { object-position: 0 78%; }
    .num8 { object-position: 0 89%; }
    .num9 { object-position: 0 100%; }

    if (window.addEventListener) {
    var eleNums = document.querySelectorAll(“.numbox>img”);
    document.querySelector(“.demo input”).addEventListener(“change”, function () {
    var value = this.value.trim() * 1;
    if (!value || value 999) {
    value = 283;
    }
    this.value = value;

    console.log(‘value’, this.value);

    this.value.split(“”).forEach(function (num, index) {
    eleNums[index].className = “num num” + num;
    });
    });
    }

    “`

    • 上文.demo后少了一个“,”说道:

      代码中document.querySelector(“.demo input”).addEventListener(“change”, function () demo后少了一个“,”。

  13. xzd说道:

    帮了我很大的忙!之前对视频用object-fit: cover 永远超出限定的frame
    看了你的文章 用了 object-position解决了!

  14. cseve说道:

    15年就有了这个文字,我17年才因为一个需求了解到的属性
    想了下现在各种-webkit偏门属性是在真正用到的时候才查出来的
    想这写不常用,但有效果的属性
    有没有什么手册查到啊???

  15. 看到你的文章内心是崩溃的说道:

    我就想看一下object-fit的属性,上面的文档一大半都是废话!

  16. 学习了说道:

    在我的网站相册上实验了一下,ie确实不支持,非常遗憾。我看很多人做网站文章列表缩略图大致都是两个思路:1、把图片做成缩略图容器的背景图来控制尺寸显示。2、所有缩略图都是事先裁剪好匹配的尺寸再上传(或者上传完,程序再裁剪)。2的方式维护管理麻烦,也不利于后期升级。1的方法方便但缩略图用背景方式体现,总感觉奇怪。

    ie以后能支持object-fit的话就太完美了!

  17. 半桶水说道:

    无法让我无法安心整理思路<<<<<<<<<<<<<<<<<<<<<<<<_<

  18. caicai说道:

    大神,对于替换元素的概念还是不明白,能否解答一下

  19. 菜鸟说道说道:

    object-fit 在移动端里面!!简直好用到暴 妈妈再也不用担心图片变形了

  20. Dudy说道:

    CSS多了更多的API,以前不知道咋控制替换元素的替换内容的,现在可以了。以后如果有个操作每个行盒的API,就更好了,希望CSS中能涌现出更多的API

  21. 路人说道:

    对出文案错误 object-fix

  22. 灵感_idea说道:

    张老师,”object-fit“写着写着怎么成了”object-fix“?不过瑕不掩瑜,很赞的一篇”半深入“。

  23. 烟囱洗衣机说道:

    哥你是在哪里发现这个属性的

  24. 搁浅被注册了说道:

    我就注意到了你把“新垣结衣”名字写错了