```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Premium Weather Cards</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=SF+Pro+Display:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #0a0a0f;
--bg-secondary: #12121a;
--text-primary: #ffffff;
--text-secondary: rgba(255,255,255,0.6);
--glass-bg: rgba(255,255,255,0.05);
--glass-border: rgba(255,255,255,0.08);
--card-radius: 28px;
--sunny-warm: #ff9500;
--rain-blue: #5ac8fa;
--snow-white: #e8e8ed;
--wind-gray: #8e8e93;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', sans-serif;
background: var(--bg-primary);
min-height: 100vh;
color: var(--text-primary);
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
}
/* ============ Ambient Background ============ */
.ambient-bg {
position: fixed;
inset: 0;
z-index: 0;
overflow: hidden;
}
.ambient-orb {
position: absolute;
border-radius: 50%;
filter: blur(120px);
opacity: 0.15;
animation: orbFloat 20s ease-in-out infinite;
}
.ambient-orb:nth-child(1) {
width: 600px; height: 600px;
background: radial-gradient(circle, #5856d6, transparent);
top: -200px; left: -100px;
animation-delay: 0s;
}
.ambient-orb:nth-child(2) {
width: 500px; height: 500px;
background: radial-gradient(circle, #ff9500, transparent);
bottom: -200px; right: -100px;
animation-delay: -7s;
}
.ambient-orb:nth-child(3) {
width: 400px; height: 400px;
background: radial-gradient(circle, #30d158, transparent);
top: 50%; left: 50%;
transform: translate(-50%, -50%);
animation-delay: -14s;
}
@keyframes orbFloat {
0%, 100% { transform: translate(0, 0) scale(1); }
25% { transform: translate(40px, -30px) scale(1.1); }
50% { transform: translate(-20px, 20px) scale(0.95); }
75% { transform: translate(30px, 40px) scale(1.05); }
}
/* ============ Layout ============ */
.app-container {
position: relative;
z-index: 1;
max-width: 1400px;
margin: 0 auto;
padding: 40px 24px 60px;
}
/* ============ Header ============ */
.header {
text-align: center;
margin-bottom: 20px;
}
.header-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 16px;
border-radius: 100px;
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.08);
font-size: 12px;
font-weight: 500;
color: var(--text-secondary);
letter-spacing: 0.5px;
text-transform: uppercase;
margin-bottom: 20px;
backdrop-filter: blur(10px);
}
.header-badge .dot {
width: 6px; height: 6px;
border-radius: 50%;
background: #30d158;
box-shadow: 0 0 8px #30d158;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.header h1 {
font-size: clamp(32px, 5vw, 52px);
font-weight: 700;
letter-spacing: -1.5px;
line-height: 1.1;
margin-bottom: 12px;
background: linear-gradient(135deg, #fff 0%, rgba(255,255,255,0.7) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.header p {
font-size: 16px;
color: var(--text-secondary);
font-weight: 400;
max-width: 500px;
margin: 0 auto;
line-height: 1.5;
}
/* ============ Time & Location Bar ============ */
.info-bar {
display: flex;
justify-content: center;
align-items: center;
gap: 24px;
margin: 28px 0 36px;
flex-wrap: wrap;
}
.info-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--text-secondary);
}
.info-item svg {
width: 16px; height: 16px;
opacity: 0.5;
}
/* ============ Control Strip ============ */
.control-strip {
display: flex;
justify-content: center;
gap: 8px;
margin-bottom: 40px;
flex-wrap: wrap;
}
.ctrl-btn {
padding: 10px 22px;
border-radius: 100px;
border: 1px solid rgba(255,255,255,0.1);
background: rgba(255,255,255,0.04);
color: var(--text-secondary);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
backdrop-filter: blur(10px);
font-family: inherit;
letter-spacing: 0.3px;
position: relative;
overflow: hidden;
}
.ctrl-btn::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
opacity: 0;
transition: opacity 0.4s;
}
.ctrl-btn:hover {
background: rgba(255,255,255,0.1);
border-color: rgba(255,255,255,0.2);
color: #fff;
transform: translateY(-1px);
}
.ctrl-btn.active {
background: rgba(255,255,255,0.15);
border-color: rgba(255,255,255,0.25);
color: #fff;
box-shadow: 0 4px 20px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.1);
}
.ctrl-btn.active[data-weather="sunny"] {
background: rgba(255,149,0,0.2);
border-color: rgba(255,149,0,0.4);
color: #ffb340;
}
.ctrl-btn.active[data-weather="rainy"] {
background: rgba(90,200,250,0.15);
border-color: rgba(90,200,250,0.3);
color: #5ac8fa;
}
.ctrl-btn.active[data-weather="snowy"] {
background: rgba(200,200,220,0.15);
border-color: rgba(200,200,220,0.3);
color: #d1d1d6;
}
.ctrl-btn.active[data-weather="windy"] {
background: rgba(142,142,147,0.2);
border-color: rgba(142,142,147,0.3);
color: #aeaeb2;
}
/* ============ Cards Grid ============ */
.cards-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
@media (max-width: 1100px) {
.cards-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
.cards-grid { grid-template-columns: 1fr; }
}
/* ============ Weather Card ============ */
.weather-card {
position: relative;
border-radius: var(--card-radius);
overflow: hidden;
height: 480px;
cursor: pointer;
transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94),
box-shadow 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
will-change: transform;
}
.weather-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 30px 80px -20px rgba(0,0,0,0.6);
}
.weather-card::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
border: 1px solid var(--glass-border);
z-index: 5;
pointer-events: none;
}
.weather-card::after {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
z-index: 4;
pointer-events: none;
background: linear-gradient(180deg,
transparent 40%,
rgba(0,0,0,0.5) 100%);
}
/* Card Backgrounds */
.card-bg {
position: absolute;
inset: 0;
z-index: 0;
transition: opacity 1s;
}
/* Sunny BG */
.card-sunny .card-bg {
background: linear-gradient(160deg, #1c1402 0%, #2d1e06 30%, #1a1205 70%, #0f0e09 100%);
}
/* Rainy BG */
.card-rainy .card-bg {
background: linear-gradient(160deg, #0a1628 0%, #0d1a2d 30%, #091420 70%, #060d15 100%);
}
/* Snowy BG */
.card-snowy .card-bg {
background: linear-gradient(160deg, #151520 0%, #1a1a2e 30%, #141425 70%, #0e0e1a 100%);
}
/* Windy BG */
.card-windy .card-bg {
background: linear-gradient(160deg, #12161a 0%, #181d22 30%, #13171b 70%, #0c0e11 100%);
}
/* ============ Canvas Layer ============ */
.weather-canvas {
position: absolute;
inset: 0;
z-index: 2;
width: 100%;
height: 100%;
}
/* ============ Card Content ============ */
.card-content {
position: relative;
z-index: 6;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 28px 24px;
}
.card-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.card-location {
font-size: 13px;
font-weight: 500;
color: rgba(255,255,255,0.5);
letter-spacing: 0.5px;
}
.card-icon-badge {
width: 40px; height: 40px;
border-radius: 50%;
background: rgba(255,255,255,0.1);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(255,255,255,0.1);
}
.card-icon-badge svg {
width: 20px; height: 20px;
}
.card-bottom {
display: flex;
flex-direction: column;
gap: 6px;
}
.card-temp {
font-size: 72px;
font-weight: 200;
letter-spacing: -4px;
line-height: 1;
font-variant-numeric: tabular-nums;
}
.card-temp .degree {
font-size: 32px;
font-weight: 300;
vertical-align: super;
letter-spacing: 0;
opacity: 0.6;
}
.card-condition {
font-size: 20px;
font-weight: 500;
margin-bottom: 4px;
letter-spacing: -0.3px;
}
.card-detail-row {
display: flex;
gap: 16px;
margin-top: 8px;
}
.card-detail {
display: flex;
align-items: center;
gap: 5px;
font-size: 12px;
color: rgba(255,255,255,0.45);
font-weight: 400;
}
.card-detail svg {
width: 14px; height: 14px;
opacity: 0.5;
}
/* ============ Sunny Specific ============ */
.sun-glow {
position: absolute;
top: -60px;
right: -60px;
width: 260px;
height: 260px;
border-radius: 50%;
background: radial-gradient(circle,
rgba(255,180,0,0.35) 0%,
rgba(255,149,0,0.15) 30%,
rgba(255,100,0,0.05) 60%,
transparent 70%);
z-index: 1;
animation: sunPulse 4s ease-in-out infinite;
}
.sun-core {
position: absolute;
top: -15px;
right: -15px;
width: 140px;
height: 140px;
border-radius: 50%;
background: radial-gradient(circle,
rgba(255,214,100,0.8) 0%,
rgba(255,180,0,0.4) 40%,
transparent 70%);
z-index: 1;
animation: sunPulse 4s ease-in-out infinite;
filter: blur(2px);
}
@keyframes sunPulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.08); opacity: 0.85; }
}
.sun-rays {
position: absolute;
top: 15px;
right: 15px;
width: 100px;
height: 100px;
z-index: 1;
animation: sunRotate 30s linear infinite;
}
@keyframes sunRotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.sun-ray {
position: absolute;
top: 50%;
left: 50%;
width: 2px;
height: 45px;
background: linear-gradient(to bottom, rgba(255,200,50,0.5), transparent);
transform-origin: top center;
border-radius: 1px;
}
/* Light streaks for sunny */
.light-streak {
position: absolute;
z-index: 1;
background: linear-gradient(90deg, transparent, rgba(255,200,60,0.06), transparent);
height: 1px;
animation: streakMove 8s ease-in-out infinite;
}
@keyframes streakMove {
0%, 100% { opacity: 0; transform: translateX(-50px); }
50% { opacity: 1; transform: translateX(50px); }
}
/* ============ Rainy Specific ============ */
.rain-cloud {
position: absolute;
z-index: 1;
border-radius: 50%;
background: rgba(30,50,80,0.5);
filter: blur(15px);
}
.puddle {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60px;
z-index: 3;
background: linear-gradient(to top,
rgba(30,80,140,0.15) 0%,
transparent 100%);
}
.puddle-ripple {
position: absolute;
border-radius: 50%;
border: 1px solid rgba(90,200,250,0.15);
animation: ripple 2s ease-out infinite;
z-index: 3;
}
@keyframes ripple {
0% { width: 0; height: 0; opacity: 0.6; }
100% { width: 40px; height: 12px; opacity: 0; }
}
/* ============ Snowy Specific ============ */
.snow-ground {
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 3;
pointer-events: none;
}
.snow-layer {
position: absolute;
bottom: 0;
left: -5%;
right: -5%;
background: white;
border-radius: 50% 50% 0 0;
}
.snow-layer:nth-child(1) {
height: 45px;
background: rgba(220,220,235,0.25);
bottom: 20px;
filter: blur(2px);
}
.snow-layer:nth-child(2) {
height: 30px;
background: rgba(230,230,240,0.2);
bottom: 5px;
border-radius: 40% 60% 0 0;
}
.snow-layer:nth-child(3) {
height: 25px;
background: rgba(240,240,250,0.15);
bottom: 0px;
border-radius: 55% 45% 0 0;
}
.frost-overlay {
position: absolute;
inset: 0;
z-index: 1;
background: radial-gradient(ellipse at bottom,
rgba(200,200,230,0.05) 0%,
transparent 60%);
pointer-events: none;
}
/* ============ Windy Specific ============ */
.wind-line {
position: absolute;
z-index: 1;
height: 1.5px;
border-radius: 1px;
background: linear-gradient(90deg,
transparent 0%,
rgba(180,180,190,0.2) 30%,
rgba(180,180,190,0.1) 70%,
transparent 100%);
animation: windSweep 3s ease-in-out infinite;
}
@keyframes windSweep {
0% { transform: translateX(-120%) scaleX(0.5); opacity: 0; }
30% { opacity: 1; scaleX: 1; }
100% { transform: translateX(120%) scaleX(0.8); opacity: 0; }
}
.cloud-shape {
position: absolute;
z-index: 1;
background: rgba(100,110,120,0.08);
border-radius: 50px;
filter: blur(8px);
animation: cloudDrift 12s ease-in-out infinite;
}
@keyframes cloudDrift {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(30px); }
}
/* Swaying tree */
.tree-container {
position: absolute;
bottom: 20px;
z-index: 3;
}
.tree-trunk {
width: 3px;
height: 40px;
background: rgba(120,100,80,0.4);
margin: 0 auto;
border-radius: 2px;
transform-origin: bottom center;
animation: treeSway 3s ease-in-out infinite;
}
.tree-crown {
width: 30px;
height: 25px;
background: rgba(60,80,60,0.3);
border-radius: 50% 50% 40% 40%;
margin: -5px auto 0;
transform-origin: bottom center;
animation: treeSway 3s ease-in-out infinite;
animation-delay: 0.2s;
}
@keyframes treeSway {
0%, 100% { transform: rotate(-4deg); }
50% { transform: rotate(6deg); }
}
/* ============ Status indicator ============ */
.status-dot {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 2px;
vertical-align: middle;
}
.card-sunny .status-dot { background: var(--sunny-warm); box-shadow: 0 0 6px var(--sunny-warm); }
.card-rainy .status-dot { background: var(--rain-blue); box-shadow: 0 0 6px var(--rain-blue); }
.card-snowy .status-dot { background: var(--snow-white); box-shadow: 0 0 6px var(--snow-white); }
.card-windy .status-dot { background: var(--wind-gray); box-shadow: 0 0 6px var(--wind-gray); }
/* ============ Footer Info ============ */
.footer-info {
text-align: center;
margin-top: 40px;
padding: 24px;
}
.footer-info p {
font-size: 12px;
color: rgba(255,255,255,0.2);
letter-spacing: 0.5px;
}
/* ============ Smooth transitions on all cards ============ */
.weather-card, .weather-card * {
-webkit-backface-visibility: hidden;
}
/* Highlighted card state */
.weather-card.highlighted {
z-index: 10;
}
.weather-card.dimmed {
opacity: 0.35;
transform: scale(0.97);
filter: brightness(0.6);
transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.weather-card.highlighted {
transform: translateY(-12px) scale(1.03);
box-shadow: 0 40px 100px -20px rgba(0,0,0,0.7);
}
/* ============ Particle Mist for transitions ============ */
.golden-particles, .blue-mist, .snow-mist, .wind-dust {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
}
/* Card shimmer effect on hover */
.card-shimmer {
position: absolute;
inset: 0;
z-index: 5;
pointer-events: none;
opacity: 0;
transition: opacity 0.5s;
background: linear-gradient(
105deg,
transparent 40%,
rgba(255,255,255,0.03) 45%,
rgba(255,255,255,0.06) 50%,
rgba(255,255,255,0.03) 55%,
transparent 60%
);
background-size: 200% 100%;
animation: shimmerSlide 3s ease-in-out infinite;
}
.weather-card:hover .card-shimmer { opacity: 1; }
@keyframes shimmerSlide {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* ============ Enhanced card inner glow ============ */
.card-sunny .card-inner-glow {
position: absolute;
top: -50%;
right: -30%;
width: 80%;
height: 80%;
border-radius: 50%;
background: radial-gradient(circle, rgba(255,180,0,0.08), transparent 70%);
z-index: 1;
}
.card-rainy .card-inner-glow {
position: absolute;
top: -30%;
left: 10%;
width: 80%;
height: 60%;
border-radius: 50%;
background: radial-gradient(circle, rgba(40,80,140,0.12), transparent 70%);
z-index: 1;
}
.card-snowy .card-inner-glow {
position: absolute;
top: -20%;
left: 20%;
width: 60%;
height: 60%;
border-radius: 50%;
background: radial-gradient(circle, rgba(180,180,220,0.06), transparent 70%);
z-index: 1;
}
.card-windy .card-inner-glow {
position: absolute;
top: 20%;
right: -20%;
width: 60%;
height: 50%;
border-radius: 50%;
background: radial-gradient(circle, rgba(120,130,140,0.06), transparent 70%);
z-index: 1;
}
/* ============ Micro-interactions ============ */
.card-sunny .card-temp { color: #ffd060; }
.card-rainy .card-temp { color: #8dd4f0; }
.card-snowy .card-temp { color: #d0d0e0; }
.card-windy .card-temp { color: #b0b8c0; }
.card-sunny .card-condition { color: rgba(255,200,80,0.9); }
.card-rainy .card-condition { color: rgba(130,200,240,0.9); }
.card-snowy .card-condition { color: rgba(200,200,230,0.9); }
.card-windy .card-condition { color: rgba(170,180,190,0.9); }
/* ============ Lightning Flash ============ */
.lightning-flash {
position: absolute;
inset: 0;
z-index: 3;
background: rgba(200,220,255,0.15);
opacity: 0;
pointer-events: none;
border-radius: inherit;
}
@keyframes lightningFlash {
0% { opacity: 0; }
5% { opacity: 1; }
10% { opacity: 0; }
12% { opacity: 0.7; }
15% { opacity: 0; }
100% { opacity: 0; }
}
</style>
</head>
<body>
<div class="ambient-bg">
<div class="ambient-orb"></div>
<div class="ambient-orb"></div>
<div class="ambient-orb"></div>
</div>
<div class="app-container">
<header class="header">
<div class="header-badge">
<span class="dot"></span>
实时天气 · LIVE
</div>
<h1>Weather Animations</h1>
<p>精致沉浸式天气可视化 · 动态粒子渲染引擎</p>
</header>
<div class="info-bar">
<div class="info-item">
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z"/><path d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z"/></svg>
<span id="locationText">上海, 中国</span>
</div>
<div class="info-item">
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
<span id="timeText"></span>
</div>
<div class="info-item">
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z"/></svg>
<span>AQI 42 · 优</span>
</div>
</div>
<div class="control-strip">
<button class="ctrl-btn" data-weather="all" onclick="highlightWeather('all')">
✦ 全部展示
</button>
<button class="ctrl-btn" data-weather="sunny" onclick="highlightWeather('sunny')">
☀ 晴天
</button>
<button class="ctrl-btn" data-weather="rainy" onclick="highlightWeather('rainy')">
🌧 降雨
</button>
<button class="ctrl-btn" data-weather="snowy" onclick="highlightWeather('snowy')">
❄ 降雪
</button>
<button class="ctrl-btn" data-weather="windy" onclick="highlightWeather('windy')">
💨 大风
</button>
</div>
<div class="cards-grid" id="cardsGrid">
<!-- ===== SUNNY CARD ===== -->
<div class="weather-card card-sunny" id="card-sunny" data-type="sunny">
<div class="card-bg"></div>
<div class="card-inner-glow"></div>
<div class="sun-glow"></div>
<div class="sun-core"></div>
<div class="sun-rays" id="sunRays"></div>
<canvas class="weather-canvas" id="sunnyCanvas"></canvas>
<div class="card-shimmer"></div>
<div class="card-content">
<div class="card-top">
<span class="card-location">上海 · 陆家嘴</span>
<div class="card-icon-badge">
<svg fill="none" stroke="#ffb340" stroke-width="1.5" viewBox="0 0 24 24">
<path d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"/>
</svg>
</div>
</div>
<div class="card-bottom">
<div class="card-temp">32<span class="degree">°</span></div>
<div class="card-condition"><span class="status-dot"></span> 晴朗</div>
<div class="card-detail-row">
<div class="card-detail">
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path d="M2.25 15a4.5 4.5 0 004.5 4.5H18a3.75 3.75 0 001.332-7.257 3 3 0 00-3.758-3.848 5.25 5.25 0 00-10.233 2.33A4.502 4.502 0 002.25 15z"/></svg>
UV 极强
</div>
<div class="card-detail">
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path d="M12 6v6h4.5"/></svg>
体感 36°
</div>
<div class="card-detail">湿度 45%</div>
</div>
</div>
</div>
</div>
<!-- ===== RAINY CARD ===== -->
<div class="weather-card card-rainy" id="card-rainy" data-type="rainy">
<div class="card-bg"></div>
<div class="card-inner-glow"></div>
<div class="rain-cloud" style="top:-20px;left:10%;width:160px;height:60px;"></div>
<div class="rain-cloud" style="top:10px;left:40%;width:200px;height:50px;opacity:0.6;"></div>
<canvas class="weather-canvas" id="rainyCanvas"></canvas>
<div class="lightning-flash" id="lightningFlash"></div>
<div class="puddle"></div>
<div class="card-shimmer"></div>
<div class="card-content">
<div class="card-top">
<span class="card-location">上海 · 外滩</span>
<div class="card-icon-badge">
<svg fill="none" stroke="#5ac8fa" stroke-width="1.5" viewBox="0 0 24 24">
<path d="M2.25 15a4.5 4.5 0 004.5 4.5H18a3.75 3.75 0 001.332-7.257 3 3 0 00-3.758-3.848 5.25 5.25 0 00-10.233 2.33A4.502 4.502 0 002.25 15z"/>
</svg>
</div>
</div>
<div class="card-bottom">
<div class="card-temp">18<span class="degree">°</span></div>
<div class="card-condition"><span class="status-dot"></span> 暴雨</div>
<div class="card-detail-row">
<div class="card-detail">
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path d="M2.25 15a4.5 4.5 0 004.5 4.5H18a3.75 3.75 0 001.332-7.257 3 3 0 00-3.758-3.848 5.25 5.25 0 00-10.233 2.33A4.502 4.502 0 002.25 15z"/></svg>
降水 85%
</div>
<div class="card-detail">风速 24km/h</div>
<div class="card-detail">湿度 92%</div>
</div>
</div>
</div>
</div>
<!-- ===== SNOWY CARD ===== -->
<div class="weather-card card-snowy" id="card-snowy" data-type="snowy">
<div class="card-bg"></div>
<div class="card-inner-glow"></div>
<div class="frost-overlay"></div>
<canvas class="weather-canvas" id="snowyCanvas"></canvas>
<div class="snow-ground">
<div class="snow-layer"></div>
<div class="snow-layer"></div>
<div class="snow-layer"></div>
</div>
<div class="card-shimmer"></div>
<div class="card-content">
<div class="card-top">
<span class="card-location">哈尔滨 · 冰雪大世界</span>
<div class="card-icon-badge">
<svg fill="none" stroke="#d0d0e0" stroke-width="1.5" viewBox="0 0 24 24">
<path d="M12 3v18m-6-6l6 6 6-6m-9-3l3 3 3-3M6 9l6-6 6 6"/>
</svg>
</div>
</div>
<div class="card-bottom">
<div class="card-temp">-8<span class="degree">°</span></div>
<div class="card-condition"><span class="status-dot"></span> 大雪</div>
<div class="card-detail-row">
<div class="card-detail">积雪 15cm</div>
<div class="card-detail">能见度 200m</div>
<div class="card-detail">湿度 78%</div>
</div>
</div>
</div>
</div>
<!-- ===== WINDY CARD ===== -->
<div class="weather-card card-windy" id="card-windy" data-type="windy">
<div class="card-bg"></div>
<div class="card-inner-glow"></div>
<div class="cloud-shape" style="top:60px;left:-20px;width:180px;height:40px;animation-duration:10s;"></div>
<div class="cloud-shape" style="top:120px;right:-30px;width:150px;height:35px;animation-duration:14s;animation-delay:-4s;"></div>
<div class="cloud-shape" style="top:200px;left:20px;width:120px;height:30px;animation-duration:12s;animation-delay:-8s;opacity:0.5;"></div>
<canvas class="weather-canvas" id="windyCanvas"></canvas>
<div class="card-shimmer"></div>
<div class="card-content">
<div class="card-top">
<span class="card-location">大连 · 星海广场</span>
<div class="card-icon-badge">
<svg fill="none" stroke="#aeaeb2" stroke-width="1.5" viewBox="0 0 24 24">
<path d="M3.75 13.5h10.5a2.25 2.25 0 002.25-2.25v0a2.25 2.25 0 00-2.25-2.25H12M3.75 17.25h14.25a2.25 2.25 0 002.25-2.25v0a2.25 2.25 0 00-2.25-2.25h-1.5M3.75 9h7.5a2.25 2.25 0 002.25-2.25v0A2.25 2.25 0 0011.25 4.5H9"/>
</svg>
</div>
</div>
<div class="card-bottom">
<div class="card-temp">14<span class="degree">°</span></div>
<div class="card-condition"><span class="status-dot"></span> 大风</div>
<div class="card-detail-row">
<div class="card-detail">风速 68km/h</div>
<div class="card-detail">阵风 8级</div>
<div class="card-detail">西北风</div>
</div>
</div>
</div>
</div>
</div>
<div class="footer-info">
<p>WEATHER VISUALIZATION ENGINE v2.0 · CANVAS PARTICLE RENDERING · 60FPS</p>
</div>
</div>
<script>
// ============================================
// Premium Weather Animation Engine
// ============================================
// --- Utility ---
const dpr = Math.min(window.devicePixelRatio || 1, 2);
function setupCanvas(canvas) {
const rect = canvas.parentElement.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
canvas.style.width = rect.width + 'px';
canvas.style.height = rect.height + 'px';
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
return { ctx, w: rect.width, h: rect.height };
}
function rand(min, max) { return Math.random() * (max - min) + min; }
function lerp(a, b, t) { return a + (b - a) * t; }
// --- Sun Rays Setup ---
(function setupSunRays() {
const container = document.getElementById('sunRays');
for (let i = 0; i < 12; i++) {
const ray = document.createElement('div');
ray.className = 'sun-ray';
ray.style.transform = `rotate(${i * 30}deg)`;
ray.style.opacity = 0.3 + Math.random() * 0.4;
container.appendChild(ray);
}
})();
// ============================================
// SUNNY – Golden floating particles + lens flares
// ============================================
(function initSunny() {
const canvas = document.getElementById('sunnyCanvas');
let { ctx, w, h } = setupCanvas(canvas);
class SunParticle {
constructor() { this.reset(); }
reset() {
this.x = rand(0, w);
this.y = rand(0, h);
this.size = rand(1, 3);
this.vx = rand(-0.15, 0.15);
this.vy = rand(-0.3, -0.05);
this.alpha = rand(0.1, 0.5);
this.alphaSpeed = rand(0.003, 0.01);
this.alphaDir = 1;
this.life = rand(100, 300);
this.age = 0;
this.color = `rgba(255,${Math.floor(rand(180,220))},${Math.floor(rand(50,100))},`;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.alpha += this.alphaSpeed * this.alphaDir;
if (this.alpha >= 0.6 || this.alpha <= 0.05) this.alphaDir *= -1;
this.age++;
if (this.age > this.life || this.y < -10 || this.x < -10 || this.x > w + 10) this.reset();
}
draw(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = this.color + this.alpha + ')';
ctx.fill();
}
}
// Lens flare streaks
class LensFlare {
constructor() { this.reset(); }
reset() {
this.x = rand(w * 0.3, w);
this.y = rand(0, h * 0.5);
this.len = rand(30, 80);
this.angle = rand(-0.5, 0.5);
this.alpha = 0;
this.maxAlpha = rand(0.03, 0.08);
this.speed = rand(0.002, 0.005);
this.phase = rand(0, Math.PI * 2);
}
update(t) {
this.alpha = this.maxAlpha * (0.5 + 0.5 * Math.sin(t * this.speed + this.phase));
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
const grad = ctx.createLinearGradient(-this.len/2, 0, this.len/2, 0);
grad.addColorStop(0, `rgba(255,200,60,0)`);
grad.addColorStop(0.5, `rgba(255,200,60,${this.alpha})`);
grad.addColorStop(1, `rgba(255,200,60,0)`);
ctx.strokeStyle = grad;
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(-this.len/2, 0);
ctx.lineTo(this.len/2, 0);
ctx.stroke();
ctx.restore();
}
}
const particles = Array.from({ length: 60 }, () => new SunParticle());
const flares = Array.from({ length: 8 }, () => new LensFlare());
let frame = 0;
function animate() {
ctx.clearRect(0, 0, w, h);
frame++;
particles.forEach(p => { p.update(); p.draw(ctx); });
flares.forEach(f => { f.update(frame); f.draw(ctx); });
requestAnimationFrame(animate);
}
animate();
window.addEventListener('resize', () => {
({ ctx, w, h } = setupCanvas(canvas));
});
})();
// ============================================
// RAINY – Rain drops + splashes + mist + lightning
// ============================================
(function initRainy() {
const canvas = document.getElementById('rainyCanvas');
let { ctx, w, h } = setupCanvas(canvas);
class RainDrop {
constructor() { this.reset(true); }
reset(initial) {
this.x = rand(-20, w + 20);
this.y = initial ? rand(-h, 0) : rand(-50, -10);
this.len = rand(15, 35);
this.speed = rand(12, 22);
this.alpha = rand(0.15, 0.4);
this.wind = rand(1, 3);
this.thickness = rand(0.8, 1.8);
}
update() {
this.y += this.speed;
this.x += this.wind;
if (this.y > h) {
splashes.push(new Splash(this.x, h - rand(10, 60)));
this.reset(false);
}
}
draw(ctx) {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + this.wind * 1.5, this.y + this.len);
ctx.strokeStyle = `rgba(150,200,255,${this.alpha})`;
ctx.lineWidth = this.thickness;
ctx.lineCap = 'round';
ctx.stroke();
}
}
class Splash {
constructor(x, y) {
this.x = x; this.y = y;
this.particles = [];
for (let i = 0; i < rand(3, 6); i++) {
this.particles.push({
vx: rand(-2, 2),
vy: rand(-3, -0.5),
alpha: rand(0.3, 0.6),
size: rand(1, 2),
life: 0
});
}
this.maxLife = 15;
}
update() {
this.particles.forEach(p => {
p.vx *= 0.96;
p.vy += 0.15;
p.alpha *= 0.92;
p.life++;
});
}
draw(ctx) {
this.particles.forEach(p => {
const px = this.x + p.vx * p.life;
const py = this.y + p.vy * p.life + 0.5 * 0.15 * p.life * p.life;
ctx.beginPath();
ctx.arc(px, py, p.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(150,200,255,${p.alpha})`;
ctx.fill();
});
}
isDead() { return this.particles[0].life > this.maxLife; }
}
// Mist/fog layer
class MistLayer {
constructor() { this.reset(); }
reset() {
this.x = rand(-100, w);
this.y = rand(h * 0.4, h);
this.width = rand(150, 350);
this.height = rand(20, 50);
this.speed = rand(0.2, 0.6);
this.alpha = rand(0.02, 0.06);
}
update() {
this.x += this.speed;
if (this.x > w + 100) this.reset();
}
draw(ctx) {
ctx.save();
ctx.globalAlpha = this.alpha;
ctx.fillStyle = 'rgba(100,140,180,1)';
ctx.filter = 'blur(20px)';
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.restore();
ctx.filter = 'none';
}
}
const drops = Array.from({ length: 120 }, () => new RainDrop());
let splashes = [];
const mists = Array.from({ length: 5 }, () => new MistLayer());
// Ripple effects at bottom
class Ripple {
constructor() { this.reset(); }
reset() {
this.x = rand(0, w);
this.y = rand(h - 70, h - 10);
this.maxR = rand(8, 20);
this.r = 0;
this.alpha = 0.4;
this.speed = rand(0.3, 0.8);
this.delay = rand(0, 120);
this.age = 0;
}
update() {
this.age++;
if (this.age < this.delay) return;
this.r += this.speed;
this.alpha = 0.4 * (1 - this.r / this.maxR);
if (this.r >= this.maxR) this.reset();
}
draw(ctx) {
if (this.age < this.delay) return;
ctx.beginPath();
ctx.ellipse(this.x, this.y, this.r, this.r * 0.3, 0, 0, Math.PI * 2);
ctx.strokeStyle = `rgba(100,180,250,${this.alpha})`;
ctx.lineWidth = 0.8;
ctx.stroke();
}
}
const ripples = Array.from({ length: 15 }, () => new Ripple());
// Lightning
const flashEl = document.getElementById('lightningFlash');
let lightningTimer = 0;
function triggerLightning() {
flashEl.style.animation = 'none';
void flashEl.offsetWidth;
flashEl.style.animation = 'lightningFlash 0.8s ease-out forwards';
lightningTimer = rand(200, 600);
}
lightningTimer = rand(180, 400);
function animate() {
ctx.clearRect(0, 0, w, h);
mists.forEach(m => { m.update(); m.draw(ctx); });
drops.forEach(d => { d.update(); d.draw(ctx); });
splashes.forEach(s => { s.update(); s.draw(ctx); });
splashes = splashes.filter(s => !s.isDead());
ripples.forEach(r => { r.update(); r.draw(ctx); });
lightningTimer--;
if (lightningTimer <= 0) triggerLightning();
requestAnimationFrame(animate);
}
animate();
window.addEventListener('resize', () => {
({ ctx, w, h } = setupCanvas(canvas));
});
})();
// ============================================
// SNOWY – Snowflakes + wind drift + sparkle
// ============================================
(function initSnowy() {
const canvas = document.getElementById('snowyCanvas');
let { ctx, w, h } = setupCanvas(canvas);
class Snowflake {
constructor() { this.reset(true); }
reset(initial) {
this.x = rand(-20, w + 20);
this.y = initial ? rand(-h, 0) : rand(-40, -5);
this.size = rand(1.5, 5);
this.speed = this.size * 0.4 + rand(0.2, 0.8);
this.wind = rand(-0.3, 0.3);
this.wobbleAmp = rand(0.5, 2);
this.wobbleSpeed = rand(0.02, 0.06);
this.phase = rand(0, Math.PI * 2);
this.alpha = rand(0.3, 0.9);
this.rotation = rand(0, Math.PI * 2);
this.rotSpeed = rand(-0.02, 0.02);
this.isStar = this.size > 3.5 && Math.random() > 0.5;
this.sparkle = 0;
this.sparkleSpeed = rand(0.03, 0.08);
}
update(frame) {
this.y += this.speed;
this.x += Math.sin(frame * this.wobbleSpeed + this.phase) * this.wobbleAmp * 0.3 + this.wind;
this.rotation += this.rotSpeed;
this.sparkle = 0.5 + 0.5 * Math.sin(frame * this.sparkleSpeed + this.phase);
if (this.y > h - 40) {
this.alpha *= 0.95;
if (this.alpha < 0.01) this.reset(false);
}
if (this.x < -30 || this.x > w + 30) this.reset(false);
}
draw(ctx, frame) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.globalAlpha = this.alpha;
if (this.isStar) {
// Draw crystalline snowflake
const s = this.size;
ctx.strokeStyle = `rgba(220,220,240,${0.6 + this.sparkle * 0.4})`;
ctx.lineWidth = 0.8;
for (let i = 0; i < 6; i++) {
ctx.save();
ctx.rotate(i * Math.PI / 3);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, -s);
// small branches
ctx.moveTo(0, -s * 0.5);
ctx.lineTo(-s * 0.3, -s * 0.7);
ctx.moveTo(0, -s * 0.5);
ctx.lineTo(s * 0.3, -s * 0.7);
ctx.stroke();
ctx.restore();
}
// Center glow
if (this.sparkle > 0.7) {
ctx.beginPath();
ctx.arc(0, 0, s * 0.3, 0, Math.PI * 2);
ctx.fillStyle = `rgba(220,230,255,${(this.sparkle - 0.7) * 0.5})`;
ctx.fill();
}
} else {
// Simple round snowflake
const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, this.size);
gradient.addColorStop(0, `rgba(240,240,255,${0.8 + this.sparkle * 0.2})`);
gradient.addColorStop(0.5, `rgba(220,220,240,${0.4 + this.sparkle * 0.2})`);
gradient.addColorStop(1, 'rgba(200,200,230,0)');
ctx.beginPath();
ctx.arc(0, 0, this.size, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
}
ctx.restore();
}
}
const flakes = Array.from({ length: 100 }, () => new Snowflake());
let frame = 0;
function animate() {
ctx.clearRect(0, 0, w, h);
frame++;
flakes.forEach(f => {
f.update(frame);
f.draw(ctx, frame);
});
requestAnimationFrame(animate);
}
animate();
window.addEventListener('resize', () => {
({ ctx, w, h } = setupCanvas(canvas));
});
})();
// ============================================
// WINDY – Wind streaks + particles + leaves
// ============================================
(function initWindy() {
const canvas = document.getElementById('windyCanvas');
let { ctx, w, h } = setupCanvas(canvas);
class WindStreak {
constructor() { this.reset(); }
reset() {
this.x = rand(-w * 0.5, 0);
this.y = rand(0, h);
this.len = rand(40, 150);
this.speed = rand(4, 12);
this.alpha = rand(0.03, 0.12);
this.thickness = rand(0.5, 1.5);
this.curve = rand(-0.3, 0.3);
}
update() {
this.x += this.speed;
this.y += this.curve;
if (this.x > w + 50) this.reset();
}
draw(ctx) {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.quadraticCurveTo(
this.x + this.len * 0.5, this.y + this.curve * 10,
this.x + this.len, this.y + this.curve * 5
);
ctx.strokeStyle = `rgba(180,190,200,${this.alpha})`;
ctx.lineWidth = this.thickness;
ctx.lineCap = 'round';
ctx.stroke();
}
}
class Leaf {
constructor() { this.reset(); }
reset() {
this.x = rand(-50, -10);
this.y = rand(h * 0.1, h * 0.9);
this.size = rand(3, 7);
this.speed = rand(3, 8);
this.vy = rand(-1, 1);
this.rotation = rand(0, Math.PI * 2);
this.rotSpeed = rand(0.05, 0.15) * (Math.random() > 0.5 ? 1 : -1);
this.alpha = rand(0.2, 0.5);
this.wobbleAmp = rand(1, 3);
this.wobbleSpeed = rand(0.03, 0.08);
this.phase = rand(0, Math.PI * 2);
this.color = [
'rgba(100,130,80,',
'rgba(140,120,60,',
'rgba(120,100,50,',
'rgba(80,110,70,',
][Math.floor(rand(0, 4))];
}
update(frame) {
this.x += this.speed;
this.y += this.vy + Math.sin(frame * this.wobbleSpeed + this.phase) * this.wobbleAmp * 0.5;
this.rotation += this.rotSpeed;
if (this.x > w + 30) this.reset();
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.globalAlpha = this.alpha;
ctx.beginPath();
ctx.ellipse(0, 0, this.size, this.size * 0.4, 0, 0, Math.PI * 2);
ctx.fillStyle = this.color + '1)';
ctx.fill();
// Leaf vein
ctx.beginPath();
ctx.moveTo(-this.size * 0.8, 0);
ctx.lineTo(this.size * 0.8, 0);
ctx.strokeStyle = this.color + '0.5)';
ctx.lineWidth = 0.5;
ctx.stroke();
ctx.restore();
}
}
class DustParticle {
constructor() { this.reset(); }
reset() {
this.x = rand(-20, 0);
this.y = rand(0, h);
this.size = rand(0.5, 2);
this.speed = rand(2, 6);
this.alpha = rand(0.05, 0.2);
this.vy = rand(-0.5, 0.5);
}
update() {
this.x += this.speed;
this.y += this.vy;
if (this.x > w + 10) this.reset();
}
draw(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(160,170,180,${this.alpha})`;
ctx.fill();
}
}
const streaks = Array.from({ length: 30 }, () => new WindStreak());
const leaves = Array.from({ length: 8 }, () => new Leaf());
const dust = Array.from({ length: 40 }, () => new DustParticle());
let frame = 0;
function animate() {
ctx.clearRect(0, 0, w, h);
frame++;
streaks.forEach(s => { s.update(); s.draw(ctx); });
dust.forEach(d => { d.update(); d.draw(ctx); });
leaves.forEach(l => { l.update(frame); l.draw(ctx); });
requestAnimationFrame(animate);
}
animate();
window.addEventListener('resize', () => {
({ ctx, w, h } = setupCanvas(canvas));
});
})();
// ============================================
// HIGHLIGHT / FILTER CONTROLS
// ============================================
function highlightWeather(type) {
// Update buttons
document.querySelectorAll('.ctrl-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.weather === type);
});
const cards = document.querySelectorAll('.weather-card');
if (type === 'all') {
cards.forEach(card => {
card.classList.remove('dimmed', 'highlighted');
});
return;
}
cards.forEach(card => {
if (card.dataset.type === type) {
card.classList.add('highlighted');
card.classList.remove('dimmed');
} else {
card.classList.add('dimmed');
card.classList.remove('highlighted');
}
});
}
// Initialize "all" as active
highlightWeather('all');
document.querySelector('.ctrl-btn[data-weather="all"]').classList.add('active');
// ============================================
// LIVE CLOCK
// ============================================
function updateTime() {
const now = new Date();
const options = {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
};
document.getElementById('timeText').textContent =
now.toLocaleDateString('zh-CN', { month: 'long', day: 'numeric', weekday: 'short' }) +
' ' + now.toLocaleTimeString('zh-CN', options);
}
updateTime();
setInterval(updateTime, 1000);
// ============================================
// Card tilt effect (subtle 3D)
// ============================================
document.querySelectorAll('.weather-card').forEach(card => {
card.addEventListener('mousemove', e => {
const rect = card.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
const rotY = (x - 0.5) * 8;
const rotX = (0.5 - y) * 8;
card.style.transform = `perspective(800px) rotateX(${rotX}deg) rotateY(${rotY}deg) translateY(-8px) scale(1.02)`;
});
card.addEventListener('mouseleave', () => {
card.style.transform = '';
});
});
// ============================================
// Auto-cycle demonstration
// ============================================
let autoCycleTimer = null;
let cycleIndex = 0;
const weatherTypes = ['sunny', 'rainy', 'snowy', 'windy', 'all'];
function startAutoCycle() {
stopAutoCycle();
autoCycleTimer = setInterval(() => {
cycleIndex = (cycleIndex + 1) % weatherTypes.length;
highlightWeather(weatherTypes[cycleIndex]);
}, 4000);
}
function stopAutoCycle() {
if (autoCycleTimer) clearInterval(autoCycleTimer);
}
// Stop auto-cycle when user interacts
document.querySelectorAll('.ctrl-btn').forEach(btn => {
btn.addEventListener('click', stopAutoCycle);
});
// Start auto-cycle after 10 seconds of no interaction
let idleTimer = setTimeout(startAutoCycle, 10000);
document.addEventListener('click', () => {
clearTimeout(idleTimer);
stopAutoCycle();
idleTimer = setTimeout(startAutoCycle, 15000);
});
console.log('%c🌤 Weather Engine Initialized', 'color: #ffb340; font-size: 14px; font-weight: bold;');
console.log('%c4 animated weather cards · Canvas particle rendering · 60fps', 'color: #888; font-size: 11px;');
</script>
</body>
</html>
17 Likes
感觉还可以啊 ![]()
有Gemini那味儿了
还不错啊
1 Like
不赖啊我感觉
1 Like
怎么感觉没有经典的渐变紫了
雷雨天气还带闪电
1 Like
看起来gemini的前端打不过claude了,TPU难不成是前端增幅器?![]()
2 Likes
还是好看的
横看成gemini侧成claude
比隔壁 gpt-5.3-codex 审美好些,不知道啥时候能用上让俺试试
试一下codex的呢
很不错啊
1 Like
比隔壁的glm5好看 ![]()
1 Like
佬,提示词能分享一下吗 ![]()
你是一位就职于苹果公司的顶级前端工程师。请创建一个包含CSS和JavaScript的HTML文件,用于生成动画天气卡片。卡片需要以不同动画效果直观展示以下天气状况:
风力(如:飘动的云朵、摇曳的树木或风线)
降雨(如:下落的雨滴、形成的水洼)
晴天(如:闪耀的光线、明亮的背景)
下雪(如:飘落的雪花、积雪效果)
所有天气卡片需要并排显示,背景采用深色设计。所有HTML、CSS和JavaScript代码都需包含在这个单一文件中。JavaScript部分需包含切换不同天气状态的功能(例如通过函数或按钮组),以演示每种天气的动画效果。
将前端显示效果优化得更精致流畅,打造出价值20元/月的精品天气网页既视感。
2 Likes
还行吧~
谢谢佬友
审美好独特

