你会用CSS画一个炫酷的3D旋转柱状体吗?

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 13 天,点击查看活动详情

用 CSS 模拟一些 3D 形状,对前端开发者的 CSS 技术水平有很高的考验。
同时也可以提高对 CSS 的掌握和认知。
本文将会使用纯粹的 HTML、CSS 来绘制下面这个外观精美的 3D 圆珠图形,而不会使用任何框架。

实现分析

在写代码之前,我们要分析应该如何实现。
首先要提供一个 3D 空间的容器用来放置圆柱体,设置倾斜度数。然后在 3D 空间中画一个圆柱体,圆柱体由多个切面组成,每个切面需要计算宽度和倾斜度数,圆柱体还要有循环旋转的动画。
简单的实现思路已经有了,下面开始动手吧!

HTML

首先编写 HTML 部分的代码。

<div class="holder">
  <div class="cylinder">
    <!-- 这里放置很多切面 -->
  </div>
</div>
复制代码

cylinder 中需要有很多切面,这里我们尝试设置 50 个切面,为了方便,我们使用 JS 的循环帮我们生成。

const cylinderEl = document.querySelector('.cylinder')

for(let i = 0; i < 50; i++) {
  cylinderEl.innerHTML += `
      <div class="face" style="--index: ${i};"></div>`
}
复制代码

这样 HTML 的部分就处理好了,是不是很简单?

CSS

要实现 3D 圆柱体,需要进行一些计算。所以在开始写代码之前,先定义出一些变量,来帮助我们更好的计算。
这是 CSS 变量。

body{
  --pi: 3.14159265358979;
  --cylinder-width: 100vw;
  --face-count: 50;
  --face-deg: (360deg / var(--face-count));
  --face-width: calc(var(--cylinder-width) / var(--face-count));
  --face-shift: calc(var(--cylinder-width) / var(--pi) / 2);
}
复制代码

我来解释一下它们每一个的作用:

  • pi:圆周率,因为我们要计算一个完美的圆
  • cylinder-width:圆柱体的宽度
  • face-count:切面的总数
  • face-deg:每个切面的旋转度数,用 360 度除以切面总数
  • face-width:每个切面的宽度,通过圆柱体宽度除掉总数就可以了。
  • face-shift:将切面向外面移动,让它们具有更多的 3D 效果

添加 holder 样式,让容器实现倾斜外观。

.holder {
  transform-style: preserve-3d;
  transform: rotateX(-35deg);
}
复制代码

设置圆柱体的旋转动画,让它绕 Y 轴 360 度旋转。

@keyframes spin {
  to {
    transform: rotateY(-360deg);
  }
}
复制代码

我们开始编写圆柱体的样式,它没什么特别之处。

.cylinder {
  position: relative;
  height: 400px;
  width: var(--cylinder-width);
  transform-style: preserve-3d;
  animation: spin 7s infinite linear;
}
复制代码

继续编写切面的样式。

.face {
  position: absolute;
  background-color: #da0060;
  opacity: 0.7;
  height: 100%;
  width: var(--face-width);
  top: 50%;
  left: 50%;
  transform: rotateY(calc(var(--face-deg) * var(--index))) translateZ(
      calc(var(--face-shift) - -6px)
    );
}
复制代码

每个侧面都需要计算宽度,其中最具有魔法性的是 transform 属性。
我们改变了 Y 轴的旋转角度,将所有切面拼凑起来,最终得到一个完美的圆。
第一个切面的偏移度是 7.2,第二个是 14.4,依次递增,第 50 个刚好是 360 度。
每个切面的 Z 轴都会向后一些,这样看起来空间会更大。
最后给第一个切面做个标记,可以更好的看到它的旋转。


.face:nth-child(1) {
  background: repeating-linear-gradient(
      -45deg,
      transparent,
      transparent 25%,
      #f55 0,
      #f55 50%
    ),
    repeating-linear-gradient(
      45deg,
      transparent,
      transparent 25%,
      #55f 0,
      #55f 50%
    ),
    #efb;
}
复制代码

最终完整的代码

<style>
body {
  --pi: 3.14159265358979;
  --cylinder-width: 100vw;
  --face-count: 50;
  --face-deg: (360deg / var(--face-count));
  --face-width: calc(var(--cylinder-width) / var(--face-count));
  --face-shift: calc(var(--cylinder-width) / var(--pi) / 2);
}

@keyframes spin {
  to {
    transform: rotateY(-360deg);
  }
}

.holder {
  transform-style: preserve-3d;
  transform: rotateX(-35deg);
}

.cylinder {
  position: relative;
  height: 50vw;
  width: var(--cylinder-width);
  transform-style: preserve-3d;
  animation: spin 5s infinite linear;
}
.face {
  position: absolute;
  background-color: #487df8;
  opacity: 0.7;
  height: 100%;
  width: var(--face-width);
  top: 50%;
  left: 50%;
  transform: rotateY(calc(var(--face-deg) * var(--index)))
    translateZ(calc(var(--face-shift) - -6px));
}

.face:nth-child(1) {
  background: repeating-linear-gradient(
      -45deg,
      transparent,
      transparent 25%,
      #f55 0,
      #f55 50%
    ),
    repeating-linear-gradient(
      45deg,
      transparent,
      transparent 25%,
      #55f 0,
      #55f 50%
    ),
    #efb;
}
</style>

<div class="holder">
  <div class="cylinder">
  </div>
</div>

<script>
  const cylinderEl = document.querySelector('.cylinder')

  for(let i = 0; i < 50; i++) {
    cylinderEl.innerHTML += `
      <div class="face" style="--index: ${i};"></div>`
  }
</script>
复制代码

这么简单的 CSS 3D 旋转柱状体,你学会了吗?

猜你喜欢

转载自juejin.im/post/7130495713196113927