纯CSS实现太阳系运转模型动画
在网页开发中,使用纯CSS实现动画不仅可以减少对JavaScript的依赖,还能让页面更加流畅和简洁。本文将详细介绍如何仅用HTML和CSS制作一个包含太阳、地球和月亮的简易运转模型动画。这个动画模拟了地球绕太阳公转、月球绕地球公转的双层轨道运动,并且地球本身还会自转。
核心思路是通过CSS animation配合transform-origin设置旋转中心,再利用嵌套的<div>结构实现轨道层级关系。整个动画不需要任何JavaScript代码,完全由CSS驱动。
准备工作:HTML结构设计
首先需要为每个天体创建对应的容器。太阳作为中心,放置在最外层;地球容器包含地球本身,并负责绕太阳公转;月球容器包含月球本身,并嵌套在地球容器内部,随地球一起运动,同时自身还绕地球公转。注意,为了让月球正确绕地球旋转,月球容器必须相对于地球容器定位,并且月球本身的transform-origin要设置在地球容器中心。
以下是一个合理的HTML结构:
<div class="solar-system">
<div class="sun"></div>
<div class="earth-orbit">
<div class="earth">
<div class="moon-orbit">
<div class="moon"></div>
</div>
</div>
</div>
</div>解释一下各部分的作用:
- solar-system:最外层容器,负责整体定位和背景。
- sun:太阳本体,固定显示在中心。
- earth-orbit:地球轨道的容器,通过旋转这个容器来实现地球的公转。
- earth:地球本体,同时作为月球轨道容器的父级。
- moon-orbit:月球轨道容器,通过旋转使月球绕地球公转。
- moon:月球本体。
CSS核心动画原理
实现多层公转的关键在于设置正确的transform-origin。以地球公转为例:地球容器earth-orbit的transform-origin应该设置为太阳的中心点,即50% 50%(相对父容器),但为了简化,通常将earth-orbit的宽高设为轨道直径,并利用position: absolute将其左上角定位到太阳中心偏移。
更常用的方法是:将earth-orbit放置在与太阳相同大小的容器中,通过position: absolute; left: 50%; top: 50%;使轨道容器以太阳中心为旋转基点。然后通过transform: rotate()动画带动整个轨道容器旋转,而地球则固定在轨道容器内部远离中心的位置(例如距左边缘恰好等于轨道半径)。这样就形成了公转效果。
同理,月球轨道容器moon-orbit也以地球中心为旋转基点,月球固定在月球轨道容器内部。
具体实现步骤
下面给出完整的CSS代码,并逐段解释关键部分。
1. 基础布局与太阳样式
设置整个场景的大小和背景,让太阳居中显示,并添加发光效果。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #000;
}
.solar-system {
position: relative;
width: 400px;
height: 400px;
}
.sun {
position: absolute;
top: 50%;
left: 50%;
width: 60px;
height: 60px;
background: radial-gradient(circle, #ff9a00, #ff3300);
border-radius: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 40px #ff6600;
}太阳使用radial-gradient模拟渐变,并添加box-shadow使其发光。
2. 地球轨道与地球本体
地球的轨道是一个旋转的容器,地球本身固定在容器内。轨道容器的旋转轴心是太阳中心,因此轨道容器需要定位到太阳中心,并且其尺寸应该等于轨道直径(例如太阳半径+地球公转半径的两倍)。实际上我们更常用的是让轨道容器完全包裹太阳,通过left: 50%; top: 50%;配合margin-left: -半径; margin-top: -半径;使其中心与太阳重合。但为了让代码更直观,可以这样处理:轨道容器宽高为轨道直径,然后position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);,这样轨道容器中心与太阳中心对齐。然后在地球轨道容器内部,地球元素被放置在轨道边缘(例如距离左边为轨道半径的位置),并通过旋转整个容器实现公转。
注意:由于地球轨道容器本身旋转,地球也会随之绕太阳旋转。
.earth-orbit {
position: absolute;
top: 50%;
left: 50%;
width: 300px; /* 轨道直径 */
height: 300px;
transform: translate(-50%, -50%);
animation: earthOrbit 10s linear infinite;
}
.earth {
position: absolute;
top: 50%;
left: 0; /* 将地球放在轨道最左边(最远处) */
width: 30px;
height: 30px;
background: radial-gradient(circle at 30% 30%, #4a90d9, #1e3a5f);
border-radius: 50%;
transform: translate(0, -50%); /* 使地球垂直居中 */
box-shadow: 0 0 10px #2a6bb0;
/* 为了让地球本身自转,可以添加自转动画 */
animation: earthSpin 2s linear infinite;
}公转动画earthOrbit很简单:
@keyframes earthOrbit {
from { transform: translate(-50%, -50%) rotate(0deg); }
to { transform: translate(-50%, -50%) rotate(360deg); }
}
@keyframes earthSpin {
from { transform: translate(0, -50%) rotate(0deg); }
to { transform: translate(0, -50%) rotate(360deg); }
}注意:earthOrbit中使用了translate(-50%, -50%)使轨道容器始终居中,然后叠加旋转。这样地球就会围绕太阳旋转。而earthSpin让地球自身旋转,通过translate(0, -50%)修正位置,因为地球在轨道容器中已经用left: 0定位,旋转中心为地球中心。
3. 月球轨道与月球本体
月球轨道容器moon-orbit应当嵌套在地球容器内部,并且以地球中心为旋转基点。这里需要特别注意:moon-orbit的父元素是earth,而earth本身是相对于earth-orbit定位的。为了简化,我们可以将moon-orbit的尺寸设置得与地球宽度一致,然后用position: absolute将moon-orbit覆盖在地球上,再通过transform-origin设置旋转中心为地球中心。但是更常见的方法是:把moon-orbit也做成一个与地球同心的大容器,类似地球轨道的做法。但这里地球容器的尺寸只有30px(地球直径),因此月球轨道容器必须小于地球容器?不对,月球轨道半径相对于地球应该很小。简单有效的方法是:将moon-orbit设置成一个较大的容器(例如直径80px),并将其中心对齐到地球中心,然后月球元素放在该容器内边缘。但这样月球轨道容器会覆盖地球,可能遮挡地球。为了不遮挡,我们可以将月球轨道容器放在地球容器的内部,并通过transform-origin设置为地球中心(即容器自身的50% 50%)。由于容器必须足够大以至于能包含月球轨道,我们可以将moon-orbit的宽高设为地球直径的两倍加月球轨道半径的两倍,但更优雅的做法是使用伪元素或者将月球放在地球的子级中,通过transform: rotate()结合translateX来模拟轨道。这里我采用另一种经典方法:将月球轨道容器设置为与地球容器同宽高,但overflow: visible,然后月球元素通过transform: translateX(距离) rotate(...)来实现绕地球公转。但为了保持代码一致性,我选择将月球轨道容器作为一个独立的绝对定位元素,其左上角位于地球中心,然后通过transform-origin为轨道容器的中心(即地球中心),月球元素放在轨道容器内部。由于轨道容器尺寸较小,月球会超出容器?没关系,设置overflow: visible即可。
为了简化并避免混乱,下面给出更清晰的实现方案:直接将月球轨道容器嵌套在地球容器内部,且其尺寸等于月球轨道直径,并通过CSS将其中心与地球中心对齐。在地球容器(earth)内部,月球轨道容器moon-orbit的position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);,然后通过animation旋转这个容器,月球元素则放在轨道容器的边缘(例如left: 0; top: 50%;)。但是注意,此时月球轨道容器的旋转中心默认是自身中心,而自身中心与地球中心重合(因为transform: translate(-50%, -50%)),所以旋转时月球就会绕地球公转。好,这个方案可行。
以下是月球部分的CSS:
.moon-orbit {
position: absolute;
top: 50%;
left: 50%;
width: 80px; /* 月球轨道直径 */
height: 80px;
transform: translate(-50%, -50%);
animation: moonOrbit 3s linear infinite;
}
.moon {
position: absolute;
top: 50%;
left: 0; /* 放在轨道边缘 */
width: 12px;
height: 12px;
background: radial-gradient(circle at 30% 30%, #ccc, #888);
border-radius: 50%;
transform: translate(0, -50%);
box-shadow: 0 0 5px #aaa;
}
@keyframes moonOrbit {
from { transform: translate(-50%, -50%) rotate(0deg); }
to { transform: translate(-50%, -50%) rotate(360deg); }
}注意:月球轨道容器moon-orbit的父元素是earth,而earth本身是position: absolute,且其宽度为30px。为了能让moon-orbit正确显示,earth需要设置overflow: visible,否则月球会被裁剪。另外,moon-orbit的宽高80px大于父容器earth的30px,但父容器没有设置overflow: hidden,所以没问题。
4. 调整细节与完整代码
为了让效果更逼真,可以添加一些额外的样式,例如轨道线(用border或伪元素),以及让太阳脉动。下面给出完整的HTML和CSS代码。注意代码中所有HTML特殊字符均已转义。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>纯CSS太阳-地球-月亮模型</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #000;
}
.solar-system {
position: relative;
width: 400px;
height: 400px;
}
.sun {
position: absolute;
top: 50%;
left: 50%;
width: 60px;
height: 60px;
background: radial-gradient(circle, #ff9a00, #ff3300);
border-radius: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 40px #ff6600;
animation: pulse 4s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: translate(-50%, -50%) scale(1); box-shadow: 0 0 40px #ff6600; }
50% { transform: translate(-50%, -50%) scale(1.1); box-shadow: 0 0 60px #ff6600; }
}
.earth-orbit {
position: absolute;
top: 50%;
left: 50%;
width: 280px;
height: 280px;
transform: translate(-50%, -50%);
animation: earthOrbit 12s linear infinite;
/* 可添加轨道线 */
border: 1px dashed rgba(255, 255, 255, 0.2);
border-radius: 50%;
}
.earth {
position: absolute;
top: 50%;
left: 0;
width: 32px;
height: 32px;
background: radial-gradient(circle at 35% 35%, #4a90d9, #1e3a5f);
border-radius: 50%;
transform: translate(0, -50%);
box-shadow: 0 0 10px #2a6bb0;
animation: earthSpin 2s linear infinite;
overflow: visible; /* 允许月球轨道超出 */
}
@keyframes earthOrbit {
from { transform: translate(-50%, -50%) rotate(0deg); }
to { transform: translate(-50%, -50%) rotate(360deg); }
}
@keyframes earthSpin {
from { transform: translate(0, -50%) rotate(0deg); }
to { transform: translate(0, -50%) rotate(360deg); }
}
.moon-orbit {
position: absolute;
top: 50%;
left: 50%;
width: 80px;
height: 80px;
transform: translate(-50%, -50%);
animation: moonOrbit 4s linear infinite;
border: 1px dashed rgba(255, 255, 255, 0.15);
border-radius: 50%;
}
.moon {
position: absolute;
top: 50%;
left: 0;
width: 14px;
height: 14px;
background: radial-gradient(circle at 30% 30%, #ccc, #888);
border-radius: 50%;
transform: translate(0, -50%);
box-shadow: 0 0 5px #aaa;
}
@keyframes moonOrbit {
from { transform: translate(-50%, -50%) rotate(0deg); }
to { transform: translate(-50%, -50%) rotate(360deg); }
}
</style>
</head>
<body>
<div class="solar-system">
<div class="sun"></div>
<div class="earth-orbit">
<div class="earth">
<div class="moon-orbit">
<div class="moon"></div>
</div>
</div>
</div>
</div>
</body>
</html>上述代码实现了太阳发光脉动、地球公转自转、月球绕地球公转。你可以调整动画时间(如12s、2s、4s)改变速度,也可以修改轨道半径(通过更改earth-orbit和moon-orbit的宽高)和天体大小。
总结
通过合理地使用CSS动画、transform和transform-origin,我们可以不依赖任何JavaScript模拟出太阳系中太阳、地球、月球的基本运动关系。这种技术不仅适用于教育演示,也可以用于网页中的装饰性动画。需要注意的是,公转的实现依赖于元素容器的旋转,而容器内部的天体则固定在边缘。通过嵌套多层这样的结构,可以构建出更复杂的轨道系统。希望本文能帮助你掌握纯CSS实现多层公转动画的技巧。