Safari 3D transform变换z-index层级渲染异常的研究

这篇文章发布于 2016年08月7日,星期日,21:51,归类于 CSS相关。 阅读 76983 次, 今日 9 次 35 条评论

 

一、Safari是新时代的IE6

在2年前介绍currentColor变量的时候就提过Safari的问题,就是伪元素hover时候的currentColor不渲染,

像这种IE浏览器都可以正常渲染的CSS,Safari居然出现各种匪夷所思的问题,对的,是各种,而且全都是与渲染相关的,这也难怪为什么Safari浏览器被称为“新时代的IE6”了!

补充于2016-08-09
Safari font-size的px单位和vm单位计算不支持,需要是百分比单位,可参见这篇文章
Safari 渐变,从#fff到transparent会有灰色带,其他浏览器都是白色到透明。

或许,很早的时候,Safari和IE有段暧昧不清的过往(看下图的攻受表情):
Safari和IE

本文即将介绍的渲染问题,也是仅Safari浏览器才有的,是我同事遇到的,我觉得很有意思,忍不住拿来和大家分享下。

二、Safari 3D变换会忽略z-index的层级

在Safari浏览器下,此Safari浏览器包括iOS的Safari,iPhone上的微信浏览器,以及Mac OS X系统的Safari浏览器,当我们使用3D transform变换的时候,如果祖先元素没有overflow:hidden/scroll/auto等限制,则会直接忽略自身和其他元素的z-index层叠顺序设置,而直接使用真实世界的3D视角进行渲染。

我们直接看例子,如果您现在用的是iMac或air或iPad或iPhone之类的苹果设备浏览本文,您可以狠狠地点击这里:Safari浏览器下3D transform和z-index层级渲染demo

如果高度不够页面滚动,请双指放大页面比例,让图片和红色条子(fixed定位)发生重叠,就会看到很有趣的渲染效果。

如果您是window系统的浏览器上浏览本文,但是手上有iPhone,也可以使用微信等app扫描访问:

demo页面二维码

不出意外,滚动页面,会看到类似下面这样的渲染效果:

渲染异常显示截图

会看到,红色的块状条子,从图片中心穿过去了。实际上,这个红色条子是层级99的position:fixed定位的元素:

.bar {
    position: fixed; 
    /* Safari下z-index无效 */
    z-index: 99;
}

而图片就是一个小白图片,没有定位属性的设置,就一个简单的带有视角的3D旋转变换:

img {
    transform: perspective(300px) rotateY(40deg);
}

按照CSS规范上的说明,红色条子应该在图片上面,类似下面这样:

Chrome浏览器下截图

IE, Chrome, FireFox都是遵循这种渲染的,但是,Safari浏览器却自己任性了一把。直接把z-index:99给无视了,对无视了,在座的诸位也不要怀疑是不是99还不够大,就算是9999999这是这般渲染,因为Safari是忽略z-index,而不是IE6,IE7那种z-index计算bug.

根据我自己的理解,Safari的这种渲染或许并不能直接称之为bug, 因为,从某些角度讲,Safari的这种渲染挺符合符合现实3D世界。

我自己YY了一下,Safari如果没有overflow的限制,就会把2次元页面变成真实的3次元,原本图片和红色条子在一个面上,当图片进行了3D旋转,那自然红色条子就从中心穿过,而且视角背后的内容是看不见的。

算了,别继续开脑洞了,来看看这个问题该如何解决吧~~

三、Safari 3D变换会忽略z-index问题解决

方法1:
父级,任意父级,非body级别,设置overflow:hidden可恢复和其他浏览器一样的渲染。

方法2:
以毒攻毒。有时候,页面复杂,我们不能给父级设置overflow:hidden,怎么办呢?

杨过的情花剧毒怎么解的?断肠草啊,另一种剧毒。这里也是类似。既然“穿越”的渲染问题是由3D transform变换产生的,那么,要解决此问题,我们也可以使用3D transform变换。

那具体该如何做呢?

我在“好吧,CSS3 3D transform变换,不过如此!”一文中就科普过z轴的概念。

我们仔细观察下面这张效果截图:

渲染异常显示截图

我们的红色条子在z轴位置0处,对不对,所以才从图片的中心穿过。而z轴是我们眼睛看屏幕这条轴,在z轴的值越大,就离用户的眼睛越近;值越小,里用户眼睛越小。所谓近大远小(如果指定了视角perspective),就是这么回事。

网页中z轴示意

所以,我们要想让红色条子覆盖在图片上,只要设置一个足够大的translateZ值就可以,如100px

.bar {
    position: fixed;
    z-index: 99;
    /* 以毒攻毒 */
    transform: translateZ(100px);
}

结果:
translateZ(100px) 的效果截图

咦,尴尬,好像还是不够大,图片还有一点点位置在红色条的上面~~

赶快用120px试试:

.bar {
    position: fixed;
    z-index: 99;
    /* 以毒攻毒 */
    transform: translateZ(120px);
}

结果:
translateZ(120px)效果截图

喔噢,这下图片完全被红色的长条子覆盖了,这下,所有浏览器的表现都是一模一样的啦!

四、结束语

本周末2天任务完成,就是写3篇文章。我现在写作习惯和以前相比有了一点变化,以前是想到内容就下笔写,现在不是,都是先拿个小本子记录,这个技术点有必要整理下,那个有必要整理下。然后,几篇一起集中攻略。因为这样效率更高。

因为写着写着就high了,就能够趁着余热继续写下面的文章内容。把大段的时间用来做同一件事件,可以有效避免频繁切换状态的精力损耗。

话说今天10点不到就可以手工,主要归功于下午看电影计划取消了,主要还是没有什么特别靠谱的电影。昨天嘛,上午钓鱼,下午逛母婴室和超市,哦,是陪逛母婴室和超市。也算交代了,自己开心了半天,夫人也开心了半天。

接下来3个星期,就专注于另外一件事情,希望可以看到明显的进度,aza! 加油!

(本篇完)

分享到:


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

  1. yuicer说道:

    感谢?

  2. psilo说道:

    谢谢,帮了大忙

  3. Panda说道:

    用translateZ以毒攻毒的解决方案的时候,作者似乎忘了说,元素的显示尺寸也会根据“近大远小”的3D策略变得超出预期……

  4. MrD说道:

    谢谢啊喂

  5. 韩曜年说道:

    解决了我的大问题,天啊!

  6. 西赛德李说道:

    太牛逼了!我搞了大半天的问题一下子就解决!!!!旭神牛逼!!!!

  7. 陈妥妥说道:

    overflow: hidden !!!!
    解决了我的问题 感谢!!!!

  8. lalala说道:

    这都能想到 牛批 感谢大神 么么哒

  9. hhacker说道:

    这个问题真的是让人相当无语,我最后的选择是没用position:fixed了

  10. Noly1990说道:

    遇到在ios微信端小程序出现这种情况,在fixed元素上加上transform: translateZ进行处理,达到目的,后续观察,感谢

  11. 无效啊说道:

    按着以上的编辑 父元素加了overflow无效 需要在顶层的元素加了translateZ依旧无效,是不是这片文章遗漏了什么

  12. 碰到一个bug说道:

    a用了css3:animation -webkit-transform:rotateY(360deg); 然后b原本是fixed,点击js改成absolute,不会随着页面滑动,层级也会在a元素后面,只有在安卓的一个低版本会出现这个问题

  13. Deboy说道:

    灵魂画师张鑫旭 哈哈
    同事遇到这个问题 我给他提了第二种方案 但是觉得不合理 搜索到了这篇文章
    尝试了第一种 发现 他是为了加阴影 overflow 了之后阴影跟着没了 于是只能继续第二种…

  14. Emo说道:

    可以试试负数的z-index…

  15. anne说道:

    window下的safari还是不行的

  16. anne说道:

    好像没有解决

  17. 豆豆说道:

    膜拜大神,pc端和安卓都可以,就是ios不行,找了半天是层级问题,就搜到这篇文章,赞赞赞

  18. Otto说道:

    神奇!在pm的mac电脑上有问题,怎么搞都搞不定,也不好调试。幸好看到这篇文章。

  19. superman说道:

    举报这货【宋健】,抄袭了你的这篇文章并重新发布,还设了打赏,太不要脸。
    大家怼他!
    文章链接:http://www.joycesong.com/archives/1082

  20. mochase说道:

    太牛逼了,这两天做3d变换测试safari时也碰到了这个问题,在这找到了解决方案.膜拜膜拜

  21. 焦贵彬说道:

    太牛逼了!怎么找到的解决方案!膜拜

  22. 111说道:

    rotateZ(0)也可以解决

  23. Jame说道:

    真是帮我大忙了,来,我膜拜你一下

  24. 狐狸说道:

    奇淫巧技啊

  25. PRebrith说道:

    在 img的父层设置一个有意义的perspective 可以解决这个问题
    原因未知

  26. lalala说道:

    Safari 渐变,从#fff到transparent会有灰色带
    关于这个,大概是因为safari的transparent是rgba(0,0,0,0);
    如果设置#fff 到rgba(255,255,255,0)就没有灰色带
    同理,如果设置别的颜色如rgba(54,139,248,1),也会有别的颜色带

  27. 草草说道:

    用Safari看看最下面的翻转动画

  28. 喵呜说道:

    卧槽 , 谢谢 !!!!!!
    太牛啦,我弄了两天
    alert(“NB”);

  29. DRIZZLE说道:

    厉害,我其实想说的是,你的画风没有进步啊。

  30. hymin说道:

    解决方案2有个问题, 在chrome下观察, translate-z似乎是以图片自身旋转后的Z轴进行translate. 也就是说, 如果设置图片的rotateY(90deg), 那么translate-z只会把图片在视角空间内进行左右移动, 而不会把图片向用户进行前后移动了…

  31. 陆斌说道:

    原来如此,我说之前遇到类似的bug,当时是没有解决了,换了方法。现在来看,8成就是它了

  32. 粉丝说道:

    我这看的怎么没事,Safari上看的。

  33. 我是忠实粉丝说道:

    666

  34. 馒头说道:

    safari 最IE6 的是 还没有开发 service worker

  35. RedSpite说道:

    高手碰到bug 就是像饿虎一样得扑过去了~