css3实现一个3d楼梯动画


theme: qklhk-chocolate

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

背景

👏👏通过给出的宽/高+个数,用css3的transform以及transform-style快速的实现一个3d楼梯,速速来Get吧~
🥇文末分享源代码。记得点赞+关注+收藏!

1.实现效果

梯形.gif

2.实现步骤

  • 定义css变量:宽w、高h、个数count
/* 楼梯高度 */
--h: 40px;
/* 楼梯宽度 */
--w: 50px;
/* 楼梯节数 */
--count: 5;
  • 写一个容器,宽为count * w; 高为count * h;设置transform-style: preserve-3d,让转换的子元素保留3D转换。
.container {
    position: relative;
    width: calc(var(--count) * var(--w));
    height: calc(var(--count) * var(--h));
    transform-style: preserve-3d;
  }
  • 在容器内写count个div,给每个div标签定义css变量c,表示当前位置,从1~count,设置width为w,高为h * c,transform-style为preserve-3d;基于父容器absolute定位,bottom都为0,left从0开始,为w*(c-1);实现效果如下:

image.png
注意:以下圈出来的即为定义的w和h

image.png

<div class="container">
    <div class="step" style="--c: 1">
    </div>
    <div class="step" style="--c: 2">
    </div>
    <div class="step" style="--c: 3">
    </div>
    <div class="step" style="--c: 4">
    </div>
    <div class="step" style="--c: 5">
    </div>
</div>
.container .step {
    position: absolute;
    --i: calc(var(--c) - 1);
    left: calc(var(--w) * var(--i));
    bottom: 0;
    width: var(--w);
    height: calc(var(--h) * var(--c));
    border: 1px solid orange;
  }
  • 对父容器进行transform偏移,看看效果

image.png

.container{
 + transform: rotateX(-30deg) rotateY(30deg);
}
  • 为每个div设置前伪元素,宽度为w,高度为count * h,设置X轴方向旋转从0-90deg,效果如下:

1.gif

.container .step::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: var(--w);
    height: calc(var(--count) * var(--h));
    background: orange;
    transform: rotateX(90deg);
  }
  • 前伪元素设置z轴方向的偏移,偏移量为总高度(count * h)的一半

image.png

.container .step::before {
    /* z轴偏移为高度的2分之一 */
    --z: calc(var(--count) * var(--h) / 2);
    transform: rotateX(90deg) translateZ(var(--z));
 }

image.png

  • 为每个div设置后伪元素,高度为h,宽度为count* h(这个值与前伪元素的高度对应),设置Y轴方向旋转从0-90deg,效果如下:

2.gif

.container .step::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: calc(var(--count) * var(--h));
  height: var(--h);
  background: orangered;
  transform: rotateY(90deg);
}
  • 后伪元素设置z轴方向的偏移,偏移量为总高度(count * h)的一半
.container .step::after {
    /* z轴偏移为高度的2分之一 */
    --z: calc(var(--count) * var(--h) / 2);
    transform: rotateY(90deg) translateZ(calc(-1 * var(--z)));
 }

image.png

  • 为前伪元素添加hover效果

7.gif

.container .step:hover::before {
  cursor: pointer;
  filter: brightness(1.1);
}
  • 将父容器进行旋转,可以看到已经有大概的轮廓了

3.gif

  • 在每个div下添加两个span标签,宽度为w,高度为父元素step的高度,做为梯形的左右两侧

image.png

<div class="container">
  <div class="step" style="--c: 1">
    <span></span>
    <span></span>
  </div>
 ... //4个相同的标签
</div>
.container .step span {
    position: absolute;
    display: block;
    width: var(--w);
    height: 100%;
    background: orchid;
    transform-style: preserve-3d;
}
  • 设置第一个span的位置,z轴方向的偏移,偏移量为总高度(count * h)的一半

4.gif

.container .step span:nth-child(1) {
  /* z轴偏移为高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  transform: translateZ(var(--z));
}
  • 设置第二个span的位置,z轴方向的偏移,偏移量为总高度(count * h)的一半

5.gif

.container .step span:nth-child(2) {
  /* z轴偏移为高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  transform: translateZ(calc(-1 * var(--z)));
}
  • 可以看到,现在还剩两处需要填充

image.png

  • 填补第一处内容,将最后一个div标签中的最后一个span为容器,添加一个伪元素,高度100%,宽度为counut * h

image.png

.container .step:last-child span:nth-child(2)::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: calc(var(--counut) * var(--h));
  height: 100%;
  background: #222;
}
  • 将该伪元素y轴方向旋转90deg

8.gif

  • 调整其在x,y,z轴各个方向的偏移量,x轴方向为总高度的一半 即 -1 * (count * h) /2,y轴方向为0,z轴方向为-1 * ((count * h) /2 – w)

image.png

.container .step:last-child span:nth-child(2)::before {
  /* z轴偏移为总高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  /*  总高度的2分之一 - 宽度*/
  --k: calc(var(--z) - var(--w));
  transform: rotateY(90deg)
    translate3d(calc(-1 * var(--z)), 0, calc(-1 * var(--k)));
}
  • 填补第二处内容,将第一个div标签中的第二个span为容器,添加一个伪元素,高度counut * h,宽度为span元素的高度* count

image.png

.container .step:first-child span:nth-child(2)::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  background: #222;
  width: calc(100% * var(--count));
  height: calc(calc(var(--count) * var(--h)));
  transform: rotateX(90deg);
}
  • 将该伪元素X轴方向旋转90deg

9.gif

  • 调整其在x,y,z轴各个方向的偏移量,x轴方向为0,y轴为总高度的一半 即 (count * h) /2,z轴方向为 ((count * h) /2 – h)

image.png

.container .step:first-child span:nth-child(2)::after {
  /* z轴偏移为总高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  /*  总高度的2分之一 - 高度*/
  --k: calc(var(--z) - var(--h));
  transform: rotateX(90deg) translate3d(0, var(--z), var(--k));
}
  • 到此,我们就是写了一个完整的3d楼梯了,去掉辅助线,将各边的颜色风格统一一致

image.png

  • 为底部添加一个阴影效果,选择最底部的区域,设置scale

image.png

.container .step:first-child span:nth-child(2)::after{
    + transform: rotateX(90deg) translate3d(0, var(--z), var(--k)) scale(1.25);
}
  • 设置filter的blur滤镜

image.png

.container .step:first-child span:nth-child(2)::after{
    +filter: blur(20px);
}
  • 父容器添加animation动画,改变rotateX, rotateY的位置

10.gif

.container {
  + transform: rotateX(-30deg) rotateY(30deg);
  + animation: animate 10s linear infinite;
}

@keyframes animate {
    0% {
        transform: rotateX(-30deg) rotateY(0deg);
    }
    100% {
        transform: rotateX(-30deg) rotateY(1turn);
    }
}
  • 修改w和h,设置不同的count,试试吧

image.png

 /* 楼梯高度 */
  --h: 10px;
  /* 楼梯宽度 */
  --w: 50px;
  /* 楼梯节数 */
  --count: 6;

3.实现代码

<style>
:root {
  --bg: linear-gradient(to top, #fdcbf1 0%, #fdcbf1 1%, #e6dee9 100%);
  --c1: #6ec5e7;
  --c3: #c2e9fb;
  --c2: skyblue;
  /* 楼梯高度 */
  --h: 10px;
  /* 楼梯宽度 */
  --w: 50px;
  /* 楼梯节数 */
  --count: 6;
}
body {
  background: var(--bg);
}
.container {
  position: relative;
  width: calc(var(--count) * var(--w));
  height: calc(var(--count) * var(--h));
  transform-style: preserve-3d;
  transform: rotateX(-30deg) rotateY(30deg);
  animation: animate 10s linear infinite;
}
@keyframes animate {
  0% {
    transform: rotateX(-30deg) rotateY(0deg);
  }

  100% {
    transform: rotateX(-30deg) rotateY(1turn);
  }
}
.container .step {
  position: absolute;
  --i: calc(var(--c) - 1);
  left: calc(var(--w) * var(--i));
  bottom: 0;
  width: var(--w);
  height: calc(var(--h) * var(--c));
  transform-style: preserve-3d;
}
.container .step::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: var(--w);
  height: calc(var(--count) * var(--h));
  background: var(--c3);
  /* z轴偏移为总高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  transform: rotateX(90deg) translateZ(var(--z));
}
.container .step:hover::before {
  cursor: pointer;
  filter: brightness(1.1);
}
.container .step::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: calc(var(--count) * var(--h));
  height: var(--h);
  background: var(--c2);
  /* z轴偏移为总高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  transform: rotateY(90deg) translateZ(calc(-1 * var(--z)));
}
.container .step span {
  position: absolute;
  display: block;
  width: var(--w);
  height: 100%;
  background: var(--c1);
  transform-style: preserve-3d;
}
.container .step span:nth-child(1) {
  /* z轴偏移为总高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  transform: translateZ(var(--z));
}
.container .step span:nth-child(2) {
  /* z轴偏移为总高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  transform: translateZ(calc(-1 * var(--z)));
}
.container .step:last-child span:nth-child(2)::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: calc(var(--count) * var(--h));
  height: 100%;
  background: var(--c2);
  /* z轴偏移为总高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  /*  总高度的2分之一 - 宽度*/
  --k: calc(var(--z) - var(--w));
  transform: rotateY(90deg)
    translate3d(calc(-1 * var(--z)), 0, calc(-1 * var(--k)));
}
.container .step:first-child span:nth-child(2)::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  background: var(--c1);
  width: calc(100% * var(--count));
  height: calc(calc(var(--count) * var(--h)));
  /* z轴偏移为总高度的2分之一 */
  --z: calc(var(--count) * var(--h) / 2);
  /*  总高度的2分之一 - 高度*/
  --k: calc(var(--z) - var(--h));
  transform: rotateX(90deg) translate3d(0, var(--z), var(--k)) scale(1.25);
  filter: blur(20px);
}
</style>

<body>
    <div class="container">
      <div class="step" style="--c: 1">
        <span></span>
        <span></span>
      </div>
      <div class="step" style="--c: 2">
        <span></span>
        <span></span>
      </div>
      <div class="step" style="--c: 3">
        <span></span>
        <span></span>
      </div>
      <div class="step" style="--c: 4">
        <span></span>
        <span></span>
      </div>
      <div class="step" style="--c: 5">
        <span></span>
        <span></span>
      </div>
      <div class="step" style="--c: 6">
        <span></span>
        <span></span>
      </div>
    </div>
</body>

4.在线预览

https://code.juejin.cn/pen/7171483323682783271

5.写在最后🍒

看完本文如果觉得对你有一丢丢帮助,记得点赞+关注+收藏鸭 🍕
© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容