关于 web-weather-creator
用于在网页中制作风格化氛围效果的设计技能,使用 SVG 滤镜、CSS 动画和 JavaScript。内容覆盖云、雨、极光、海浪等效果,并提供 React 和 Tailwind 集成指引。
主要功能
- 01SVG 滤镜与 CSS 动画技巧
- 02云、雨、雾和海浪效果
- 03极光、光束与镜头光晕模式
- 04分层与视差指引
- 05性能及减少动态效果模式
- 06React 和 Tailwind 集成指引
适用场景
- 01为网页添加动态天气效果
- 02制作分层氛围背景
- 03构建动态海洋、天空或风暴场景
- 04针对移动设备和减少动态效果进行调整
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ curiositech/ some_claude_skills --skill web-weather-creator安装后在 Claude Code 中输入/web-weather-creator确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.claude-plugin/plugin.jsonreferences/layering-strategies.mdreact-components.tsx详细介绍
Web Weather Creator
使用 SVG 滤镜和 CSS 动画,打造风格化的大气效果。可创建云、波浪、闪电、雨、雾、极光、耶稣光、镜头光晕、暮色天空和海浪飞沫——风格高级而精致,绝不显得廉价。
激活条件
适用于:“云”、“波浪”、“天气效果”、“大气”、“闪电”、“雨”、“雾”、“薄雾”、“极光”、“北极光”、“耶稣光”、“曙暮光线”、“镜头光晕”、“暮色”、“日落渐变”、“黄金时刻”、“海浪飞沫”、“海滩冲刷”、“泡沫”、“烟雾”、“视差天空”、“水面涟漪”、“动画背景”
不适用于:
- 照片级真实感渲染(使用 WebGL/Three.js)
- 静态天气图标(使用图标库)
- 天气数据 API(使用后端服务)
- 3D 体积效果(使用 Babylon.js/Three.js)
理念:风格化,而非廉价
目标是打造高级的风格化效果——效果应显得经过精心设计、富有艺术感,而不是像素材库视频或占位艺术图。
三大原则
- 分层营造深度 - 多个半透明图层配合视差运动
- 有机运动 - 变化的速度、缓动和细微的随机效果
- 克制 - 一个主要效果,搭配细微的辅助元素
质量光谱
CHEAP ──────────────────────── STYLIZED ──────────────────────── REALISTIC
CSS gradient blob SVG filter + layered opacity WebGL particles
Single div animation 3-5 layers with parallax Fluid simulation
Linear easing Cubic-bezier + varied timing Physics engine
目标区域: 中间地带——风格化程度足以体现精心设计,同时具备适合生产环境的性能。
云穿过内容浮动(Z-Index 策略)
最常见的问题:“怎样才能让云穿过我的内容浮动,而不只是出现在内容后面?”
分层解决方案
<div class="atmosphere-container">
<div class="clouds clouds--back"></div>
<main class="content">
<div class="card">Your content here</div>
</main>
<div class="clouds clouds--front"></div>
</div>
.atmosphere-container {
position: relative;
isolation: isolate; /* Creates stacking context */
}
.clouds {
position: absolute;
inset: 0;
pointer-events: none; /* CRITICAL: Let clicks through */
filter: url(#cloud-cumulus);
}
.clouds--back {
z-index: -1;
opacity: 0.3;
transform: scale(1.5);
animation: drift 120s linear infinite;
}
.content {
position: relative;
z-index: 1;
}
.clouds--front {
z-index: 2;
opacity: 0.12; /* LOW opacity so content is readable */
transform: scale(0.8);
animation: drift 50s linear infinite reverse;
/* Optional: Mask to clear space around content */
mask-image: linear-gradient(
to bottom,
black 0%,
transparent 30%,
transparent 70%,
black 100%
);
}
关键原则
- 将云拆分为 2–3 个图层(后层、中层、前层)
- 前层云使用较低的不透明度(10–15%),以保持内容清晰可见
- 云图层始终使用
pointer-events: none - 使用遮罩为重要内容周围留出空间
- 改变动画速度营造视差深度(后层 = 慢,前层 = 快)
自然融合的混合模式
.clouds--front {
mix-blend-mode: soft-light; /* Blends naturally with content */
opacity: 0.25;
}
/* Or for white clouds on dark backgrounds */
.clouds--front {
mix-blend-mode: screen;
opacity: 0.15;
}
内容可读性
/* Add subtle backdrop to cards so text is always readable */
.card {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(4px); /* Blurs back clouds for extra depth */
}
→ 完整模式请参阅 references/layering-strategies.md,其中包括视差、内容感知遮罩和 React 组件。
核心技术:SVG 滤镜链
所有大气效果都基于这一滤镜架构:
<svg width="0" height="0">
<defs>
<filter id="atmosphere" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence
type="fractalNoise"
baseFrequency="0.01"
numOctaves="4"
seed="1"
result="noise"
/>
<feDisplacementMap
in="SourceGraphic"
in2="noise"
scale="30"
xChannelSelector="R"
yChannelSelector="G"
result="displaced"
/>
<feGaussianBlur
in="displaced"
stdDeviation="2"
result="blurred"
/>
<feDiffuseLighting
in="noise"
lighting-color="white"
surfaceScale="2"
result="lit"
>
<feDistantLight azimuth="45" elevation="60" />
</feDiffuseLighting>
<feComposite in="blurred" in2="lit" operator="multiply" />
</filter>
</defs>
</svg>
关键参数参考
| 参数 | 效果 | 范围 | 说明 |
|---|---|---|---|
type="fractalNoise" | 柔和、云状 | - | 云、烟雾、雾 |
type="turbulence" | 波浪状、液态 | - | 水、水波、涟漪 |
baseFrequency | 形状大小 | 0.001-0.1 | 数值越低,形状越大 |
numOctaves | 细节级别 | 1-8 | 3-5 最佳,>5 时收益递减 |
scale (displacement) | 失真 | 0-100 | 从 20-30 开始 |
stdDeviation (blur) | 柔和度 | 0-20 | 云用 2-5,水用 0-2 |
SECTION A:云效果
云类型配方
| 云类型 | baseFrequency | numOctaves | 模糊 | 特征 |
|---|---|---|---|---|
| 积云(蓬松) | 0.008 | 4 | 3-5 | 蓬松如棉花 |
| 卷云(纤细) | 0.003 | 5 | 1-2 | 纤薄、条纹状 |
| 层云(平坦) | 0.015 | 2 | 5-8 | 低矮、均匀 |
| 风暴云(戏剧性) | 0.006 | 5 | 2-3 | 浓密、有纹理 |
| 卡通风格 | 0.02 | 2 | 8-12 | 柔和、简洁 |
多层云系统
.cloud-layer {
position: absolute;
inset: 0;
background: radial-gradient(
ellipse 200% 100% at 50% 100%,
rgba(255, 255, 255, 0.9) 0%,
transparent 70%
);
filter: url(#cloud-filter);
}
/* Layer hierarchy: back → front */
.cloud-back {
opacity: 0.3;
transform: scale(1.5);
animation: drift 120s linear infinite;
}
.cloud-mid {
opacity: 0.5;
transform: scale(1.2);
animation: drift 80s linear infinite;
}
.cloud-front {
opacity: 0.7;
transform: scale(1);
animation: drift 50s linear infinite;
}
@keyframes drift {
from { transform: translateX(-10%); }
to { transform: translateX(10%); }
}
云彩配色方案
:root {
/* Daylight clouds */
--cloud-white: rgba(255, 255, 255, 0.9);
--cloud-highlight: rgba(255, 250, 240, 1);
--cloud-shadow: rgba(180, 190, 210, 0.6);
/* Sunset clouds */
--cloud-gold: rgba(255, 200, 100, 0.8);
--cloud-pink: rgba(255, 150, 180, 0.7);
--cloud-purple: rgba(180, 100, 200, 0.5);
/* Storm clouds */
--storm-dark: rgba(60, 70, 90, 0.9);
--storm-purple: rgba(80, 60, 100, 0.7);
--storm-highlight: rgba(200, 210, 230, 0.4);
}
SECTION B:波浪与水面效果
关键区别:波浪使用 turbulence
<feTurbulence
type="turbulence"
baseFrequency="0.01 0.03"
numOctaves="3"
result="waves"
/>
波浪类型配方
| 波浪类型 | baseFrequency | numOctaves | Scale | 特征 |
|---|---|---|---|---|
| 海洋涌浪 | 0.005 0.015 | 3 | 40 | 宽阔、起伏翻滚 |
| 湖面涟漪 | 0.02 0.04 | 2 | 15 | 细小、规律 |
| 海滩岸浪 | 0.008 0.02 | 4 | 30 | 逐渐涌来,带有泡沫 |
| 泳池倒影 | 0.03 0.03 | 2 | 10 | 细微、均匀 |
动态波浪 SVG
<svg viewBox="0 0 1440 320" preserveAspectRatio="none">
<defs>
<filter id="wave-distort">
<feTurbulence
type="turbulence"
baseFrequency="0.01 0.02"
numOctaves="3"
result="turbulence"
>
<animate
attributeName="baseFrequency"
dur="20s"
values="0.01 0.02; 0.015 0.025; 0.01 0.02"
repeatCount="indefinite"
/>
</feTurbulence>
<feDisplacementMap
in="SourceGraphic"
in2="turbulence"
scale="20"
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</defs>
<path
fill="var(--ocean-color)"
filter="url(#wave-distort)"
d="M0,160 C360,220 720,100 1080,180 C1260,220 1380,140 1440,160 L1440,320 L0,320 Z"
>
<animate
attributeName="d"
dur="10s"
repeatCount="indefinite"
values="
M0,160 C360,220 720,100 1080,180 C1260,220 1380,140 1440,160 L1440,320 L0,320 Z;
M0,180 C360,120 720,200 1080,140 C1260,100 1380,180 1440,140 L1440,320 L0,320 Z;
M0,160 C360,220 720,100 1080,180 C1260,220 1380,140 1440,160 L1440,320 L0,320 Z
"
/>
</path>
</svg>
海洋配色方案
:root {
/* Deep ocean */
--ocean-deep: #0c4a6e;
--ocean-mid: #0369a1;
--ocean-surface: #38bdf8;
--ocean-foam: rgba(255, 255, 255, 0.8);
/* Tropical */
--tropical-deep: #047857;
--tropical-mid: #10b981;
--tropical-surface: #6ee7b7;
/* Stormy */
--stormy-deep: #1e3a5f;
--stormy-mid: #334155;
--stormy-surface: #64748b;
/* Sunset reflection */
--sunset-water: linear-gradient(
180deg,
rgba(251, 146, 60, 0.4) 0%,
rgba(14, 165, 233, 0.6) 50%,
#0c4a6e 100%
);
}
SECTION C:闪电效果
SVG 描边偏移闪电
闪电效果最有效的技术是在 SVG 路径上使用动画描边偏移:
<svg viewBox="0 0 100 200" class="lightning">
<defs>
<filter id="lightning-glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<path
class="bolt"
d="M50,0 L45,40 L55,45 L40,90 L60,95 L35,150 L70,100 L50,95 L65,50 L45,45 L60,10 Z"
fill="none"
stroke="white"
stroke-width="2"
filter="url(#lightning-glow)"
/>
</svg>
.lightning {
position: absolute;
pointer-events: none;
opacity: 0;
}
.bolt {
stroke-dasharray: 500;
stroke-dashoffset: 500;
}
.lightning.flash {
animation: lightning-flash 0.5s ease-out forwards;
}
.lightning.flash .bolt {
animation: bolt-draw 0.15s ease-out forwards;
}
@keyframes lightning-flash {
0% { opacity: 0; }
10% { opacity: 1; }
20% { opacity: 0.5; }
30% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes bolt-draw {
to { stroke-dashoffset: 0; }
}
闪电闪光叠加层
.lightning-flash-overlay {
position: fixed;
inset: 0;
background: rgba(255, 255, 255, 0);
pointer-events: none;
z-index: 1000;
}
.lightning-flash-overlay.active {
animation: screen-flash 0.3s ease-out;
}
@keyframes screen-flash {
0% { background: rgba(255, 255, 255, 0); }
10% { background: rgba(200, 220, 255, 0.4); }
20% { background: rgba(255, 255, 255, 0); }
30% { background: rgba(200, 220, 255, 0.2); }
100% { background: rgba(255, 255, 255, 0); }
}
分支闪电图案
function generateLightningPath(startX, startY, endY, segments = 8) {
let path = `M${startX},${startY}`;
let x = startX;
let y = startY;
const segmentHeight = (endY - startY) / segments;
for (let i = 0; i < segments; i++) {
const jitter = (Math.random() - 0.5) * 30;
x += jitter;
y += segmentHeight;
path += ` L${x},${y}`;
// Random branch (20% chance)
if (Math.random() < 0.2) {
const branchX = x + (Math.random() - 0.5) * 40;
const branchY = y + segmentHeight * 0.5;
path += ` M${x},${y} L${branchX},${branchY} M${x},${y}`;
}
}
return path;
}
SECTION D:雨效
仅用 CSS 实现的雨效(使用自定义属性)
.rain-container {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.raindrop {
position: absolute;
width: 2px;
background: linear-gradient(
180deg,
transparent 0%,
rgba(174, 194, 224, 0.5) 50%,
rgba(174, 194, 224, 0.8) 100%
);
border-radius: 0 0 2px 2px;
animation: rain-fall var(--fall-duration) linear infinite;
animation-delay: var(--delay);
left: var(--x);
height: var(--length);
opacity: var(--opacity);
}
@keyframes rain-fall {
0% {
transform: translateY(-100vh);
}
100% {
transform: translateY(100vh);
}
}
雨效生成器(JavaScript)
function createRain(container, dropCount = 100) {
for (let i = 0; i < dropCount; i++) {
const drop = document.createElement('div');
drop.className = 'raindrop';
drop.style.setProperty('--x', `${Math.random() * 100}%`);
drop.style.setProperty('--delay', `${Math.random() * 2}s`);
drop.style.setProperty('--fall-duration', `${0.5 + Math.random() * 0.5}s`);
drop.style.setProperty('--length', `${15 + Math.random() * 20}px`);
drop.style.setProperty('--opacity', `${0.3 + Math.random() * 0.5}`);
container.appendChild(drop);
}
}
雨势强度预设
/* Light drizzle */
.rain-light {
--drop-count: 50;
--fall-duration-base: 1.2s;
--drop-opacity: 0.3;
}
/* Moderate rain */
.rain-moderate {
--drop-count: 150;
--fall-duration-base: 0.8s;
--drop-opacity: 0.5;
}
/* Heavy downpour */
.rain-heavy {
--drop-count: 300;
--fall-duration-base: 0.5s;
--drop-opacity: 0.7;
}
雨效 + 风向角度
.raindrop.windy {
animation: rain-fall-diagonal var(--fall-duration) linear infinite;
}
@keyframes rain-fall-diagonal {
0% {
transform: translate(-50vw, -100vh) rotate(15deg);
}
100% {
transform: translate(50vw, 100vh) rotate(15deg);
}
}
SECTION E:雾效
多层雾效系统
.fog-container {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.fog-layer {
position: absolute;
width: 200%;
height: 100%;
background: url("data:image/svg+xml,...") repeat-x;
opacity: 0.3;
filter: url(#fog-filter);
}
.fog-back {
animation: fog-drift 60s linear infinite;
opacity: 0.2;
transform: scale(1.5);
}
.fog-mid {
animation: fog-drift 40s linear infinite reverse;
opacity: 0.3;
transform: scale(1.2);
}
.fog-front {
animation: fog-drift 25s linear infinite;
opacity: 0.4;
}
@keyframes fog-drift {
from { transform: translateX(-50%); }
to { transform: translateX(0%); }
}
SVG 雾效滤镜
<filter id="fog-filter" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence
type="fractalNoise"
baseFrequency="0.003"
numOctaves="4"
seed="5"
result="noise"
/>
<feGaussianBlur in="noise" stdDeviation="20" result="blur" />
<feColorMatrix
type="matrix"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 0.5 0"
result="faded"
/>
</filter>
雾气浓度变化
:root {
/* Light haze */
--fog-light: rgba(255, 255, 255, 0.2);
--fog-blur-light: 10px;
/* Morning mist */
--fog-mist: rgba(200, 210, 230, 0.4);
--fog-blur-mist: 20px;
/* Dense fog */
--fog-dense: rgba(180, 190, 210, 0.6);
--fog-blur-dense: 40px;
/* Eerie fog (horror) */
--fog-eerie: rgba(100, 120, 140, 0.5);
--fog-blur-eerie: 30px;
}
贴地薄雾
.ground-mist {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 30%;
background: linear-gradient(
to top,
rgba(255, 255, 255, 0.6) 0%,
rgba(255, 255, 255, 0.3) 40%,
transparent 100%
);
filter: url(#fog-filter);
animation: mist-breathe 8s ease-in-out infinite;
}
@keyframes mist-breathe {
0%, 100% {
opacity: 0.5;
transform: scaleY(1);
}
50% {
opacity: 0.7;
transform: scaleY(1.1);
}
}
SECTION F:暮光与天空渐变
天空时段变化
:root {
/* Pre-dawn (5:00-5:30) */
--sky-predawn: linear-gradient(
to top,
#1a1a2e 0%,
#16213e 30%,
#0f3460 60%,
#1a1a2e 100%
);
/* Dawn (5:30-6:30) */
--sky-dawn: linear-gradient(
to top,
#ff6b6b 0%,
#feca57 20%,
#ff9ff3 40%,
#54a0ff 70%,
#1a1a2e 100%
);
/* Golden hour (6:30-7:30) */
--sky-golden: linear-gradient(
to top,
#ff9a56 0%,
#ffbe76 30%,
#ffeaa7 50%,
#74b9ff 80%,
#0984e3 100%
);
/* Midday (10:00-14:00) */
--sky-midday: linear-gradient(
to top,
#74b9ff 0%,
#0984e3 50%,
#0652dd 100%
);
/* Sunset (17:30-18:30) */
--sky-sunset: linear-gradient(
to top,
#e17055 0%,
#fdcb6e 20%,
#f8a5c2 40%,
#686de0 70%,
#30336b 100%
);
/* Dusk (18:30-19:30) */
--sky-dusk: linear-gradient(
to top,
#4a69bd 0%,
#6a89cc 30%,
#b8e994 10%,
#e55039 5%,
#1e3799 60%,
#0c2461 100%
);
/* Night (20:00+) */
--sky-night: linear-gradient(
to top,
#0c2461 0%,
#1e3799 40%,
#0a1931 100%
);
}
动态天空过渡
.sky-canvas {
background: var(--sky-midday);
transition: background 3s ease-in-out;
}
.sky-canvas[data-time="dawn"] { background: var(--sky-dawn); }
.sky-canvas[data-time="golden"] { background: var(--sky-golden); }
.sky-canvas[data-time="midday"] { background: var(--sky-midday); }
.sky-canvas[data-time="sunset"] { background: var(--sky-sunset); }
.sky-canvas[data-time="dusk"] { background: var(--sky-dusk); }
.sky-canvas[data-time="night"] { background: var(--sky-night); }
日月位置系统
.celestial-body {
position: absolute;
width: 60px;
height: 60px;
border-radius: 50%;
transition: all 2s cubic-bezier(0.4, 0, 0.2, 1);
}
.sun {
background: radial-gradient(
circle,
#fff7e6 0%,
#ffd93d 40%,
#ff8c00 100%
);
box-shadow:
0 0 40px 10px rgba(255, 200, 50, 0.6),
0 0 80px 30px rgba(255, 150, 0, 0.3);
}
.moon {
background: radial-gradient(
circle at 30% 30%,
#f5f5f5 0%,
#e0e0e0 50%,
#bdbdbd 100%
);
box-shadow:
0 0 20px 5px rgba(255, 255, 255, 0.3),
inset -10px -10px 20px rgba(0, 0, 0, 0.1);
}
SECTION G:极光
叠层渐变极光
.aurora-container {
position: absolute;
inset: 0;
overflow: hidden;
background: linear-gradient(to top, #0a0a1a 0%, #1a1a3a 100%);
}
.aurora-band {
position: absolute;
top: 10%;
left: -50%;
width: 200%;
height: 40%;
opacity: 0.6;
mix-blend-mode: screen;
filter: blur(30px);
}
.aurora-green {
background: linear-gradient(
90deg,
transparent 0%,
rgba(0, 255, 128, 0.4) 20%,
rgba(0, 255, 200, 0.6) 50%,
rgba(0, 255, 128, 0.4) 80%,
transparent 100%
);
animation: aurora-wave 15s ease-in-out infinite;
}
.aurora-purple {
background: linear-gradient(
90deg,
transparent 0%,
rgba(138, 43, 226, 0.3) 25%,
rgba(75, 0, 130, 0.5) 50%,
rgba(138, 43, 226, 0.3) 75%,
transparent 100%
);
animation: aurora-wave 20s ease-in-out infinite reverse;
animation-delay: -5s;
}
.aurora-blue {
background: linear-gradient(
90deg,
transparent 0%,
rgba(0, 150, 255, 0.3) 30%,
rgba(0, 200, 255, 0.4) 50%,
rgba(0, 150, 255, 0.3) 70%,
transparent 100%
);
animation: aurora-wave 18s ease-in-out infinite;
animation-delay: -8s;
}
@keyframes aurora-wave {
0%, 100% {
transform: translateX(-20%) scaleY(1) skewX(-5deg);
opacity: 0.4;
}
25% {
transform: translateX(-10%) scaleY(1.2) skewX(5deg);
opacity: 0.7;
}
50% {
transform: translateX(0%) scaleY(0.8) skewX(-3deg);
opacity: 0.5;
}
75% {
transform: translateX(10%) scaleY(1.1) skewX(3deg);
opacity: 0.6;
}
}
极光帷幕效果
.aurora-curtain {
position: absolute;
top: 0;
width: 100%;
height: 60%;
background: repeating-linear-gradient(
90deg,
transparent 0px,
rgba(0, 255, 150, 0.1) 2px,
transparent 4px
);
filter: blur(2px);
animation: curtain-shimmer 3s ease-in-out infinite;
}
@keyframes curtain-shimmer {
0%, 100% { opacity: 0.3; }
50% { opacity: 0.6; }
}
极光配色方案
:root {
/* Classic green */
--aurora-green-1: rgba(0, 255, 128, 0.6);
--aurora-green-2: rgba(50, 255, 180, 0.4);
/* Purple/violet */
--aurora-purple-1: rgba(138, 43, 226, 0.5);
--aurora-purple-2: rgba(186, 85, 211, 0.4);
/* Blue tones */
--aurora-blue-1: rgba(0, 191, 255, 0.4);
--aurora-blue-2: rgba(65, 105, 225, 0.3);
/* Rare red (high activity) */
--aurora-red-1: rgba(255, 69, 0, 0.3);
--aurora-red-2: rgba(255, 99, 71, 0.2);
}
SECTION H:耶稣光(曙暮光线)
圆锥渐变耶稣光
.god-rays-container {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.god-rays {
position: absolute;
top: -50%;
left: 50%;
transform: translateX(-50%);
width: 200%;
height: 200%;
background: conic-gradient(
from 180deg at 50% 0%,
transparent 0deg,
rgba(255, 248, 220, 0.2) 5deg,
transparent 10deg,
transparent 20deg,
rgba(255, 248, 220, 0.15) 25deg,
transparent 30deg,
transparent 45deg,
rgba(255, 248, 220, 0.2) 50deg,
transparent 55deg,
transparent 70deg,
rgba(255, 248, 220, 0.1) 75deg,
transparent 80deg,
transparent 100deg,
rgba(255, 248, 220, 0.15) 105deg,
transparent 110deg,
transparent 130deg,
rgba(255, 248, 220, 0.2) 135deg,
transparent 140deg,
transparent 160deg,
rgba(255, 248, 220, 0.1) 165deg,
transparent 170deg,
transparent 180deg
);
mask-image: radial-gradient(
ellipse 100% 100% at 50% 0%,
black 0%,
transparent 70%
);
animation: rays-pulse 8s ease-in-out infinite;
}
@keyframes rays-pulse {
0%, 100% { opacity: 0.6; }
50% { opacity: 0.9; }
}
窗户透射耶稣光
.window-rays {
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(
135deg,
transparent 0%,
transparent 30%,
rgba(255, 248, 220, 0.3) 30%,
rgba(255, 248, 220, 0.1) 35%,
transparent 35%,
transparent 45%,
rgba(255, 248, 220, 0.25) 45%,
rgba(255, 248, 220, 0.05) 52%,
transparent 52%,
transparent 60%,
rgba(255, 248, 220, 0.2) 60%,
rgba(255, 248, 220, 0.05) 68%,
transparent 68%
);
filter: blur(20px);
mix-blend-mode: overlay;
}
动态光线旋转
.rotating-rays {
animation: rays-rotate 120s linear infinite;
}
@keyframes rays-rotate {
from { transform: translateX(-50%) rotate(0deg); }
to { transform: translateX(-50%) rotate(360deg); }
}
SECTION I:镜头光晕与相机效果
径向渐变镜头光晕
.lens-flare-container {
position: absolute;
inset: 0;
pointer-events: none;
overflow: hidden;
}
.flare-source {
position: absolute;
width: 80px;
height: 80px;
border-radius: 50%;
background: radial-gradient(
circle,
rgba(255, 255, 255, 1) 0%,
rgba(255, 240, 200, 0.8) 20%,
rgba(255, 200, 100, 0.4) 40%,
transparent 70%
);
box-shadow:
0 0 60px 30px rgba(255, 200, 100, 0.5),
0 0 120px 60px rgba(255, 150, 50, 0.3);
}
.flare-artifact {
position: absolute;
border-radius: 50%;
opacity: 0.3;
}
.flare-artifact.hexagon {
width: 40px;
height: 40px;
background: radial-gradient(
circle,
rgba(100, 200, 255, 0.4) 0%,
rgba(100, 200, 255, 0.1) 50%,
transparent 70%
);
clip-path: polygon(
50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%
);
}
.flare-artifact.streak {
width: 200px;
height: 2px;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 200, 150, 0.6) 30%,
rgba(255, 255, 255, 0.8) 50%,
rgba(255, 200, 150, 0.6) 70%,
transparent 100%
);
transform-origin: center;
}
.flare-artifact.ring {
width: 100px;
height: 100px;
border: 2px solid rgba(255, 200, 150, 0.2);
background: transparent;
}
变形宽银幕光晕(电影效果)
.anamorphic-flare {
position: absolute;
width: 100%;
height: 4px;
top: 50%;
transform: translateY(-50%);
background: linear-gradient(
90deg,
transparent 0%,
rgba(100, 180, 255, 0.3) 20%,
rgba(255, 255, 255, 0.6) 50%,
rgba(100, 180, 255, 0.3) 80%,
transparent 100%
);
filter: blur(2px);
animation: anamorphic-pulse 4s ease-in-out infinite;
}
@keyframes anamorphic-pulse {
0%, 100% {
opacity: 0.4;
transform: translateY(-50%) scaleX(0.8);
}
50% {
opacity: 0.7;
transform: translateY(-50%) scaleX(1.2);
}
}
跟随鼠标的动态光晕
function createDynamicFlare(container, sourcePosition) {
const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
const direction = {
x: center.x - sourcePosition.x,
y: center.y - sourcePosition.y
};
// Create artifacts along the flare line
const artifactCount = 5;
for (let i = 0; i < artifactCount; i++) {
const t = (i + 1) / (artifactCount + 1);
const artifact = document.createElement('div');
artifact.className = 'flare-artifact hexagon';
artifact.style.left = `${sourcePosition.x + direction.x * t * 1.5}px`;
artifact.style.top = `${sourcePosition.y + direction.y * t * 1.5}px`;
artifact.style.opacity = 0.3 - (i * 0.05);
artifact.style.transform = `scale(${1 - i * 0.15})`;
container.appendChild(artifact);
}
}
SECTION J:海洋飞沫与水花
基于粒子的飞沫
.spray-container {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 40%;
overflow: hidden;
pointer-events: none;
}
.spray-particle {
position: absolute;
width: var(--size, 4px);
height: var(--size, 4px);
border-radius: 50%;
background: radial-gradient(
circle,
rgba(255, 255, 255, 0.9) 0%,
rgba(200, 230, 255, 0.6) 50%,
transparent 100%
);
animation: spray-rise var(--duration, 1s) ease-out forwards;
animation-delay: var(--delay, 0s);
}
@keyframes spray-rise {
0% {
transform: translateY(0) scale(1);
opacity: 0.8;
}
50% {
opacity: 0.6;
}
100% {
transform: translateY(-100px) translateX(var(--drift, 20px)) scale(0.3);
opacity: 0;
}
}
飞沫生成器
function createSpray(container, intensity = 'medium') {
const config = {
light: { count: 20, maxSize: 6, maxDuration: 1.2 },
medium: { count: 50, maxSize: 10, maxDuration: 1.5 },
heavy: { count: 100, maxSize: 15, maxDuration: 2 }
}[intensity];
for (let i = 0; i < config.count; i++) {
const particle = document.createElement('div');
particle.className = 'spray-particle';
particle.style.setProperty('--size', `${2 + Math.random() * config.maxSize}px`);
particle.style.setProperty('--duration', `${0.5 + Math.random() * config.maxDuration}s`);
particle.style.setProperty('--delay', `${Math.random() * 0.5}s`);
particle.style.setProperty('--drift', `${(Math.random() - 0.5) * 60}px`);
particle.style.left = `${Math.random() * 100}%`;
particle.style.bottom = '0';
container.appendChild(particle);
// Remove after animation
setTimeout(() => particle.remove(), (0.5 + config.maxDuration) * 1000 + 500);
}
}
撞击水花环
.splash-ring {
position: absolute;
border-radius: 50%;
border: 3px solid rgba(255, 255, 255, 0.6);
background: transparent;
animation: splash-expand 0.8s ease-out forwards;
}
@keyframes splash-expand {
0% {
width: 10px;
height: 10px;
opacity: 1;
transform: translate(-50%, -50%) rotateX(70deg);
}
100% {
width: 150px;
height: 150px;
opacity: 0;
transform: translate(-50%, -50%) rotateX(70deg);
}
}
SECTION K:海滩冲刷与泡沫
岸边冲刷动画
<svg class="beach-wave" viewBox="0 0 1440 200" preserveAspectRatio="none">
<defs>
<linearGradient id="foam-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="rgba(255,255,255,0.9)" />
<stop offset="30%" stop-color="rgba(200,230,255,0.6)" />
<stop offset="100%" stop-color="rgba(56,189,248,0.4)" />
</linearGradient>
<filter id="foam-texture">
<feTurbulence type="turbulence" baseFrequency="0.02 0.05" numOctaves="3" />
<feDisplacementMap in="SourceGraphic" scale="5" />
</filter>
</defs>
<path
class="wave-foam"
fill="url(#foam-gradient)"
filter="url(#foam-texture)"
>
<animate
attributeName="d"
dur="4s"
repeatCount="indefinite"
values="
M0,100 Q360,50 720,100 T1440,100 L1440,200 L0,200 Z;
M0,80 Q360,130 720,80 T1440,80 L1440,200 L0,200 Z;
M0,100 Q360,50 720,100 T1440,100 L1440,200 L0,200 Z
"
/>
</path>
</svg>
.beach-container {
position: relative;
height: 300px;
overflow: hidden;
}
.wet-sand {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 100px;
background: linear-gradient(
to top,
#c2b280 0%,
#d4c896 50%,
#e6dbb0 100%
);
}
.wet-sand::before {
content: '';
position: absolute;
inset: 0;
background: rgba(56, 189, 248, 0.2);
animation: wet-recede 4s ease-in-out infinite;
}
@keyframes wet-recede {
0%, 100% {
clip-path: polygon(0 0, 100% 0, 100% 80%, 0 60%);
}
50% {
clip-path: polygon(0 30%, 100% 50%, 100% 100%, 0 100%);
}
}
泡沫气泡细节
.foam-bubbles {
position: absolute;
width: 100%;
height: 30px;
bottom: 60px;
}
.foam-bubble {
position: absolute;
width: var(--size, 8px);
height: var(--size, 8px);
border-radius: 50%;
background: radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.9) 0%,
rgba(200, 230, 255, 0.5) 50%,
transparent 100%
);
animation: bubble-pop var(--duration, 2s) ease-out infinite;
animation-delay: var(--delay, 0s);
}
@keyframes bubble-pop {
0% {
transform: scale(1);
opacity: 0.8;
}
80% {
transform: scale(1.1);
opacity: 0.6;
}
100% {
transform: scale(0);
opacity: 0;
}
}
退潮波痕
.wave-trail {
position: absolute;
bottom: 60px;
left: 0;
right: 0;
height: 5px;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.6) 10%,
rgba(255, 255, 255, 0.8) 50%,
rgba(255, 255, 255, 0.6) 90%,
transparent 100%
);
animation: trail-recede 4s ease-in-out infinite;
}
@keyframes trail-recede {
0% {
transform: translateY(0) scaleX(1);
opacity: 0.8;
}
50% {
transform: translateY(-30px) scaleX(0.95);
opacity: 0.4;
}
100% {
transform: translateY(0) scaleX(1);
opacity: 0.8;
}
}
SECTION L:性能优化
性能等级
/* Tier 1: Ultra Performance (CSS-only) */
@media (prefers-reduced-motion: reduce) {
.weather-effect {
animation: none !important;
filter: none !important;
}
}
/* Tier 2: High Performance (static filters) */
.weather-effect--performance {
filter: url(#static-filter);
animation-play-state: paused;
}
/* Tier 3: Full Effects */
.weather-effect--full {
filter: url(#animated-filter);
animation-play-state: running;
}
GPU 加速
.weather-layer {
/* Force GPU compositing */
will-change: transform, opacity;
transform: translateZ(0);
backface-visibility: hidden;
/* Avoid triggering repaints */
contain: layout paint;
}
/* Avoid animating these properties */
.weather-layer {
/* BAD: causes repaints */
/* animation: filter-change 10s; */
/* GOOD: GPU-accelerated */
animation: transform-change 10s;
}
使用 Intersection Observer 处理屏幕外元素
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const effect = entry.target;
if (entry.isIntersecting) {
effect.classList.add('weather-effect--active');
} else {
effect.classList.remove('weather-effect--active');
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.weather-effect').forEach(el => observer.observe(el));
移动设备优化
@media (max-width: 768px) {
/* Reduce layer count on mobile */
.cloud-back,
.fog-back {
display: none;
}
/* Simplify filters */
.weather-filter {
filter: blur(var(--blur)) opacity(0.5);
}
/* Reduce particle count */
.rain-container {
--drop-count: 50;
}
}
SECTION M:React 集成
天气效果提供器
import React, { createContext, useContext, useState, useEffect } from 'react';
type WeatherType =
| 'clear'
| 'cloudy'
| 'rainy'
| 'stormy'
| 'foggy'
| 'snowy'
| 'aurora';
type TimeOfDay = 'dawn' | 'morning' | 'noon' | 'afternoon' | 'dusk' | 'night';
interface WeatherContextType {
weather: WeatherType;
timeOfDay: TimeOfDay;
intensity: number; // 0-1
setWeather: (w: WeatherType) => void;
setTimeOfDay: (t: TimeOfDay) => void;
setIntensity: (i: number) => void;
}
const WeatherContext = createContext<WeatherContextType | null>(null);
export function WeatherProvider({ children }: { children: React.ReactNode }) {
const [weather, setWeather] = useState<WeatherType>('clear');
const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('noon');
const [intensity, setIntensity] = useState(0.5);
// Auto-detect time of day
useEffect(() => {
const hour = new Date().getHours();
if (hour >= 5 && hour < 7) setTimeOfDay('dawn');
else if (hour >= 7 && hour < 12) setTimeOfDay('morning');
else if (hour >= 12 && hour < 14) setTimeOfDay('noon');
else if (hour >= 14 && hour < 17) setTimeOfDay('afternoon');
else if (hour >= 17 && hour < 20) setTimeOfDay('dusk');
else setTimeOfDay('night');
}, []);
return (
<WeatherContext.Provider value={{
weather, timeOfDay, intensity,
setWeather, setTimeOfDay, setIntensity
}}>
{children}
</WeatherContext.Provider>
);
}
export const useWeather = () => {
const context = useContext(WeatherContext);
if (!context) throw new Error('useWeather must be used within WeatherProvider');
return context;
};
大气效果 Canvas 组件
import React from 'react';
import { useWeather } from './WeatherProvider';
import { CloudLayer } from './CloudLayer';
import { RainEffect } from './RainEffect';
import { LightningEffect } from './LightningEffect';
import { FogLayer } from './FogLayer';
import { AuroraEffect } from './AuroraEffect';
import { SkyGradient } from './SkyGradient';
export function AtmosphericCanvas() {
const { weather, timeOfDay, intensity } = useWeather();
return (
<div className="atmospheric-canvas">
{/* SVG Filters */}
<svg width="0" height="0" aria-hidden="true">
<defs>
<filter id="cloud-filter">...</filter>
<filter id="fog-filter">...</filter>
<filter id="wave-filter">...</filter>
</defs>
</svg>
{/* Sky base */}
<SkyGradient timeOfDay={timeOfDay} />
{/* Weather layers */}
{(weather === 'cloudy' || weather === 'rainy' || weather === 'stormy') && (
<CloudLayer variant={weather === 'stormy' ? 'storm' : 'cumulus'} />
)}
{weather === 'foggy' && <FogLayer intensity={intensity} />}
{weather === 'aurora' && <AuroraEffect intensity={intensity} />}
{(weather === 'rainy' || weather === 'stormy') && (
<RainEffect intensity={weather === 'stormy' ? 1 : intensity} />
)}
{weather === 'stormy' && <LightningEffect frequency={intensity} />}
</div>
);
}
单独的效果组件
// CloudLayer.tsx
interface CloudLayerProps {
variant: 'cumulus' | 'cirrus' | 'stratus' | 'storm';
opacity?: number;
}
export function CloudLayer({ variant, opacity = 0.7 }: CloudLayerProps) {
const configs = {
cumulus: { baseFrequency: 0.008, blur: 4, layers: 3 },
cirrus: { baseFrequency: 0.003, blur: 2, layers: 2 },
stratus: { baseFrequency: 0.015, blur: 6, layers: 2 },
storm: { baseFrequency: 0.006, blur: 3, layers: 4 }
};
const config = configs[variant];
return (
<div className="cloud-system" style={{ '--opacity': opacity } as React.CSSProperties}>
{Array.from({ length: config.layers }, (_, i) => (
<div
key={i}
className={`cloud-layer cloud-layer-${i}`}
style={{
'--layer-index': i,
'--animation-duration': `${50 + i * 30}s`
} as React.CSSProperties}
/>
))}
</div>
);
}
SECTION N:Tailwind 集成
自定义 Tailwind 工具类
// tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
'drift': 'drift 60s linear infinite',
'drift-slow': 'drift 120s linear infinite',
'drift-fast': 'drift 30s linear infinite',
'wave': 'wave 10s ease-in-out infinite',
'rain-fall': 'rain-fall 0.8s linear infinite',
'lightning': 'lightning-flash 0.5s ease-out',
'aurora': 'aurora-wave 15s ease-in-out infinite',
'fog-breathe': 'fog-breathe 8s ease-in-out infinite',
},
keyframes: {
drift: {
'0%': { transform: 'translateX(-10%)' },
'100%': { transform: 'translateX(10%)' },
},
wave: {
'0%, 100%': { transform: 'translateY(0)' },
'50%': { transform: 'translateY(-10px)' },
},
'rain-fall': {
'0%': { transform: 'translateY(-100vh)' },
'100%': { transform: 'translateY(100vh)' },
},
'lightning-flash': {
'0%': { opacity: '0' },
'10%': { opacity: '1' },
'20%': { opacity: '0.5' },
'30%': { opacity: '1' },
'100%': { opacity: '0' },
},
'aurora-wave': {
'0%, 100%': { transform: 'translateX(-20%) skewX(-5deg)', opacity: '0.4' },
'50%': { transform: 'translateX(0%) skewX(5deg)', opacity: '0.7' },
},
'fog-breathe': {
'0%, 100%': { opacity: '0.5', transform: 'scaleY(1)' },
'50%': { opacity: '0.7', transform: 'scaleY(1.1)' },
},
},
backdropBlur: {
'fog': '20px',
'mist': '10px',
},
},
},
plugins: [
function({ addUtilities }) {
addUtilities({
'.filter-cloud': {
filter: 'url(#cloud-filter)',
},
'.filter-fog': {
filter: 'url(#fog-filter)',
},
'.filter-wave': {
filter: 'url(#wave-filter)',
},
'.mix-screen': {
mixBlendMode: 'screen',
},
'.mix-overlay': {
mixBlendMode: 'overlay',
},
});
},
],
};
Tailwind 组件类
<div class="absolute inset-0 opacity-50 filter-cloud animate-drift-slow"></div>
<div class="absolute inset-0 overflow-hidden pointer-events-none">
<div class="w-0.5 h-5 bg-gradient-to-b from-transparent via-sky-200/50 to-sky-300/80 rounded-b animate-rain-fall"></div>
</div>
<div class="absolute inset-0 bg-white/30 backdrop-blur-fog filter-fog animate-fog-breathe"></div>
<div class="absolute top-1/4 -left-1/2 w-[200%] h-2/5 opacity-60 mix-screen blur-3xl animate-aurora bg-gradient-to-r from-transparent via-emerald-400/40 to-transparent"></div>
快速参考卡
效果选择矩阵
| 效果 | 最适合用于 | 复杂度 | 性能 |
|---|---|---|---|
| 云 | 主视觉区域、页眉 | 中 | 高 |
| 波浪 | 页脚、分区分隔线 | 中 | 高 |
| 闪电 | 戏剧性时刻、游戏 | 低 | 非常高 |
| 雨 | 氛围、情绪营造 | 中 | 中 |
| 雾 | 神秘感、纵深感 | 低 | 高 |
| 极光 | 夜景、奇幻场景 | 高 | 中 |
| 耶稣光 | 神圣感、电影效果 | 低 | 高 |
| 镜头光晕 | 写实、摄影 | 中 | 高 |
| 海洋飞沫 | 海滩、动作场景 | 高 | 低 |
| 海滩冲刷 | 海岸、平静场景 | 中 | 中 |
| 暮光天空 | 时间提示 | 低 | 非常高 |
常见组合
| 场景 | 效果 | 备注 |
|---|---|---|
| 戏剧性风暴 | 风暴云 + 雨 + 闪电 | 错开闪电的出现时间 |
| 宁静海滩 | 浅云 + 海滩冲刷 + 耶稣光 | 放慢动画 |
| 雾蒙蒙的清晨 | 雾 + 地面薄雾 + 柔和云层 | 降低饱和度 |
| 北境夜色 | 极光 + 星星 + 轻雾 | 深色背景必不可少 |
| 电影感时刻 | 耶稣光 + 镜头光晕 + 轻薄霾 | 不要过度使用光晕 |
性能预算
| 设备 | 最大图层数 | 最大粒子数 | 滤镜复杂度 |
|---|---|---|---|
| 桌面设备 | 5-7 | 200 | 完整 |
| 平板设备 | 3-5 | 100 | 简化 |
| 移动设备 | 2-3 | 50 | 最低 |
资源与参考
参考教程
工具
代码合集
版本历史
- v1.0.0(2026-01-25):由 web-cloud-designer 和 web-wave-designer 合并而来,新增闪电、雨、雾、暮光、极光、上帝光、镜头光晕、海洋飞沫和海滩冲刷效果
请记住:风格化天气效果的关键在于克制与层次。一个精心打造的效果胜过五个平庸的效果。如有疑虑,就降低强度、增强微妙感。
评论
支持 Markdown