玩转CSS 3D -正八面体与正十二面体

 阅读约 18 分钟

正八面体与正十二面体,这两个正多面体虽然组合的面比较多,不过因为具备了对称性,所以只需要制作出一半的结构,另外一半再用反转的方式接在一起即可。

正八面体

正八面体可以想像成“两个金字塔”叠合在一起,为了方便我们后续作整个金字塔旋转的动作,我们要用另外一个容器来把金字塔包装起来,可以想像成把金字塔的四个面变成一个群组,就可以针对这个群组做变形或移动,HTML的结构如下,space里面有box1和box2,box1是上半部的金字塔,box2是下半部的金字塔:

<div class="camera">
    <div class="space">
        <div class="box1">
            <div class="face1"></div>
            <div class="face2"></div>
            <div class="face3"></div>
            <div class="face4"></div>
        </div>
        <div class="box2">
            <div class="face1"></div>
            <div class="face2"></div>
            <div class="face3"></div>
            <div class="face4"></div>
        </div>
    </div>
</div>

再来同样先针对camera和space做设定。

.camera{
    width:200px;
    height:200px;
    -webkit-perspective-origin:50% 50%;
    -webkit-perspective:500px;
}
.space{
    position:relative;
    width:100%;
    height:100%;
    border:1px dashed #000;
    -webkit-transform-style:preserve-3d;
}

然后因为我们用到的面face都是三角形,同样要用border来达成,记得position要设定为absolute,并且由于box1,box2本身内部也是立体空间,所以同样要加上transform-style: preserve-3d的属性。

.space div{
    position:absolute;
}
.box1, .box2{
    transform-style:preserve-3d;
}
.box1 div,.box2 div{
  width:0;
  height:0;
  border-width:0 50px 87px;
  border-style:solid;
  opacity:.4;
}

再来制作box1的金字塔造型,由于正八面体每个面夹角为109.28度,又因为分为上两半,从中间差开,一半为54.64度,因此我们要旋转的角度是:90 - 54.64 = 35.36,所以待会我们要旋转的角度就以此为主,先看到face1,顺着x轴往内转35.36度。

.face1{
    border-color:transparent transparent #f00;
    transform-origin:center bottom;
    transform:translateX(50px) translateY(50px) rotateX(35.36deg);
}

图片描述

face2则要先以右边为圆心,绕Y轴旋转90度,之后再绕X轴旋转35.36度。

.face2{
    border-color:transparent transparent #00f;
    transform-origin:right bottom;
    transform:translateX(50px) translateY(50px) rotateY(-90deg) rotateX(-35.36deg);
}

图片描述

face3在face1的对面,所以先在Z轴移动100px,接着再绕X轴旋转35.36度。

.face3{
    border-color:transparent transparent #0f0;
    transform-origin:left bottom;
    transform:translateX(50px) translateY(50px)  translateZ(-100px) rotateX(-35.36deg);
}

图片描述

face4跟face2雷同,只是face4用左侧为圆心。

.face4{
    border-color:transparent transparent #f90;
    transform-origin:left bottom;
    transform:translateX(50px) translateY(50px) rotateY(90deg) rotateX(-35.36deg);
}

图片描述

基本上到这边,已经完成了上半部box1的金字塔造型,这时后的box1与box2是重叠的,我们只要将box2反转并改变位置,就可以变成一个正八面体的造型,但这里会遇到一个很有意思的问题,box2变形的中心点在哪里?这时候就必须用到好几次的三角函数计算,因为是一个金子塔造型旋转的中心点,所以就要把Z轴考察进去,首先看到X轴设定为center是没问题的,Z轴因为金子塔的底部是100 x 100的正方形,所以中心点在50px的位置也很正常,但Y轴就比较麻烦,首先我们看到正三角形的一个边长为100px,中线的长度就是sin(60)x 100 = 86.6左右,再来用sin(54.64)x 86.6就得到金子塔的高度70.6,四舍五入一下就得到了71,因此当我们将变形中心设定在这边,旋转的时候就会绕着金子塔顶旋转,旋转180度之后,就要利用translateY来归位,要移动的距离约为100 + 71/2 = 135左右,但因为从一开始我们就都是用四舍五入的方式进行,难免最后用整数表现会有误差(两个金字塔接不起来),这时就必须要用手动微调,这里设定为-132px即可。

.box2{
      transform-origin: center 71px -50px;
      transform: rotateX(180deg) translateY(-132px);
}

图片描述

同样的,旋转space让整个正八面体旋转,看起来更有立体感。

.space{
    position:relative;
    width:100%;
    height:100%;
    -webkit-transform-style:preserve-3d;
    -webkit-transform-origin:center center -50px;
    -webkit-animation:s 4s linear infinite;
}
@-webkit-keyframes s{
    0%{
        -webkit-transform:rotateY(0);
    }
    100%{
        -webkit-transform:rotateY(-359.9deg);
    }
}

图片描述

正十二面体

会作正八面体之后,正十二面体差不多也是同样的原理,只是正十二面体更加复杂,因为它是由十二个正五边形组成,不过这里要从正五边形的五个边去长出五个面,每个面的夹角为116.56度,180 - 116.56 = 63.44,待会用到的角度大概不会脱离这两个数值,当然因为有很多小数点,所以届时还是必须手动微调(因为像素最小单位是1)。

HTML的结构如下,也是分成两块,内容各有六个面。

<div class="camera">
    <div class="space">
        <div class="box1">
            <div class="face1"></div>
            <div class="face2"></div>
            <div class="face3"></div>
            <div class="face4"></div>
            <div class="face5"></div>
            <div class="face6"></div>
        </div>
        <div class="box2">
            <div class="face1"></div>
            <div class="face2"></div>
            <div class="face3"></div>
            <div class="face4"></div>
            <div class="face5"></div>
            <div class="face6"></div>
        </div>
    </div>
</div>

先设定一开始的CSS。

.camera{
    width:200px;
    height:200px;
    perspective-origin:50% 0%;
    -moz-perspective-origin:50% 0%;
    -webkit-perspective-origin:50% 0%;
    perspective:500px;
    -moz-perspective:500px;
    -webkit-perspective:500px;
}
.space{
    position:relative;
    width:100%;
    height:100%;
    border:1px dashed #000;
    transform-style:preserve-3d;
    -moz-transform-style:preserve-3d;
    -webkit-transform-style:preserve-3d;
}
.space div{
    position:absolute;
}
.box1, .box2{
    transform-style:preserve-3d;
    -moz-transform-style:preserve-3d;
    -webkit-transform-style:preserve-3d;
}

再来是慢慢的画每个面,首先是画出所有的正五边形。

.box1 div,.box2 div{
  width:162px;
  height:154px;
  opacity:.9;
}
.box1 div:before,.box2 div:before{
  position:absolute;
  content:"";
  top:0;
  width:0;
  height:0;
  border-width:0 81px 59px;
  border-style:solid;
  border-color:transparent transparent #069;
}
.box1 div:after,.box2 div:after{
  position:absolute;
  content:"";
  top:59px;
  left:0;
  width:100px;
  height:0;
  background:none;
  border-width:95px 31px 0;
  border-style:solid;   
  border-color:#069 transparent transparent;
}

图片描述

因为是要由正五边形的五个边往外长五个面,所以face1就不做更动,届时再让box做旋转即可,而face2到face6这五个面,比较简单的做法就是直接先做Z轴旋转的动作,然后再进行Y轴旋转,再把各个面translate到对应的位置,不过这里要非常注意,因为我们先进行了Z轴与Y轴的旋转,所以各个面的坐标系统已经改变,可以参照下图,就可以明白该如何translate,基本上就是在Y轴旋转了-116.56度之后,各个面先朝向自己的Y轴移动69px,然后为了让各个边贴齐,必须要再往Y轴移动31px(cos(180-116.56)x 69),往Z轴移动62px(sin(180-116.56)x 69)。
图片描述

因此face2到face6就可以几乎用完全一样的方式写出来(Z轴旋转角度每个面差72度)

.face2{  
    transform-origin:81px 85px 0;
        transform:rotateZ(0) rotateX(-116.56deg) translateY(-69px) translateY(-31px) translateZ(62px);
}
.face3{  
        transform-origin:81px 85px 0;
        transform:rotateZ(72deg) rotateX(-116.56deg) translateY(-69px) translateY(-31px) translateZ(62px);
}
.face3{  
        transform-origin:81px 85px 0;
        transform:rotateZ(72deg) rotateX(-116.56deg) translateY(-69px) translateY(-31px) translateZ(62px);
}
.face4{
        transform-origin:81px 85px 0;
        transform:rotateZ(144deg) rotateX(-116.56deg) translateY(-69px) translateY(-31px) translateZ(62px);
}
.face5{  
      transform-origin:81px 85px 0;
      transform:rotateZ(-144deg) rotateX(-116.56deg) translateY(-69px) translateY(-31px) translateZ(62px);
}
.face6{  
        transform-origin:81px 85px 0;
      transform:rotateZ(-72deg) rotateX(-116.56deg) translateY(-69px) translateY(-31px) translateZ(62px);
}

为了让每个面不同颜色,这里必须要改变伪元素的border色彩。

.box1 .face2:before{
  border-color:transparent transparent #f00;
}
.box1 .face2:after{
  border-color:#f00 transparent transparent;
}
.box1 .face3:before{
  border-color:transparent transparent #0f0;
}
.box1 .face3:after{
  border-color:#0f0 transparent transparent;
}
.box1 .face4:before{
  border-color:transparent transparent #f90;
}
.box1 .face4:after{
  border-color:#f90 transparent transparent;
}
.box1 .face5:before{
  border-color:transparent transparent #09f;
}
.box1 .face5:after{
  border-color:#09f transparent transparent;
}
.box1 .face6:before{
  border-color:transparent transparent #f0f;
}
.box1 .face6:after{
  border-color:#f0f transparent transparent;
}

图片描述

最后就针对box1与box2做旋转的动作,这里比较需要注意的是translateZ,整个正五边形的高度为276(sin(63.44)x 154 x 2),但位移的时候并非完全是这个高度,必须扣掉接合处短边的高度,因此会变成223(276 - sin(36)x 100)

.box1{
    transform-origin:81px 85px 0;
    transform:rotateX(90deg) translateZ(-223px);
}
.box2{
    transform-origin:81px 85px 0;
    transform:rotateX(-90deg);
}

图片描述

space加上动画效果,验证一下每个面是否都有接合的完美。

.space{
    position:relative;
    width:100%;
    height:100%;
    -webkit-transform-style:preserve-3d;
    -webkit-transform-origin:81px 170px 0;
    -webkit-animation:s 4s linear infinite;
}
@-webkit-keyframes s{
    0%{
        -webkit-transform:rotateY(0) rotateX(0);
    }
    100%{
        -webkit-transform:rotateY(-359.9deg)  rotateX(359.9deg);
    }
}

图片描述
图片描述

阅读 479发布于 4月18日
推荐阅读
CSS技术学习
用户专栏

CSS技术学习

438 人关注
54 篇文章
专栏主页
目录