金字塔css动画解析

003_the_pyramid

第一次见到这个动图的时候,比较惊讶,心想这真是css能实现的吗?后面对css动画有了一定学习了解后,才发现其实css的强大超乎想象👍,而这仅仅是冰山一角。话不多说,接下来我将详细解析如何通过代码一步步实现这个动画。

元素拆分

对于整个动画的实现,我通常不会一开始就着手考虑 CSS 动画如何实现,而是优先分析 HTML 元素的结构构成。以上这个金字塔图片,可以拆分为几个独立的元素。

QQ_1737099854010

上面我用数字标出来分别代表天空地面太阳金字塔左面金字塔右面阴影6个元素。

基础图形绘制

背景

首先绘制出承载这些元素的背景,包括一个偏黑色的正方形背景和位于正中心的圆形。其中,圆形通过 border-radius: 50% 实现。

1
2
3
<div class="frame">
<div class="circle"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.frame {
position: absolute;
//元素局中定位
left: 50%;
top: 50%;
transform: translate(-50%, -50%);

height: 400px;
width: 400px;
background: #272C34;
border-radius: 4px;
box-shadow: 1px 2px 10px 0 rgba(0, 0, 0, 0.3);
}

.circle {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
height: 180px;
width: 180px;
background: #fff;
border-radius: 50%;
}

image-20250117164450668

天空&地面

从图中可以看出,天空的高度相较于地面更大一些。我们可以设计两个高度不同的 div,通过设置它们的高度比例来保证完整填充父元素。

1
2
3
4
<div class="circle">
<div class="sky"></div>
<div class="ground"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.sky {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 124px;
background: #7DDFFC;
}

.ground {
position: absolute;
bottom: 0;
left: 0;
right: 0;
top: 124px;
background: #F0DE75;
}

image-20250117165729235

之前的圆形没有显示出来,此时可以在父元素circle的样式中设置 overflow: hidden,将超出圆形范围的部分隐藏即可

image-20250117170028705

太阳

同样是绘制一个圆形,先通过绝对定位将其固定在指定位置

1
2
3
<div class="circle">
<div class="sun"></div>
</div>
1
2
3
4
5
6
7
8
9
.sun{
position: absolute;
top:20px;
left:10px;
width: 34px;
height: 34px;
border-radius: 50%;
background: #FFEF00;
}

sun

金字塔

通过两个三角形拼接,可以模拟金字塔的立体效果。利用 clip-path: polygon 绘制三角形,并设置不同的背景颜色以实现阴影效果。

1
2
3
4
5
<div class="circle">
<!--金字塔-->
<div class="side-left"></div>
<div class="side-right"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.side-left{
position: absolute;
top: 67px;
left: 35px;
height: 57px;
width: 116px;
background: #F4F4F4; // 金字塔的亮面
}

.side-right{
position: absolute;
top: 67px;
left: 93px;
height: 57px;
width: 58px;
background: #DDDADA;// 金字塔的阴影面
}

image-20250126160210812

使用clip-path:polygon绘制出三角形,给出三角形三个点坐标即可。

1
2
3
4
5
6
7
.side-left{
clip-path: polygon(0% 100%, 100% 100%, 50% 0%);
}

.side-right{
clip-path: polygon(30% 100%, 100% 100%, 0% 0%);
}

这里以side-left元素为例,三个点分别表示左下角到右上角坐标,第一个点(x=0%, y=100%) 表示容器宽度的 0%(最左侧)和高度的 100%(最底部),即容器的左下角。第二个点对应右下角,第三个点则为顶部中间。

pyramid2

最终叠加效果展示金字塔效果如下图

image-20250126162129765

阴影

金字塔的地面阴影同样通过绘制一个三角形来完成。并设置适当的透明度,模拟真实的光影效果。

1
2
3
<div class="circle">
<div class="shadow"></div>
</div>
1
2
3
4
5
6
7
8
9
10
.shadow{
position: absolute;
z-index: 2;
top: 124px;
left: -80px;
height: 30px;
width: 360px;
background: rgba(0,0,0,0.2);// 半透明黑色,模拟阴影
clip-path: polygon(115px 0%, 231px 0%, 80% 100%);
}

录屏2025-01-26 16.35.39

超出部分被隐藏后,效果如下。

image-20250126163823859

至此,一个由多个元素组成的金字塔页面已成功绘制完成。接下来,我们将为其加入动画效果,使金字塔动起来


动画

在日常开发中,CSS 动画使用频率相对较低,对我来说是这样的(

因此可能需要专门花时间学习。然而,接下来的实现可能需要对动画有一定基础了解

本次动画的缓动函数统一采用了 ease-out,具有以下效果:

  • 缓慢启动:动画在起始阶段速度较慢,提供平滑的启动感。
  • 逐渐加速:中间阶段速度逐渐加快,形成流畅的过渡。
  • 平稳停止:接近终点时速度快速减缓,最终平稳结束。

接下来,让我们逐步为页面中的各个元素加入动画效果吧!

夕阳下沉

首先定义一下太阳运动的动画帧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@keyframes sun-goes-down {
0% {
background: #F57209; /* 太阳起始颜色(橙色) */
transform: rotate(-70deg);/*启始旋转角度*/
}

30% {
background: #FFEF00; /* 太阳中间阶段颜色(黄色) */
transform: rotate(-28deg);
}

70% {
background: #FFEF00;
}

100% {
background: #F57209;/* 太阳下沉时颜色恢复为橙色 */
transform: rotate(70deg);
}
}

这里旋转角度,需要选择一个旋转参考变换原点

1
2
3
4
5
6
.sun{
/*变换原点为 元素水平居中、垂直在顶部以下 4 倍高度的位置*/
transform-origin: 50% 400%;
/*4s动画持续时间,infinite动画无限执行*/
animation: sun-goes-down 4s ease-out infinite;
}

为什么选择 50% 400%

  • 选择水平居中 (50%) 是为了让旋转时太阳的位置在视觉上不会偏移,即太阳始终在水平中心线。
  • 选择 400% 作为垂直方向的参考点,是为了让太阳看起来从非常高的地方下落,模拟太阳从天空中下沉的效果。这样,动画中的旋转看起来就像太阳从远处的高空落下,而不是简单的围绕自己中心旋转。

夜幕降临

给天空也定义一个动画,主要改变其背景颜色,实现天空由明亮到黑暗,模拟夜晚来临。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.sky{
animation: sky-turns-black 4s ease-out infinite;
}

@keyframes sky-turns-black {
0% {
background: #272C34; //黑色
}

30% {
background: #7DDFFC; //蓝色
}

70% {
background: #7DDFFC;
}

100% {
background: #272C34;
}
}

沙漠褪色

沙地的颜色也跟随动画过程中逐渐变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.ground {
animation: fading-sand 4s ease-out infinite;
}

@keyframes fading-sand {
0% {
background: #272C34; //黑色
}

30% {
background: #F0DE75; //黄色
}

70% {
background: #F0DE75;
}

100% {
background: #272C34;
}
}

上述三个动画的效果图

animation1

金字塔阴影

在太阳运行的过程中,金字塔的阴影也会随之变化。随着时间推移,阴影从右侧逐渐移动至左侧,呈现出动态光照效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.side-left {
animation: pyramid-shading 4s ease-out infinite;
}

.side-right {
animation: pyramid-shading 4s ease-out infinite reverse; // 反向动画,使阴影效果相反
}

@keyframes pyramid-shading {
0% {
background: #272C34; // 初始阴影
}

30% {
background: #F4F4F4; // 过渡到亮面
}

70% {
background: #DDDADA; // 进一步变亮
}

100% {
background: #272C34; // 恢复初始阴影
}
}

animation2

地面影动

在太阳运行的过程中,地面上的金字塔阴影也会随之变化,模拟真实光照下的动态投影效果。阴影的形态不仅会随着光源位置拉伸或收缩,还会在不同角度之间过渡。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.shadow{
animation: shadow-on-the-floor 4s ease-out infinite;
}

//x轴不变,y轴伸缩 三角形两点固定,另一点变换位置
//通过 Y 轴缩放和 clip-path 调整三角形阴影形态
@keyframes shadow-on-the-floor {
0% {
transform: scaleY(0); // 初始状态,影子收缩
clip-path: polygon(115px 0%, 231px 0%, 100% 100%);
}

30% {
transform: scaleY(1); // 影子完全展开
clip-path: polygon(115px 0%, 231px 0%, 80% 100%);
}

55% {
transform: scaleY(0.4); // 影子部分收缩,模拟光照变化
}

75% {
transform: scaleY(1); // 再次展开
}

100% {
transform: scaleY(0); // 影子消失,回到初始状态
clip-path: polygon(115px 0%, 231px 0%, 0% 100%);
}
}

去掉遮挡背景和超出隐藏的部分后效果图

animation3

总结

将上述动画效果整合在一起,便形成了文章开头展示的 金字塔动态光影效果。随着太阳的位置变化,金字塔的阴影与地面投影同步变换。完整的代码已上传至GitHub :📌 金字塔源码

最终效果:

003_the_pyramid