技术控

    今日:126| 主题:49289
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] CSS 3D应该注意的事项

[复制链接]
い明 媚 发表于 2016-10-3 15:00:58
150 1

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
我一直喜欢3D。我也开始使用CSS 3D Transform,而且浏览器对它的支持度越来越好。但给我的感觉,使用Transform就是用来创建2D图形,并且使用旋转和位移可以创建一些3D图形。但在实际使用的时候,还是越到了不少的麻烦,而且这些麻烦出乎我的意料。我想或许大家也同样遇到过这样的问题,为了大家在使用CSS 3D Transform能避免这些麻烦,我把我碰到的与在大家分享一下。
  3D渲染上下文

  我清楚的记得一个晚上遇到一个麻烦,而且这个麻烦引起我的好奇心,于是我想亲自写一个测试用例来试试看,看看浏览器如何处理平面的交叉。该测试用例只包含了两个平面元素:
  [code]

[/code]   它们尺寸大小相同,并且使用绝对定位,让元素在屏幕中居中(水平垂直居中),并且给他们设置了个背景色,让它们在屏幕中可见:
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}[/code]   把 body 元素当作一个场景,并且设置了 perspective (视角),让视角覆盖整个视窗。视角的值越大,元素似乎看起来离自已越远,看起来越小,反之越大。
  [code]body {
  margin: 0;
  height: 100vh;
  perspective: 40em;
}[/code]   测试示例实际上就是测试两个平面相交,所以使用Transform中的 rotateY() 做了一个 Y 轴旋转,并且设置了一个不同的背景颜色:
  [code].plane:last-child {
  transform: rotateY(60deg);
  background: #d14730;
}[/code]  结果是令人失望的。浏览器似乎并没有正确的处理好两个平面交叉:
   事实上是我错了,造成这个现象是我的代码导致的。我应该做的是让两个平面在 3D上下文渲染 。3D上下文渲染和 层叠上下文渲染 还是不同的。就像如果它们不在同一个层叠上下文中,我们不能使用 z-index 来改变元素在 z 轴的顺序。同样的,如果不在同一个3D渲染范围内,3D Transform同样不能改变元素的顺序,让元素交叉。
  为了确保这两个平面是在相同的3D渲染环境,最简单的方法就是把它们放在同一个容器中:
  [code]

   

   
   
[/code]   同样让容器元素通过绝对定位,让它放置在容器中间,同时给它设置一个 transform-style:preserve-3d :
  [code]div {
    position: absolute;
}

.assembly {
    top: 50%;
    left: 50%;
    transform-style: preserve-3d;
}[/code]  这样就解决了这个问题:
  如果你使用Firefox查看上面的示例,你看到的效果是两个平面并没有交叉,这或许是Firefox独有的特性吧,但你使用Webkit内核浏览器或者Edge浏览器看到的效果是我们想要的效果。
   
CSS 3D应该注意的事项-1 (background,absolute,position,水平垂直,浏览器)

  上图演示是前面示例的效果,两个平面没有交叉。
   现在你可能会感到非常奇怪,为什么不给这两个平面添加一个容器,不在场景中设置 transform-style:preserve-3d 就不能正常工作(在我们的示例中是 body 元素)?那么,在这种特殊情况下,如果在最初的示例中增加这个规则(在 boyd 元素是直接添加 transform-style:preserve-3d ),它就能正常工作(除非你是在Firefox浏览器查看这效果,正如前面所说,Firefox对于3D的顺序和交叉还是有问题的)。
   实际上,如果我们想要在网页上使用3D,我们的场景可能不会直接是 body 元素,可能会在场景中添加其他属性,这些可能会影响页面的性能。
  打断3D(造成压扁)

   比方说,我们的场景是页面中的一个 div 元素,而且有其他的内容围绕着这个场景 div :
   在第二个元素上多添加了几个 transform 的属性,使之更加明显,看上去有些部分在场景外。上面示例看到的效果是我们不想要的。我们希望添加一些属性让文本更好的阅读。
  overflow

   最初让我想到的解决方案是在场景中添加一个 overflow:hidden 。然而,这样做是能让文本更好的阅读,但3D交叉效果又不正常了。
   即使在场景中设置了 preserve-3d ,只要设置了 overflow 的值,就算是 visible 也会使效果变得像 transform-style 设置了 flat (压平)一样。因此,多添加一个容器可能会让我写更多一点代码,但能让我们绕开这些麻烦。
  这就是为什么我们要把一切元素都放置在一个独立的元素中,把这个容器当作场景,即使该元素不使用3D Transform。例如下面这个示例:
   所有旋转的六边形列都放在 .helix 元素内:
  [code]

   

        
   

   
[/code]   除了保证整个组件绝对定位在视窗中间的样式之外, .helix 元素没有任何其他样式(除非是继承下来的样式),并且所有的列都在同一个3D渲染范围内:
  [code]div {
    position: absolute;
    transform-style: preserve-3d;
}

.helix {
    top: 50%;
    left: 50%;
}[/code]   在场景中设置了 overflow:hidden (这个示例 body 元素就是场景),这样做是因为六边形不依赖于视窗的大小,我不知道它们是否会延伸到容器外而导致滚动条的出现,这种现象是我不想看到的。
   我承认我不止一次碰到这样的现象,从中吸引了相关教训。我保守起见,为了不让溢出出现在这里,直接使用 overflow:hidden ,这样能让溢出看起来不明显。
   transform-style:preserve-3d 告诉浏览器 设置了3D Transform的子元素不应该在他们的父元素内拍平(元素上设置了 transform-style:preserve-3d )。因此,就算是直觉,场景中设置了 overflow:hidden ,防止其子元素打破他们的父容器,也不会让3D元素在场景内拍平。
  但有时一个3D Transform子元素仍然可以在其父容器是平面的。比如说下面这种情况,我们一张具有两面的卡片:
  [code]

   
front

   
back

[/code]   通过绝对定位让所有元素在场景中(这个示例场景指的是 body 元素)水平垂直居中,并且给这两个卡片具有相同的大小尺寸。为了让这们在相同的空间,给卡片设置 transform-style:preserve-3d 。为了让背面可见,设置 backface-visibility:hidden ,并且在第二张卡片上设置 rotate ,让其沿着垂直轴( Y 轴)旋转半圈( .5turn ):
  [code]$dim: 40vmin;

div {
    position: absolute;
    width: $dim;
    height: $dim;
}

.card {
    top: 50%;
    left: 50%;
    margin: -.5*$dim;
    transform-style: preserve-3d;
}

.face {
    backface-visibility: hidden;
    background: #ee8c25;

    &:last-child {
        transform: rotateY(.5turn);
        background: #d14730;
    }
}[/code]  示例的效果如下所示:
   两张卡片在他的父容器内仍然是平的,只不过第二张卡片绕着它的垂直轴( Y 轴)旋转了半圈。它的朝向是相反的方式,但仍然是在同一平面上。到目前为止,这一切看上去都是正常的。
   好,现在我想这两个卡片不是矩形的。给它们一个 border-radius: 50% 。但看起来没有任何变化:
   接下来在 .card 上设置 overflow:hidden ,效果就正常了:
   哎呀,这打破了我们的3D卡片。既然我们无法做到这一点,我们就在 .face 上设置:
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}0[/code]  在这种情况之下,解决这个问题的方法比打破3D卡这个问题更简单。但是,如果我们想要的是另一种形状,比如说一个正八边形,正八边形还是很容易实现的,比如采用两个元素(或者元素和伪元素的配合):
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}1[/code]   给这两个元素设置相同的尺寸,并且 .inner 元素设置 rotate 的值为 45deg ,给他们设置一个背景色,然后在 .octagon 元素上设置 overflow:hidden ,就可以看到一个正八边形:
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}2[/code]  你看到的八边形效果如下所示:
   如果你对如何制作正多边形感兴趣,建议你阅读《Sass绘制多边形》和《 單一 div 的正多邊形變換 ( 純 CSS ) 》。
  如果希望在正多边形中添加文本呢?
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}3[/code]  你将看到的效果是这样,不尽人意:
   造成这个现象是因为裁角的时候把文本也裁剪掉了。为了让文本能正常显示,给它设置一个 text-align:center ,并且设置一个 line-height 的值等于 .octagon 的高度(或者 .inner ),让文本垂直居中:
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}4[/code]   现在看起来好多了,但文本仍然是旋转的,因为 .inner 元素设置了一个 rotate(45deg) :
   为了解决这个问题,只需要在 .octagon 元素上增加一个 rotate ,其旋转的角度值和 .inner 的旋转值一样,只是旋转方向刚好与 .inner 元素相反,所以是一个负值:
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}5[/code]  这下,文本在正八边形中的文本显示正常了:
   现在让我们看看,如果我们想一正八边形的卡片,又将如何应用它。我们不能直接在 .card 上直接运用 overflow:hidden (让它在 .octagon 元素上作用,同时两个面 .inner 又会是什么样)。因为在卡片上设置了 overflow:hidden 样式,根据前面介绍的内容,这样就会打破3D空间,让两个页不在同一个3D渲染环境。
   替代方案是,需要把这些规则用在 .octagon 元素,并且使用它们的伪元素来做卡片的两个面:
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}6[/code]  最后看到的效果就是我们想要的效果:
  clip-path

   能引起类似的问题还有另一个属性 clip-path 。回到上面卡片的示例,我们不能在 .card 元素上直接使用 clip-path 来绘制三角形,因为我们需要一个3D Ttransform的子元素,也就是第二个面。我们应该用在卡片的面上:
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}7[/code]   注意: clip-path 属性在Webkit内核浏览器下还是需要添加 -webkit- 前缀。对于Firefox(47+)浏览器需要通过 about:config 将 layout.css.clip-path-shapes.enabled 设置为 true ,另外Edge是不支持这个属性的(但你可以在这里 进行投票 ,让Edge早日能支持这个属性)。
   如果您从未接触过 clip-path 这个属性,建议你先阅读《  CSS的 clip-path  》一文进行了解。
  上面的代码看到的效果应该是这样的:
   虽然不存在3D的问题,但效果看起来真的很别扭。如果从正面观察卡片,三角形的顶角是朝右的,按理说,后面应该是朝左的。但效果并不是我们所期望的,反面也朝右了。要解决这个问题,那么需要为不同的面设置不同的路径。 clip-path 绘制正面的三角形超右,绘制反正的三角形超左。
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}8[/code]  下面看到的效果才是我们想要的:
   注意:还需要修改 text-align 的值:正面的默认值为 left ,反正的就需要设置为 right 。
   另外,我们还可以在反面是使用 scaleX(-1) 来实现。如查你想进一步的了解 scale 的工作机制,可以看看下面的示例:
  将上面介绍的原理运用到我们前面介绍的DEMO中:
  [code]$dim: 40vmin;

.plane {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -.5*$dim;
  width: $dim;
  height: $dim;
  background: #ee8c25;
}9[/code]  效果如下:
   这样看起来三角的方向是对了,但文字又出问题了。这意味着我们实际上要把文本放在背景元素的伪元素上,并且在 .face 元素上做一个反转。 scale 的反转就是设置另一个 scale 是 1/f 。在我们这个示例中, f 就是 -1 。也就是说在伪元素上设置 scale 的值为 1/-1=-1 ,就可以让文本看起来正常:
  [code]body {
  margin: 0;
  height: 100vh;
  perspective: 40em;
}0[/code]  最后的效果如下:
   如果 mask 设置非 none 值时,也会致使 transform-style 变为 flat ,就像 overflow 和 clip-path 设置了 visible 和 none 以外的值一样。
  opacity

   这是一个意想不到的问题。相对而言,这也是 规范中相对较新的一个变化 ,如果在3D渲染环境下设置的 opacity 值小于 1 ,效果就像是在层叠上下文一样(失去3D渲染上下文,就像前面所说的3D拍平)。这效果并不是在所有浏览器中都会发生的,比如在Edge、Safari和Brave下正常,而Chrome、Firefox和Opera看到的效果就是拍平后的效果。
  请看下面的示例,一组立方体在3D空间内同时旋转:
   结构很简单,在 .assembly 容器内有很多个(这个示例是有 20 个) .cube 元素,而且每个 .cube 有 6 个面。
  [code]body {
  margin: 0;
  height: 100vh;
  perspective: 40em;
}1[/code]  现在我们说,想要的立方体是半透明的。那么我们这样做,能不能做到呢?
  [code]body {
  margin: 0;
  height: 100vh;
  perspective: 40em;
}2[/code]   这样一来,就算在 .cube 上设置了 transform-style:preserve-3d ,也会变成 flat 的效果,就像 .cube 在它的父容器里被拍平了。现在只是Chrome、Opera和Firefox,但是在将来,所有浏览器都会是这样:

CSS 3D应该注意的事项-2 (background,absolute,position,水平垂直,浏览器)

   在Brave、Edge和Safari中,设置 opacity 值小于 1 时,立方体没有拍平。
1234下一页
友荐云推荐




上一篇:MariaDB 10.2 Beta
下一篇:Optimizing parallel HTTP request batches
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

izxyc 发表于 2016-10-7 02:14:51
い明 媚高端大气上档次,鉴定完毕
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表