用 CSS 模拟一些 3D 形状,对前端开发者的 CSS 技术水平有很高的考验。
同时也可以提高对 CSS 的掌握和认知。
本文将会使用纯粹的 HTML、CSS 来绘制下面这个外观精美的 3D 圆珠图形,而不会使用任何框架。
实现分析
在写代码之前,我们要分析应该如何实现。
首先要提供一个 3D 空间的容器用来放置圆柱体,设置倾斜度数。然后在 3D 空间中画一个圆柱体,圆柱体由多个切面组成,每个切面需要计算宽度和倾斜度数,圆柱体还要有循环旋转的动画。
简单的实现思路已经有了,下面开始动手吧!
HTML
首先编写 HTML 部分的代码。
<div class="holder"> <div class="cylinder"> div> div> cylinder 中需要有很多切面,这里我们尝试设置 50 个切面,为了方便,我们使用 JS 的循环帮我们生成。 javascript 复制代码 const cylinderEl = document.querySelector('.cylinder') for(let i = 0; i < 50; i++) { cylinderEl.innerHTML += ` ` }
这样 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 += ` ` } script>
这么简单的 CSS 3D 旋转柱状体,你学会了吗?