前言
👏CSS 实现一个3d魔方,速速来Get吧~
🥇文末分享源代码。记得点赞+关注+收藏!
1.实现效果
2.实现步骤
- 魔方的一面为九个圆角正方形,定义正方形的宽高为–w,九个正方形的直接的间距为–gap,由此可以计算出父容器的宽/高,box-sizing为border-box
--w: 60px;
--gap: 10px;
--allW: calc(3 * var(--w) + 4 * var(--gap));
- 魔方有六个面,定义六个面中格子的背景颜色,以及六个面的背景色
--c1: #f5e7a1;
--c2: skyblue;
--c3: #fff;
--c4: #97d497;
--c5: #801a1a;
--c6: orange;
--bg: #000;
- 设置父容器宽高为–allW
<div class="container"></div>
.container {
width: var(--allW);
height: var(--allW);
}
- 添加魔方元素的父元素,宽高与父容器一致,设置 transform-style: preserve-3d,指示元素的子元素应位于 3D 空间
<div class="container">
+ <div class="container-box"></div>
</div>
.container-box {
border: 1px solid red;
position: relative;
width: var(--allW);
height: var(--allW);
transform-style: preserve-3d;
}
- 在container-box添加魔方的其中一面cube-side,absolute定位,宽高与父元素一致,设置一定的圆角,内边距为gap
<div class="container-box">
+ <div class="cube-side"></div>
</div>
.cube-side {
width: 100%;
height: 100%;
position: absolute;
background: var(--bg);
border-radius: 7px;
padding: var(--gap);
}
- 在cube-side中添加一个宫格,设置color为c1,背景色为currentColor
currentColor:表示元素color属性的计算值。它能让原本不能默认通过属性或子元素继承的颜色属性继承。如果没有设置color就找父元素,一级一级找,直到根元素。
<div class="cube-side">
+ <div class="cube-grid" style="--c: var(--c1)"></div>
</div>
.cube-side .cube-grid {
width: var(--w);
height: var(--w);
border-radius: calc(var(--w) / 5);
color: var(--c);
background: currentColor;
}
为cube-side添加box-shadow,实现剩余的8个宫格(当然你也可以再写8个宫格,实现九宫格布局)
box-shadow:
CSS box-shadow 属性用于在元素的框架上添加阴影效果。你可以在同一个元素上设置多个阴影效果,并用逗号将他们分隔开。该属性可设置的值包括阴影的 X 轴偏移量、Y 轴偏移量、模糊半径、扩散半径和颜色。
eg:
- box-shadow添加第二个宫格
.cube-grid{
box-shadow: calc(var(--w) + var(--gap)) 0 0 currentColor;
}
- box-shadow添加第三个宫格
box-shadow: calc(var(--w) + var(--gap)) 0 0 currentColor, calc(var(--w) * 2 + var(--gap) * 2) 0 currentColor;
- box-shadow添加第四个宫格
box-shadow: calc(var(--w) + var(--gap)) 0 0 currentColor,
calc(var(--w) * 2 + var(--gap) * 2) 0 currentColor,
0 calc(var(--w) + var(--gap)) currentColor;
- 按照此规律,实现剩下的宫格
box-shadow: calc(var(--w) + var(--gap)) 0 0 currentColor,
calc(var(--w) * 2 + var(--gap) * 2) 0 currentColor,
0 calc(var(--w) + var(--gap)) currentColor,
calc(var(--w) + var(--gap)) calc(var(--w) + var(--gap)) currentColor,
calc(var(--w) * 2 + var(--gap) * 2) calc(var(--w) + var(--gap))
currentColor,
0 calc(var(--w) * 2 + var(--gap) * 2) currentColor,
calc(var(--w) + var(--gap)) calc(var(--w) * 2 + var(--gap) * 2)
currentColor,
calc(var(--w) * 2 + var(--gap) * 2) calc(var(--w) * 2 + var(--gap) * 2)
currentColor;
- 这样,魔方的其中一面就完成了
- container-box添加transform旋转
.container-box {
+ transform: rotate(45deg) rotateY(45deg) rotateZ(45deg);
}
- 添加第二面cube-side,宫格实现方式一致,添加transform旋转,x方向旋转-180deg,z轴方向偏移为宽的2分之一
<div class="cube-side side-back">
+ <div class="cube-grid" style="--c: var(--c2)"></div>
</div>
.cube-side.side-back {
transform: rotateX(-180deg) translateZ(calc(var(--allW) / 2));
}
- 添加第三面cube-side,宫格实现方式一致,添加transform旋转,y方向旋转90deg,z轴方向偏移为宽的2分之一
<div class="cube-side side-right">
+ <div class="cube-grid" style="--c: var(--c3)"></div>
</div>
.cube-side.side-right {
transform: rotateY(90deg) translateZ(calc(var(--allW) / 2));
}
- 添加第四面cube-side,宫格实现方式一致,添加transform旋转,y方向旋转-90deg,z轴方向偏移为宽的2分之一
<div class="cube-side side-left">
+ <div class="cube-grid" style="--c: var(--c4)"></div>
</div>
.cube-side.side-left {
transform: rotateY(-90deg) translateZ(calc(var(--allW) / 2));
}
- 添加第五面cube-side,宫格实现方式一致,添加transform旋转,x方向旋转90deg,z轴方向偏移为宽的2分之一
<div class="cube-side side-top">
+ <div class="cube-grid" style="--c: var(--c5)"></div>
</div>
.cube-side.side-top {
transform: rotateX(90deg) translateZ(calc(var(--allW) / 2));
}
- 添加第六面cube-side,宫格实现方式一致,添加transform旋转,x方向旋转-90deg,z轴方向偏移为宽的2分之一
<div class="cube-side side-bottom">
+ <div class="cube-grid" style="--c: var(--c6)"></div>
</div>
.cube-side.side-bottom {
transform: rotateX(-90deg) translateZ(calc(var(--allW) / 2));
}
- 将第一面z轴方向偏移为宽的2分之一
<div class="cube-side side-front">
<div class="cube-grid" style="--c: var(--c1)"></div>
</div>
.cube-side.side-front {
transform: translateZ(calc(var(--allW) / 2));
}
- 可以看到,从某些角度可以看见元素后面的颜色,为每面设置backface-visibility: hidden
backface-visibility:
CSS 属性 backface-visibility 指定当元素背面朝向观察者时是否可见。
.cube-side {
+ backface-visibility: hidden;
}
- 去掉刚开始的border辅助线
.container-box {
- border: 1px solid red;
}
- 为container-box添加动画,进行transform旋转
.container-box {
+ animation: rotate 10s infinite alternate;
}
@keyframes rotate {
0% {
transform: rotateZ(0deg) rotateX(0deg) rotateY(0deg);
}
15% {
transform: rotateZ(45deg) rotateX(45deg) rotateY(45deg);
}
30% {
transform: rotateZ(45deg) rotateX(90deg) rotateY(90deg);
}
45% {
transform: rotateZ(45deg) rotateX(135deg) rotateY(135deg);
}
60% {
transform: rotateZ(45deg) rotateX(90deg) rotateY(180deg);
}
75% {
transform: rotateZ(45deg) rotateX(45deg) rotateY(225deg);
}
100% {
transform: rotateZ(45deg) rotateX(0deg) rotateY(270deg);
}
}
- 在最外层的父容器上添加perspective,改变透视效果,使之变得更加的协调
perspective:
CSS 属性 perspective 指定了观察者与 z=0 平面的距离,使具有三维位置变换的元素产生透视效果。z>0 的三维元素比正常大,而 z<0 时则比正常小,大小程度由该属性的值决定。
.container {
+ perspective: 900px;
}
- 现在我们试着调整一下w和gap,就完成了啦~
--w: 30px;
--gap: 8px;
- 当然,颜色也是可以调整的,这里就不在赘述了哈
3.实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>151.3d魔方</title>
</head>
<link rel="stylesheet" href="../common.css" />
<style>
:root {
--bg: #000;
--c1: #f5e7a1;
--c2: skyblue;
--c3: #fff;
--c4: #97d497;
--c5: #801a1a;
--c6: orange;
--w: 30px;
--gap: 8px;
--allW: calc(3 * var(--w) + 4 * var(--gap));
}
.container {
width: var(--allW);
height: var(--allW);
perspective: 900px;
}
.container-box {
position: relative;
width: var(--allW);
height: var(--allW);
transform-style: preserve-3d;
animation: rotate 5s infinite alternate;
}
.cube-side {
width: 100%;
height: 100%;
position: absolute;
background: var(--bg);
border-radius: 7px;
padding: var(--gap);
backface-visibility: hidden;
}
.cube-side .cube-grid {
width: var(--w);
height: var(--w);
border-radius: calc(var(--w) / 5);
color: var(--c);
background: currentColor;
box-shadow: calc(var(--w) + var(--gap)) 0 0 currentColor,
calc(var(--w) * 2 + var(--gap) * 2) 0 currentColor,
0 calc(var(--w) + var(--gap)) currentColor,
calc(var(--w) + var(--gap)) calc(var(--w) + var(--gap)) currentColor,
calc(var(--w) * 2 + var(--gap) * 2) calc(var(--w) + var(--gap))
currentColor,
0 calc(var(--w) * 2 + var(--gap) * 2) currentColor,
calc(var(--w) + var(--gap)) calc(var(--w) * 2 + var(--gap) * 2)
currentColor,
calc(var(--w) * 2 + var(--gap) * 2) calc(var(--w) * 2 + var(--gap) * 2)
currentColor;
}
.cube-side.side-front {
transform: translateZ(calc(var(--allW) / 2));
}
.cube-side.side-back {
transform: rotateX(-180deg) translateZ(calc(var(--allW) / 2));
}
.cube-side.side-right {
transform: rotateY(90deg) translateZ(calc(var(--allW) / 2));
}
.cube-side.side-left {
transform: rotateY(-90deg) translateZ(calc(var(--allW) / 2));
}
.cube-side.side-top {
transform: rotateX(90deg) translateZ(calc(var(--allW) / 2));
}
.cube-side.side-bottom {
transform: rotateX(-90deg) translateZ(calc(var(--allW) / 2));
}
@keyframes rotate {
0% {
transform: rotateZ(0deg) rotateX(0deg) rotateY(0deg);
}
15% {
transform: rotateZ(45deg) rotateX(45deg) rotateY(45deg);
}
30% {
transform: rotateZ(45deg) rotateX(90deg) rotateY(90deg);
}
45% {
transform: rotateZ(45deg) rotateX(135deg) rotateY(135deg);
}
60% {
transform: rotateZ(45deg) rotateX(90deg) rotateY(180deg);
}
75% {
transform: rotateZ(45deg) rotateX(45deg) rotateY(225deg);
}
100% {
transform: rotateZ(45deg) rotateX(0deg) rotateY(270deg);
}
}
</style>
<body>
<div class="container">
<div class="container-box">
<div class="cube-side side-front">
<div class="cube-grid" style="--c: var(--c1)"></div>
</div>
<div class="cube-side side-back">
<div class="cube-grid" style="--c: var(--c2)"></div>
</div>
<div class="cube-side side-right">
<div class="cube-grid" style="--c: var(--c3)"></div>
</div>
<div class="cube-side side-left">
<div class="cube-grid" style="--c: var(--c4)"></div>
</div>
<div class="cube-side side-top">
<div class="cube-grid" style="--c: var(--c5)"></div>
</div>
<div class="cube-side side-bottom">
<div class="cube-grid" style="--c: var(--c6)"></div>
</div>
</div>
</div>
</body>
</html>