马黑黑 发表于 2023-2-3 11:49

再谈六面体

本帖最后由 马黑黑 于 2023-2-3 20:31 编辑

多面体中,六面体我们已经探讨了不少,因为它是立体几何中最基础的,弄懂它其他的多面体就不再是难题。

在HTML里,实现六面体首先需要3D视界,它实际上相当观察者的眼睛,以及,眼睛离被观察对象的纵深空间:

      <div id="眼睛">
                其他元素代码
      </div>

对应纵深的相关属性要在CSS中设置:

      #眼睛 {
                perspective: 1000px;
                perspective-origin: 50% 40%;
      }

perspective属性就是景深,指眼睛到被观察对象间的空间距离,等同于眼睛和荧屏里的被观察3D对象的距离,属Z轴指向的方向。这个景深是客观存在的也是虚拟的,说它客观存在,因为电脑使用者的眼睛的确与荧屏事实存在一定距离,说它虚拟,是因为被观察3D对象在荧屏里它真实只存在于屏幕表面,它在Z轴上离观者眼睛的远近是依靠算法虚拟出来的。CSS还能虚拟出观者的角度,就是 perspective-origin 所干的事,由它模拟出被观察对象的视点和视面,上例中,观者眼睛的角度在X轴的中央(50%)、在Y轴中心往上10个百分点(40%),有略微俯视的意味。

被观察对象需要活动场景,即舞台(想象一下兔子跳钢管舞或做单杠运动的空间),这个舞台,也是由HTML的一个div或其他相应元素标签来营造:

      <div id="眼睛">
                <div id="舞台">
                        其他元素代码
                </div>
      </div>

我们需要这个舞台要能呈现出3D风格,因此需要通过CSS来定义该风格属性:

      #舞台 {
                transform-style: preserve-3d;
      }

如果我们不设置 transform-style 为 preserve-3d,以及该属性缺省时,舞台所能呈现的是 flat(平面)风格,没有立体效果。

现在,我们有了观者(眼睛)和舞台,我们花了钱去剧场,不能只欣赏空空荡荡的舞台吧?因此,演员,要登场表演,这就是3D对象,它在HTML代码流里,也是由 div 或其他元素标签去实现的:

      <div id="眼睛">
                <div id="舞台">
                        <span class="演员">前面</span>
                        <span class="演员">背后</span>
                        <span class="演员">左侧</span>
                        <span class="演员">右侧</span>
                        <span class="演员">顶部</span>
                        <span class="演员">底部</span>
                </div>
      </div>

舞台节目虽然有单口相声和独角戏,可是更多的节目是群体节目,演员不止一个,为了方便设置,我们上面的代码,演员用class而不是id来表明身份,总共六位(请想一想快递盒子有多少面)。这些演员,有共性特征,我们用CSS来描述这些相同的属性:

      .演员 {
                position: absolute;
                width: 100%;
                height: 100%;
                display: grid;
                place-items: center;
                font: normal 2em arial sans-serif;
                opacity: .85;
      }

上面代码中,每个面板的宽高都是100%,它们继承上层父盒子即舞台的尺寸,也可以根据需要设置合乎自己需求的值。接下来我们来处理第一块,即面对我们的那一块。我们打算,① 把它往我们眼睛的方向即在Z轴方向拉过来一点,距离是其长度的一半,这么做是为了其余多数面板不用额外挪动Z轴方向的位置;② 给它一个背景颜色,以便方便观察效果:

      .演员:nth-of-type(1) {
                background: teal;
                transform: translateZ(150px) rotateY(0deg);
      }

第二块,就是背后那一块,自然要往后拉相同的距离,背景颜色也给一个(后面的面板也都设定不同的背景色):

      .演员:nth-of-type(2) {
                background: tan;
                transform: translateZ(-150px) rotateY(0deg);
      }

以上前、后两块面板都不需要旋转,因为它们正对着我们,面板是与Z轴垂直的。

第三、四块在左、右侧,它们要沿Y轴旋转90度(转个身),旋转之后它们与X轴垂直,且在X轴的中心点,所以要各自往左、往右挪动其长度的一半的距离:

      .演员:nth-of-type(3) {
                background: orange;
                transform: translateX(-150px) rotateY(90deg);
      }

      .演员:nth-of-type(4) {
                background: pink;
                transform: translateX(150px) rotateY(90deg);
      }

第五、六块分别在顶部和底部,他们需要沿X轴旋转90度(躺平),旋转后与Y轴垂直,它们此时在Y轴的中心点,需要各自往上、往下挪动自身长度一半的距离:

.演员:nth-of-type(5) {
      background: purple;
      transform: translateY(-150px) rotateX(90deg);
}

      .演员:nth-of-type(6) {
                background: yellow;
                transform: translateY(150px) rotateX(90deg);
      }

这样,这六块面板就构成了一个漂亮的快递盒子,不过,由于角度问题,我们看到的不是很真切,要解决这个问题,我们有三种方案:其一,让它沿XYZ轴旋转起来,其二,改变眼睛的视点(perspective-origin),其三,让舞台旋转那么一丢丢。我们采用第三种方法,用静态的方式观察快递盒子的美:

#舞台 {
      position: relative;
      width: 300px;
      height: 300px;
      transform-style: preserve-3d;
      animation: spin1 10s infinite linear;
      transform: rotateX(-15deg) rotateY(-15deg) rotateX(-15deg);
}

本帖探讨内容所围绕的例子,完整代码如下,效果请在后面的回复中查看:

<style>
#眼睛 {
      margin: 200px;
      perspective: 3000px;
      perspective-origin: 50% 40%;
}

#舞台 {
      position: relative;
      width: 300px;
      height: 300px;
      transform-style: preserve-3d;
      animation: spin1 10s infinite linear;
      transform: rotateX(-15deg) rotateY(-15deg) rotateX(-15deg);
}

.演员 {
      position: absolute;
      width: 100%;
      height: 100%;
      display: grid;
      place-items: center;
      font: normal 2em arial sans-serif;
      opacity: .85;
}

.演员:nth-of-type(1) {
      background: teal;
      transform: translateZ(150px) rotateY(0deg);
}

.演员:nth-of-type(2) {
      background: tan;
      transform: translateZ(-150px) rotateY(0deg);
}

.演员:nth-of-type(3) {
      background: orange;
      transform: translateX(-150px) rotateY(90deg);
}

.演员:nth-of-type(4) {
      background: tan;
      transform: translateX(150px) rotateY(90deg);
}

.演员:nth-of-type(5) {
      background: purple;
      transform: translateY(-150px) rotateX(90deg);
}

.演员:nth-of-type(6) {
      background: yellow;
      transform: translateY(150px) rotateX(90deg);
}

@keyframes spin {
      from { transform: rotateX(0) rotateY(0) rotateZ(0); }
      to { transform: rotateX(0deg) rotateY(360deg) rotateZ(360deg); }
}
</style>

<div id="眼睛">
      <div id="舞台">
                <span class="演员">前面</span>
                <span class="演员">背后</span>
                <span class="演员">左侧</span>
                <span class="演员">右侧</span>
                <span class="演员">顶部</span>
                <span class="演员">底部</span>
      </div>
</div>

起个网名好难 发表于 2023-2-3 18:47

本帖最后由 起个网名好难 于 2023-2-3 18:49 编辑 <br /><br /><style type="text/css">
#cube{
        margin:100px auto;
    width:250px; /*立方体面的宽*/
    height:250px; /*立方体面的高*/
    position:relative; /*相对定位*/
    transform-style:preserve-3d; /* 默认flat 2D */
    transform:rotateX(-15deg) rotateY(15deg);
}

#cube img{
    width:100%;
    height:100%;
    position:absolute; /*绝对定位*/
    opacity:.8; /*透明度*/
        display:none;
        border:thin blue solid;
}

@keyframes rotate{
    0%{
      transform:rotateX(0deg) rotateY(0deg);
    }
    100%{
      transform:rotateX(360deg) rotateY(360deg);
    }
}
#oblk        {
        width:800px;height:600px;
        overflow:hidden;display:grid;place-items:center;
        perspective: 3000px;
}
</style>

<div id="oblk">
<div id="cube">
<img src="https://s1.ax1x.com/2022/11/21/zQXlkD.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXMTO.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXK0K.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXum6.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXmOx.jpg" />
<img src="https://s1.ax1x.com/2022/11/21/zQXe61.jpg" />
</div></div>

<script type="text/javascript">
var cude = document.querySelector('#cube');
var imgs = cude.getElementsByTagName('img');
var aniObj = [];
var aIdx = 0;
for(n = 0; n < imgs.length; n++)        {
        if(n < 4)        {
                aniObj = imgs.animate([{transform:"rotateX(" + n*90 + "deg) translateZ(125px)"}], {duration:3000, fill:'forwards'});
        }
        else        {
                aniObj = imgs.animate([{transform:"rotateY(" + (n==4?90:-90) + "deg) translateZ(125px)"}], {duration:3000, fill:'forwards'});
        }
        aniObj.pause();
        aniObj.onfinish = function()        {
                aIdx++;
                if(aIdx < imgs.length)        {
                        imgs.style.display = "block";
                        aniObj.play();
                }
                else {
                       
                        cude.style.animation = "rotate 15s infinitelinear";console.log(cude.style.animationPlayState);
                }
        }
}       
        imgs.style.display = "block";
        aniObj.play();

</script>

马黑黑 发表于 2023-2-3 11:50

本帖最后由 马黑黑 于 2023-2-3 20:31 编辑 <br /><br /><style>
#眼睛 {
        margin: 200px;
        perspective: 3000px;
        perspective-origin: 50% 40%;
}

#舞台 {
        position: relative;
        width: 300px;
        height: 300px;
        transform-style: preserve-3d;
        animation: spin1 10s infinite linear;
        transform: rotateX(-15deg) rotateY(-15deg) rotateX(-15deg);
}

.演员 {
        position: absolute;
        width: 100%;
        height: 100%;
        display: grid;
        place-items: center;
        font: normal 2em arial sans-serif;
        opacity: .85;
}

.演员:nth-of-type(1) {
        background: teal;
        transform: translateZ(150px) rotateY(0deg);
}

.演员:nth-of-type(2) {
        background: tan;
        transform: translateZ(-150px) rotateY(0deg);
}

.演员:nth-of-type(3) {
        background: orange;
        transform: translateX(-150px) rotateY(90deg);
}

.演员:nth-of-type(4) {
        background: pink;
        transform: translateX(150px) rotateY(90deg);
}

.演员:nth-of-type(5) {
        background: purple;
        transform: translateY(-150px) rotateX(90deg);
}

.演员:nth-of-type(6) {
        background: yellow;
        transform: translateY(150px) rotateX(90deg);
}

@keyframes spin {
        from { transform: rotateX(0) rotateY(0) rotateZ(0); }
        to { transform: rotateX(0deg) rotateY(360deg) rotateZ(360deg); }
}
</style>

<div id="眼睛">
        <div id="舞台">
                <span class="演员">前面</span>
                <span class="演员">背后</span>
                <span class="演员">左侧</span>
                <span class="演员">右侧</span>
                <span class="演员">顶部</span>
                <span class="演员">底部</span>
        </div>
</div>

红影 发表于 2023-2-3 12:19

这个讲得太透彻了,而且特别生动形象。黑黑太棒了{:4_199:}

红影 发表于 2023-2-3 12:21

现在知道了为什么之前的旋转四面体可以从左侧看或右侧看了,是因为舞台旋转了角度{:4_204:}

红影 发表于 2023-2-3 12:23

“我们有三种方案:其一,让它沿XYZ轴旋转起来,其二,改变眼睛的视点(perspective-origin),其三,让舞台旋转那么一丢丢。”
这个可以放在一起用的吧,让它转起来,而且改变观察角度。

马黑黑 发表于 2023-2-3 12:34

红影 发表于 2023-2-3 12:19
这个讲得太透彻了,而且特别生动形象。黑黑太棒了

这个,看了还不懂的话,我就得去找个癞蛤蟆来举例了{:4_170:}

马黑黑 发表于 2023-2-3 12:35

红影 发表于 2023-2-3 12:21
现在知道了为什么之前的旋转四面体可以从左侧看或右侧看了,是因为舞台旋转了角度

对,这是影响因素之一

马黑黑 发表于 2023-2-3 12:37

红影 发表于 2023-2-3 12:23
“我们有三种方案:其一,让它沿XYZ轴旋转起来,其二,改变眼睛的视点(perspective-origin),其三,让舞 ...
perspective一般是固定的,设置好之后不再改变。但,如果需要,是可以改变的,去年就看过一个洋妞程序员演示 perspective-origin 的效果,她使用JS去改变视点的。

梦缘 发表于 2023-2-3 13:58

感谢老师的代码分享,问好!{:4_180:}

小辣椒 发表于 2023-2-3 17:02

这个6面体的制作菱角就很分明了,整体我做4面的时候就发现立体感没有完全出来,我就把图片压了边,加了颜色,这样出来效果漂亮,而且边角透明,可以看见背面图片的一角,4面的效果也是特别棒棒的。

小辣椒 发表于 2023-2-3 17:02

六面体 的立体感就更加好了{:4_178:}

红影 发表于 2023-2-3 17:10

马黑黑 发表于 2023-2-3 12:34
这个,看了还不懂的话,我就得去找个癞蛤蟆来举例了

哈哈,还是别去折腾癞蛤蟆了,它们还在冬眠呢{:4_189:}

红影 发表于 2023-2-3 17:12

马黑黑 发表于 2023-2-3 12:35
对,这是影响因素之一

这个总算明白了。

红影 发表于 2023-2-3 17:13

马黑黑 发表于 2023-2-3 12:37
perspective一般是固定的,设置好之后不再改变。但,如果需要,是可以改变的,去年就看过一个洋妞程序员 ...

那样还要好,可以动态的连续改变了。

马黑黑 发表于 2023-2-3 17:34

红影 发表于 2023-2-3 17:10
哈哈,还是别去折腾癞蛤蟆了,它们还在冬眠呢

屋里有暖气

马黑黑 发表于 2023-2-3 17:37

红影 发表于 2023-2-3 17:13
那样还要好,可以动态的连续改变了。

演示需要可以,一般不会去改变视点

马黑黑 发表于 2023-2-3 17:38

梦缘 发表于 2023-2-3 13:58
感谢老师的代码分享,问好!

{:4_191:}

马黑黑 发表于 2023-2-3 17:38

红影 发表于 2023-2-3 17:12
这个总算明白了。

不容易{:4_173:}

马黑黑 发表于 2023-2-3 17:39

小辣椒 发表于 2023-2-3 17:02
这个6面体的制作菱角就很分明了,整体我做4面的时候就发现立体感没有完全出来,我就把图片压了边,加了颜色 ...

压边面板之间的边界清晰一些

马黑黑 发表于 2023-2-3 17:40

小辣椒 发表于 2023-2-3 17:02
六面体 的立体感就更加好了

{:4_181:}
页: [1] 2 3 4 5
查看完整版本: 再谈六面体