-webkit-box-reflect属性简介及元素镜像倒影实现

这篇文章发布于 2016年08月14日,星期日,15:18,归类于 CSS相关。 阅读 46456 次, 今日 4 次 25 条评论

 

一、从前有只鸭子

从前有只鸭子,长得不是一般的丑:

梁朝伟 我是一只鸭子

于是,鸭子一直很自卑,从来不敢照镜子,也不敢下河游泳。别的鸭子很喜欢下河游泳,因为可以一边玩耍,一边上WC,让吃进去的东西舒舒服服从后面出来;而这只鸭子一旦下河,吃进去的东西只会是从前面出来,因为总会不经意看到自己的脸。

就这样很过去了很多很多年,世事变化无常,当年会说话住山洞被鸭子调戏的猩猩逆袭现在住进了动物界的五星级宾馆——上海野生动物园;而鸭子自己,历经岁月的沧桑,容貌也发生了巨大的变化:

梁朝伟 很帅的鸭子

然而,鸭子却不知道这一切,因为它自卑不敢照镜子不敢游泳,所以它并不知道自己现在的容貌是什么样子,直到2016年8月中旬,鸭子偶然间遇到了一件宝物,这才突然发现自己现在™长得比梁朝伟还帅。

咦,既不是镜子,也不是水流,那是什么东西让鸭子知道了自己的真面目呢?

好,欢迎大家收看本期的走进前端:鸭子真面目毕露之宝物——box-reflect属性

二、CSS宝物之盒倒影属性-webkit-box-reflect

当时的情形是这样的:鸭子有点感冒,痰有些多,于是决定趁机休个假打飞的去伦敦喂鸽子散个心,在机场候机的时候,旁边有个黑衣人模样的人正在用5K屏幕水果笔记本看电影《东成西就》,鸭子就侧过去一起看了。当电影放到“香肠嘴”那里的时候,鸭子居然狂笑了起来!

梁朝伟香肠嘴

居然,没错,是“居然”!你可知道,鸭子这数百年来几乎就没笑过,即使这几个世纪见过无数搞笑的场景,但是,一直把笑压抑在自己心中。但是,或许是因为这“香肠嘴”有一种前世熟悉的感觉,让自己一下子没忍住,连同之前数百年压抑的笑一下子喷薄了出来,“哈……哈……”

哈哈

然而,当鸭子一瞬间意识到自己笑出声的时候,容不得自己思考,似乎发现自己犯了天大错误一样,使尽全力收住自己的笑气。但是,这百年积攒的笑气能量之强,岂是想收住就能收住了。一瞬间,笑气内涌,胸腔顿时涨裂之感,无法呼吸。鸭子实在无法忍受,翻涌的能量化作利剑一般,伴随一声巨咳从口中释放出来。

这一切来得是如此的突然如此的猝不及防,虽有些失色,有些狼狈,但还不至于造成太大的干扰,毕竟就2个笑声,1个咳嗽声而已。直到鸭子长吁一口气,继续看黑衣男笔记本的时候……鸭子发现5K屏幕上多了形状像“八”的粘性液体组成的图案。这一瞬间,世界似乎停止了运转,鸭子真希望现在有条河可以让自己钻进去~~

但是,黑衣男似乎很镇定,只见他一键关机,然后,顺势从肚子前的口袋里拿出了写着-webkit-box-reflect字样的魔法布,对着5K屏幕就这么一抹,卧槽,奇迹发生了,没有任何接触,屏幕体贴变得干干净净,不仅如此,屏幕变得剔透异常,把周围景物反射的清清楚楚。

此时,鸭子突然从屏幕中看到了一个大帅哥,“咦,此人长得跟我很像嘛……” (目标浏览器才有倒影,IE和FireFox留白无倒影)

后面的故事大家应该就知道了,从此,鸭子开始自信起来,笑容也多了起来,这就是“丑小鸭变成白天鹅”的故事。

上面让鸭子看到自己面容倒影的宝物就是CSS届的-webkit-box-reflect,上面施展的魔法技能是:

-webkit-box-reflect: below;

当然,既然是宝物,技能不可能只有一件,速速全部拿来开光。

语法如下:

-webkit-box-reflect: [ above | below | right | left ]? <length>? <image>?

上面语法中,[]|?都是正则表达式中的通用表达含义,例如?表示0个或1个。|表示或者的意思。具体来说-webkit-box-reflect最多可以由3部分组成,分别如下:

  • 方位
    可以是下面4个值中的1个,above,below,left,right,分别表示上下左右。如果不是使用类似inherit等全局关键字,则此方位值是不能缺省的。
  • 偏移大小
    倒影和原始元素偏移距离。可以是数值,也可以是百分比值。如果是百分比值,则百分比大小是相对于元素自身尺寸计算的。和transformtranslate的百分比计算是一致的。
  • 遮罩图片
    对元素倒影的遮罩控制。语法类似于background-image

因此,我们可以有如下的一些书写:

-webkit-box-reflect: below;

-webkit-box-reflect: right;

-webkit-box-reflect: right 10px;

-webkit-box-reflect: below 0 linear-gradient(transparent, white);

-webkit-box-reflect: below 0 url(shuai2.png);

等。然后,对应的效果如下:

您可以狠狠地点击这里:-webkit-box-reflect各个属性值效果演示demo

如果您使用的是Chrome或Safari浏览器,或者使用的是手机设备浏览,则应该会看到类似下面截图的效果:
倒影效果截图

分别对应上面书写的效果。然而,还有很多信息是demo里面体现不出来需要额外说明的。

其他说明

  1. 倒影不占据尺寸空间,是和outline, box-shadow一样的那种真正意义上的不占据空间尺寸。所以,demo使用了padding值撑开间距,免得几张鸭子图片重叠在一起;
  2. 上面提到了,倒影偏移值支持百分比,且是根据元素自身尺寸计算的。但还有一点需要补充,就是,尺寸计算的方位是根据你的倒影方向自动识别的。比说说,你的倒影方向是belowabove,则偏移百分比是根据原始元素的高度想计算的,如你的倒影方向是leftright,则根据元素的宽度来计算;
  3. 遮罩图片可以使用CSS3渐变。并且,这里的渐变语法和CSS3 background-image的语法几乎是一模一样的。注意这里的措辞“几乎”,说明还是有不同之处的,不同支持就是-webkit-box-reflect的遮罩图片不支持多图(对于background的多背景),只能是一张图。其他的语法就是一模一样的。包括不仅支持线性渐变(linear-gradient),还支持径向渐变(radial-gradient),支持to topto bottom新语法,支持stop断点,例如:
    -webkit-box-reflect: below 0 linear-gradient(transparent, white 50%, white);
  4. 使用遮罩图片的时候,倒影的偏移值是不能缺省的。如果没有偏移,请使用0占位,这也是为何示意代码中有个0的原因;
  5. 遮罩的最终效果与颜色无关,也就是你使用linear-gradient(transparent, white)linear-gradient(transparent, black)的效果是一模一样的,关键在于是否透明以及透明度是多少。实色遮罩的结果就是透明,透明遮罩的结果就是暴露原始色值。
  6. 遮罩图片也可以直接是图片,语法同background-image。但是有个需要注意的是,当图片应用于倒影遮罩的时候,这个图片自身也会被(因倒影)而翻转。比方说上面demo最后一个例子,遮罩出了一个“帅”字,而这个原始的“帅”文字图片是这样的:
    “帅”字

    大家发现没,是倒着的。这样,倒影下来,我们就可以在倒影上看到正过来的“帅”效果了。

  7. 倒影效果具有实时渲染特性。也就是,如果我们突然把鸭子恢复到当年“丑小鸭”的状态,则,鸭子一定会想起不堪回事的往事。
    丑小鸭倒影

    于是,我们可以利用这一特性,实现一些很酷的动效。对吧,就那种……上面动下面也跟着一起动的那种……你应该懂的~~

三、-webkit-box-reflect兼容性

-webkit-box-reflect说到现在,我几乎就米有把-webkit-私有前缀去掉,因为,盒倒影属性box-reflect本身就是webkit私有的一个特性,并且支持已经有很多很多年了,所以,只要浏览器是-webkit-内核,均支持该属性。

包括Chrome/Safari/Opera以及移动端浏览器(from caniuse):
倒影兼容性表

所以,我们可以在内部项目,或者在移动端项目中放心大胆使用该CSS属性。

那如果我们需要在传统PC项目上也能实现投影效果,该怎么办呢?其实是可以曲线救国的。

四、-moz-element()让FireFox浏览器也支持元素投影

首先,我们看看FireFox也没有什么方法可以实现元素投影效果。

有,试试element()函数,关于element()函数,我在2011的时候就介绍过,“CSS3 background扩展属性element简介”, 可以让一个元素作为背景使用,语法很简单:

 background: element(#id)

只要#id对应的元素不是<img>元素,也是具有实时跟踪渲染特性的。

//zxx: 不知从哪个版本开始,FireFox不支持图片的跟踪渲染了~~

//zxx: 更新于2020年8月4日,应该是临时bug,现在跟踪效果很好

如果是FireFox浏览器,您可以狠狠地点击这里:利用-moz-element()实现倒影demo

进入页面扑面而来的是这样的效果:
FireFox浏览器下的倒影效果

我们点击demo页面下面的按钮,让原图旋转起来,大家可以发现,倒影图片也旋转起来了(更新于2020年8月4日:倒影旋转现在不是下图样子了,Firefox已经修复了)。

旋转跟随截图

然而,和真正的倒影效果相比,还是有很多问题的。

局限

  1. 不支持遮罩。demo所见的“倒影”实际是覆盖了一层白色渐变模拟的。
  2. 占据空间。展示的倒影是不占据任何尺寸位置的。但是,如果background模拟,就需要设定一个尺寸。且背景图片是无法超出元素的,因此(如上图所示),倒影可能会显示不全。
  3. 倒影的位置是死的。真实的倒影旋转应该是下图这样,而不是上图那样:
    真实的倒影和旋转的位置关系
  4. 渲染性能很差。FireFox浏览器下,原图在360度旋转,但是,“倒影”元素的旋转取像是吃了“含笑半步癫”,一癫一癫、一顿一顿的,一股IE6的春风扑面而来。

更新于2020年8月4日

上述局限都已经不存在了。

  1. 首先,Firefox浏览器目前已经完全支持mask遮罩,且比Chrome浏览器支持的还要好。
  2. 其次,占据空间可以设置为绝对定位+pointer-events:none穿透。
  3. 然后倒影位置是死的问题现在也没有了。
  4. 最后,现在渲染很流畅。

所以,总结下来,-moz-element()只适合模拟静态倒影效果。

五、让IE浏览器也支持元素投影

现在,webkit/moz两派都登场了,那IE浏览器怎么办呢?有没有什么私有的倒影属性之类的。

在我目前所掌握的知识范畴里,没有。

但是,要想实现也是可以的。

可以使用现代web技术,SVG和canvas都是可以的。

如果使用这些技术,自然,Chrome和FireFox浏览器也是支持的。也就是,如果你想在全兼容的页面实现倒影效果,可以试试SVG或canvas,但是,有局限,只适用于图片,或文本,如果是复杂的DOM结构,理论上可以实现,但实际成本相当感人,是不划算滴。

所以,这里展示下,如何使用SVG和canvas实现图片倒影效果

1. SVG实现倒影
SVG中有真正意义的遮罩,且可以使用fill属性吧图片作为底纹填充给多个元素,理论上就实现了原图和倒影的联动效果。

还是使用鸭子图片做示意,代码如下:

<svg width="150" height="352">
  <defs>
      <linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0%"   stop-color="#fff" stop-opacity="0"/>
      <stop offset="100%" stop-color="#fff" stop-opacity="1"/>
    </linearGradient>
    <mask id="mask">
      <rect x="0" y="176" width="150" height="177" fill="url(#gradient)"  />
    </mask>
    <pattern id="reflect" patternUnits="userSpaceOnUse" width="150" height="176">
      <image xlink:href="chaowei.jpg" width="150" height="176" />
    </pattern>
  </defs>
  <!-- 原图 -->
  <rect x="0" y="0" width="150" height="176" fill="url(#reflect)"></rect>
  <!-- 倒影图 -->
  <rect x="0" y="176" width="150" height="176" fill="url(#reflect)" mask="url(#mask)" transform="translate(75 264) scale(1, -1) translate(-75 -264)"></rect>
</svg>

您可以狠狠地点击这里:SVG实现倒影效果演示demo

效果如下:

2. canvas实现倒影
关于canvas实现倒影,以前曾分享过一个JS插件,具体参见:“reflection.js-实现图片倒影效果js插件”,这篇文章是相当元老的一篇文章,发表于09年的8月份。

reflection.js是一个很简单的JS文件,很小,您可以点击这里下载之~

由于此JS文件比较老,有些地方有些out,比方说IE9支持canvas,但是,实际却是有filter去实现,是有问题的,如下处修改:

// 第65行
if (document.all && !window.opera) {
↓
if (![].map) {

reflection.js会自动对含有reflect类名的图片进行倒影处理。

您可以狠狠地点击这里:鸭子变帅后canvas倒影效果demo

使用很简单:

<img src="chaowei.jpg" class="reflect">

<script src="reflection.js"></script>
<script>
// 全身倒影
Reflection.defaultHeight = 1;
// 倒影透明度变化从0到1
Reflection.defaultOpacity = 1;
</script>

此实现同样有一些局限,例如:不支持动态变化,投影占据空间尺寸等。

六、让IE7/IE8也支持元素倒影

虽然SVG和canvas可以让IE也能实现图片的静态投影效果,但是,对于IE7,IE8浏览器怎么办?这些都不支持的。

如果非要让IE7/IE8也能实现倒影效果,也是可以的,需要使用IE的私有滤镜来模拟。

一个是flipV/flipH翻转,还有一个就是渐变滤镜(如透明到白色)覆盖图片。

关于IE的水平或垂直翻转,之前有文章介绍过:“CSS垂直翻转/水平翻转提高web页面资源重用性”。

/*水平翻转*/
.flipx {
    transform:scaleX(-1);
    /*IE*/
    filter:FlipH;
}

/*垂直翻转*/
.flipy {
    transform:scaleY(-1);
    /*IE*/
    filter:FlipV;
}

关于IE的渐变滤镜,之前也有文章介绍过:“CSS实现兼容性的渐变背景(gradient)效果”。

filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=red,endcolorstr=blue,gradientType=1);

现在,IE7,IE8浏览器的份额已经比较低了,为了这些过时的浏览器去折腾一些比较酷的视觉效果,我觉得是非常不划算的事情,个人建议是,直接就不要倒影什么的,满足基本功能是第一位。因为,人力成本也是成本,而且这些技术都是要被淘汰的,花时间掌握日后的收益怕是有些低。

所以,这里就不专门整demo展示IE7/IE8下图片倒影效果的实现了。

七、结束语

感谢-webkit-box-reflect,让鸭子找回了自信,这不,喂完鸽子回来,敢和空姐合影了。

梁朝伟喂完鸽子回来

这个故事说明了什么?

很显然,学好CSS,丑小鸭也能变成白天鹅,娶空姐,走上人生巅峰……

恩,好久没有这么一本正经地胡说八道了。左看看

feel good!

(本篇完)

分享到:


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

  1. 分享一下说道:

    父元素有背景图的话,子元素
    background-image:inherit;
    再加transform能够做到全兼容的倒影效果,滤镜渐变什么的可以随便加

  2. 小胖纸说道:

    赞赞赞赞

  3. kk说道:

    挺好挺好学习了。

  4. kail说道:

    那个demo在firefox转起来了。但是特别的卡

  5. horan说道:

    跨平台的倒影/镜像/反射/reflecting 也可以用 background-image:inherit 来简单实现:http://codepen.io/horan/pen/mPaXJg

  6. levomm说道:

    去哪里挖掘这些好玩的属性的?

  7. 说道:

    鑫哥,Android 低版本不支持 position:fixed ,怎么办呢??

  8. RedSpite说道:

    张大大好久没更博了

  9. 萌小乖说道:

    我发现你有毒。。。。开心的不要不要的

  10. 奇怪的bug说道:

    发现个坑,当有元素应用了transform:translate 且transition-duration > 0 时,box-reflect 失效。
    环境:android下的qq浏览器和微信。

  11. 深红说道:

    真心人在哪

  12. css和js改样式说道:

    张老师,如果你能看到我的问题。希望不会打扰到您。前端小白想问问。我在修改一些样式的时候 喜欢用less直接 修改一些css 或者增加一些css。但是我们组的另一个人喜欢用js去修改 样式。你怎么看 你觉得哪个更好?有什么区别。

    • 张 鑫旭说道:

      术业有专攻而已。喜欢用JS的往往CSS比较弱或不感兴趣。没什么好不不好的,看需求,看场景。有时候CSS方便,有时候JS合理。

  13. 正常的敦大侠说道:

    亲,怎么改头像啊

  14. whj说道:

    想问个问题,-webkit-box-reflect: below 0 url(shuai2.png), 这里的遮罩图是红色的,为什么倒影之后就成了透明的呢?没搞懂,张大大可否解释一下?

  15. 艾尔cc说道:

    好评 好评!哈哈!!

  16. iframe说道:

    鑫旭兄你好 想请教一下 js 怎么获取iframe 里的节点 (src的链接不是本地项目的页面,是外部链接) 如下

    如果鑫旭兄 能看到 还请不吝赐教

    • iframe说道:

      获取的时候有一个跨域问题
      那个外链的 的html 是我这边传给后台的标签 然后后台拿到 生成一个HTML文件
      我叫存数据库 他们怕访问量多了 数据库压力大 要生成一个HTML文件 传到 服务器上,然后拉取回来 在解析成标签给我 里面汉字全是 乱码

  17. 会飞的胖子说道:

    东邪西毒-0-

  18. rotate720deg说道:

    我竟然看完了

  19. 说道:

    人生的巅峰来了,准备迎娶白富美~~~

  20. 说道:

    这故事我居然看完了~~~