最近突发奇想,想用代码实现一个流星雨的效果,毕竟谁能拒绝一场浪漫的视觉盛宴呢?在一番捣鼓后,终于用 CSS 实现了这个效果,现在就来和大家分享一下实现过程。
整体布局与背景设置
先看整体布局,HTML 结构很简单,就几个div元素,分别用于呈现星空背景、山脉轮廓和流星容器。
<div class="stars"></div>
<div class="mountains"></div>
<div class="meteor-container">
<!-- 基础流星 -->
<div class="meteor"></div>
<div class="meteor small dim"></div>
<div class="meteor large bright"></div>
<div class="meteor"></div>
<div class="meteor bright"></div>
<div class="meteor small"></div>
<div class="meteor large"></div>
<div class="meteor dim"></div>
<div class="meteor"></div>
<div class="meteor bright"></div>
<div class="meteor small dim"></div>
<div class="meteor"></div>
<div class="meteor large bright"></div>
<div class="meteor"></div>
<div class="meteor"></div>
</div>
核心部分还是 CSS 样式的编写。首先是背景,用linear-gradient创造出渐变效果,模拟深邃夜空。从深蓝到更深的蓝紫过渡,为整个场景奠定基调。
body {
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 50%, #1a1a2e 100%);
overflow: hidden;
height: 100vh;
position: relative;
}
再添加山脉轮廓,通过clip-path属性定义多边形形状,勾勒出起伏山脉,还用linear-gradient实现从深到浅的颜色过渡,模拟山脉的光影效果。
.mountains {
position: absolute;
bottom: 0;
width: 100%;
height: 30%;
background: linear-gradient(to top, #0a0a0a 0%, #1a1a1a 50%, transparent 100%);
clip-path: polygon(
0% 100%,
0% 70%,
10% 60%,
20% 65%,
30% 55%,
40% 60%,
50% 50%,
60% 55%,
70% 45%,
80% 50%,
90% 40%,
100% 45%,
100% 100%
);
}
营造星空闪烁效果
星空闪烁是流星雨场景的重要部分,用background-image配合radial-gradient来模拟星星。一个个径向渐变代表星星,设置不同的位置、透明度和大小,营造远近不同的星星效果。
.stars {
position: absolute;
width: 100%;
height: 100%;
background-image:
radial-gradient(1px 1px at 20px 30px, rgba(255,255,255,0.8), transparent),
radial-gradient(1px 1px at 40px 70px, rgba(255,255,255,0.6), transparent),
radial-gradient(0.5px 0.5px at 90px 40px, rgba(255,255,255,0.9), transparent),
radial-gradient(1px 1px at 130px 80px, rgba(255,255,255,0.5), transparent),
radial-gradient(0.5px 0.5px at 160px 30px, rgba(255,255,255,0.7), transparent);
background-repeat: repeat;
background-size: 200px 150px;
animation: twinkle 3s ease-in-out infinite alternate;
}
这里还定义了一个twinkle动画,让星星闪烁起来。通过改变透明度,在0%和100%关键帧之间切换,模拟星星闪烁的视觉效果。
@keyframes twinkle {
0% { opacity: 0.7; }
100% { opacity: 1; }
}
打造流星特效
流星是这个效果的主角,先来设置流星容器,用position: absolute让流星在整个页面范围内运动。
.meteor-container {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
每个流星都是一个div元素,设置为圆形,用border-radius: 50%实现。流星默认是透明的,通过动画让它出现、划过并消失。
.meteor {
position: absolute;
width: 2px;
height: 2px;
background: white;
border-radius: 50%;
opacity: 0;
}
流星的尾巴是用伪元素::before实现的。通过linear-gradient创建从亮到暗的渐变效果,模拟流星划过留下的轨迹。再通过transform旋转和平移,调整尾巴的角度和位置。
.meteor::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 1px;
background: linear-gradient(90deg,
rgba(255, 255, 255, 1) 0%,
rgba(255, 255, 255, 0.8) 20%,
rgba(255, 255, 255, 0.6) 40%,
rgba(255, 255, 255, 0.3) 70%,
transparent 100%);
transform: rotate(-135deg);
transform-origin: 0 0;
}
最关键的是流星的动画效果,定义meteor-fall动画。在0%关键帧,流星在页面外且透明;在10%关键帧,流星出现;在90%关键帧,流星保持可见;在100%关键帧,流星消失在页面外。通过transform改变流星的位置,实现从页面一端划过到另一端的效果。
@keyframes meteor-fall {
0% {
opacity: 0;
transform: translateX(-200px) translateY(-200px);
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateX(calc(100vw + 300px)) translateY(calc(100vh + 300px));
}
}
为了让流星雨效果更丰富,给不同的流星设置了不同的起始位置、动画时长和延迟时间。通过nth-child选择器,给每个流星添加独特的样式。比如nth-child(1)设置在left: 5%; top: 5%;的位置,动画时长3s,延迟0s。
.meteor:nth-child(1) {
left: 5%;
top: 5%;
animation: meteor - fall 3s linear infinite;
animation - delay: 0s;
}
还有不同大小和亮度的流星,大流星增加width和height,并在伪元素中增加width和height,让尾巴更明显。小流星则减小相应尺寸。明亮流星通过box - shadow添加光晕效果,暗淡流星降低opacity并调整伪元素渐变颜色,让流星看起来更真实多样。
/* 大流星样式 */
.meteor.large {
width: 3px;
height: 3px;
}
.meteor.large::before {
width: 150px;
height: 2px;
}
/* 小流星样式 */
.meteor.small {
width: 1px;
height: 1px;
}
.meteor.small::before {
width: 60px;
height: 0.5px;
}
/* 明亮流星样式 */
.meteor.bright {
box-shadow: 0 0 6px rgba(255, 255, 255, 0.8);
}
.meteor.bright::before {
background: linear-gradient(90deg,
rgba(255, 255, 255, 1) 0%,
rgba(255, 255, 255, 0.9) 20%,
rgba(255, 255, 255, 0.7) 40%,
rgba(255, 255, 255, 0.4) 70%,
transparent 100%);
}
/* 暗淡流星样式 */
.meteor.dim {
opacity: 0.6;
}
.meteor.dim::before {
background: linear-gradient(90deg,
rgba(255, 255, 255, 0.7) 0%,
rgba(255, 255, 255, 0.5) 20%,
rgba(255, 255, 255, 0.3) 40%,
rgba(255, 255, 255, 0.1) 70%,
transparent 100%);
}
JavaScript 交互增强
为了让用户能参与其中,用 JavaScript 添加了一些交互效果。点击页面可以添加流星,通过document.addEventListener('click', () => {})监听点击事件,每次点击创建 3 个流星,通过setTimeout控制流星出现的间隔。
document.addEventListener('click', () => {
for (let i = 0; i < 3; i++) {
setTimeout(() => createMeteor(), i * 200);
}
});
还设置了定时器,定期自动添加流星。通过setInterval(() => {}, 3000)每 3 秒检查一次,如果流星数量小于 20,就调用createMeteor函数创建新流星。
setInterval(() => {
if (meteorCount < 20) {
createMeteor();
}
}, 3000);
createMeteor函数负责创建流星元素,并给流星随机添加样式、位置、动画时长和延迟时间。随机选择预定义的样式类,如small、large、bright、dim等,随机设置left和top位置,随机生成动画时长和延迟时间,让每次生成的流星都不一样。
function createMeteor() {
const container = document.querySelector('.meteor - container');
const meteor = document.createElement('div');
meteor.className ='meteor';
const styles = ['', 'small', 'large', 'bright', 'dim','small dim', 'large bright'];
const randomStyle = styles[Math.floor(Math.random() * styles.length)];
meteor.className ='meteor'+ randomStyle;
meteor.style.left = Math.random() * 80 + '%';
meteor.style.top = Math.random() * 40 + '%';
meteor.style.animationDuration = (2 + Math.random() * 2)+'s';
meteor.style.animationDelay = Math.random() * 2 +'s';
container.appendChild(meteor);
meteorCount++;
setTimeout(() => {
if (meteor.parentNode) {
meteor.parentNode.removeChild(meteor);
meteorCount--;
}
}, 7000);
}
通过这些 CSS 和 JavaScript 代码,就打造出了一个浪漫的流星雨效果。在这个过程中,充分发挥了 CSS 的样式能力和 JavaScript 的交互能力,将原本静态的页面变得生动有趣。希望大家也能从中找到乐趣,尝试自己动手实现更多有趣的效果。
完整CSS代码如下,大家可以直接复制到 HTML 文件中运行,感受这场代码里的流星雨。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 50%, #1a1a2e 100%);
overflow: hidden;
height: 100vh;
position: relative;
}
/* 山脉轮廓 */
.mountains {
position: absolute;
bottom: 0;
width: 100%;
height: 30%;
background: linear-gradient(to top, #0a0a0a 0%, #1a1a1a 50%, transparent 100%);
clip-path: polygon(
0% 100%,
0% 70%,
10% 60%,
20% 65%,
30% 55%,
40% 60%,
50% 50%,
60% 55%,
70% 45%,
80% 50%,
90% 40%,
100% 45%,
100% 100%
);
}
/* 星空背景 */
.stars {
position: absolute;
width: 100%;
height: 100%;
background-image:
radial-gradient(1px 1px at 20px 30px, rgba(255,255,255,0.8), transparent),
radial-gradient(1px 1px at 40px 70px, rgba(255,255,255,0.6), transparent),
radial-gradient(0.5px 0.5px at 90px 40px, rgba(255,255,255,0.9), transparent),
radial-gradient(1px 1px at 130px 80px, rgba(255,255,255,0.5), transparent),
radial-gradient(0.5px 0.5px at 160px 30px, rgba(255,255,255,0.7), transparent);
background-repeat: repeat;
background-size: 200px 150px;
animation: twinkle 3s ease-in-out infinite alternate;
}
@keyframes twinkle {
0% { opacity: 0.7; }
100% { opacity: 1; }
}
/* 流星容器 */
.meteor-container {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
/* 流星基础样式 */
.meteor {
position: absolute;
width: 2px;
height: 2px;
background: white;
border-radius: 50%;
opacity: 0;
}
/* 流星尾光 */
.meteor::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 1px;
background: linear-gradient(90deg,
rgba(255, 255, 255, 1) 0%,
rgba(255, 255, 255, 0.8) 20%,
rgba(255, 255, 255, 0.6) 40%,
rgba(255, 255, 255, 0.3) 70%,
transparent 100%);
transform: rotate(-135deg);
transform-origin: 0 0;
}
/* 流星动画 */
@keyframes meteor-fall {
0% {
opacity: 0;
transform: translateX(-200px) translateY(-200px);
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateX(calc(100vw + 200px)) translateY(calc(100vh + 200px));
}
}
/* 不同的流星样式 */
.meteor:nth-child(1) {
left: 10%;
top: 5%;
animation: meteor-fall 3s linear infinite;
animation-delay: 0s;
}
.meteor:nth-child(2) {
left: 20%;
top: 8%;
animation: meteor-fall 2.5s linear infinite;
animation-delay: 0.5s;
}
.meteor:nth-child(3) {
left: 15%;
top: 12%;
animation: meteor-fall 3.2s linear infinite;
animation-delay: 1s;
}
.meteor:nth-child(4) {
left: 30%;
top: 6%;
animation: meteor-fall 2.8s linear infinite;
animation-delay: 1.5s;
}
.meteor:nth-child(5) {
left: 25%;
top: 15%;
animation: meteor-fall 3.5s linear infinite;
animation-delay: 2s;
}
.meteor:nth-child(6) {
left: 40%;
top: 10%;
animation: meteor-fall 2.7s linear infinite;
animation-delay: 2.5s;
}
.meteor:nth-child(7) {
left: 35%;
top: 18%;
animation: meteor-fall 3.1s linear infinite;
animation-delay: 3s;
}
.meteor:nth-child(8) {
left: 50%;
top: 8%;
animation: meteor-fall 2.9s linear infinite;
animation-delay: 3.5s;
}
.meteor:nth-child(9) {
left: 45%;
top: 20%;
animation: meteor-fall 3.3s linear infinite;
animation-delay: 4s;
}
.meteor:nth-child(10) {
left: 60%;
top: 12%;
animation: meteor-fall 2.6s linear infinite;
animation-delay: 4.5s;
}
.meteor:nth-child(11) {
left: 55%;
top: 25%;
animation: meteor-fall 3.4s linear infinite;
animation-delay: 5s;
}
.meteor:nth-child(12) {
left: 70%;
top: 15%;
animation: meteor-fall 2.4s linear infinite;
animation-delay: 5.5s;
}
.meteor:nth-child(13) {
left: 65%;
top: 30%;
animation: meteor-fall 3.6s linear infinite;
animation-delay: 6s;
}
.meteor:nth-child(14) {
left: 80%;
top: 18%;
animation: meteor-fall 2.8s linear infinite;
animation-delay: 6.5s;
}
.meteor:nth-child(15) {
left: 75%;
top: 35%;
animation: meteor-fall 3.2s linear infinite;
animation-delay: 7s;
}
/* 大流星样式 */
.meteor.large {
width: 3px;
height: 3px;
}
.meteor.large::before {
width: 150px;
height: 2px;
}
/* 小流星样式 */
.meteor.small {
width: 1px;
height: 1px;
}
.meteor.small::before {
width: 60px;
height: 0.5px;
}
/* 明亮流星样式 */
.meteor.bright {
box-shadow: 0 0 6px rgba(255, 255, 255, 0.8);
}
.meteor.bright::before {
background: linear-gradient(90deg,
rgba(255, 255, 255, 1) 0%,
rgba(255, 255, 255, 0.9) 20%,
rgba(255, 255, 255, 0.7) 40%,
rgba(255, 255, 255, 0.4) 70%,
transparent 100%);
}
/* 暗淡流星样式 */
.meteor.dim {
opacity: 0.6;
}
.meteor.dim::before {
background: linear-gradient(90deg,
rgba(255, 255, 255, 0.7) 0%,
rgba(255, 255, 255, 0.5) 20%,
rgba(255, 255, 255, 0.3) 40%,
rgba(255, 255, 255, 0.1) 70%,
transparent 100%);
}
/* 随机样式应用 */
.meteor:nth-child(odd) {
animation-duration: 2.5s;
}
.meteor:nth-child(even) {
animation-duration: 3.5s;
}
.meteor:nth-child(3n) {
animation-duration: 2.8s;
}
.meteor:nth-child(4n) {
animation-duration: 3.2s;
}
.meteor:nth-child(5n) {
animation-duration: 2.7s;
}
/* 响应式设计 */
@media (max-width: 768px) {
.meteor::before {
width: 60px;
}
.meteor.large::before {
width: 100px;
}
.meteor.small::before {
width: 40px;
}
}