苹果风格高级天气应用:四组动态天气卡片与深色玻璃态设计

By 13082141699, 24 April, 2026
推理
The user wants me to create a beautiful weather card application with animated weather effects. Let me think about the design approach:

1. **Product Type**: Weather application
2. **Aesthetic Direction**: Premium, dark mode, Apple-like design
3. **Layout**: Four cards side by side showing Wind, Rain, Sunny, Snow
4. **Features**: Animated weather effects, toggle buttons to switch between weather states

Let me first get a design system recommendation and some assets.

Let me think about the design:

- **Dark background** - premium feel
- **Apple design philosophy** - clean, minimal, elegant typography
- **Four weather cards** - Wind, Rain, Sunny, Snow
- **Animated effects** for each weather type
- **Toggle functionality** to switch between weather states

Let me get some design inspiration first.Let me design a premium weather application with Apple-like aesthetics. I'll use:

1. **Typography**: SF Pro Display-like (using Inter or a beautiful alternative) - actually, let me use something more unique. Let me use "Plus Jakarta Sans" for headings and "Inter" for body text.

2. **Colors**: Dark background with subtle gradients, vibrant accent colors for each weather type.

3. **Layout**: 4 cards side by side, with a control panel at the bottom to switch weather states.

Let me think about the animations:

**Wind** - Floating clouds moving across, wind lines/streaks, swaying trees
**Rain** - Falling raindrops, puddle ripples
**Sunny** - Radiant light beams, glowing sun, bright background
**Snow** - Falling snowflakes, accumulating snow

Let me create a premium, polished implementation.

I'll use a dark glassmorphism design with:
- Dark background with subtle gradient
- Glassmorphism cards
- Beautiful typography
- Smooth animations
- Apple-like attention to detail

Let me write the complete HTML file.

Design decisions:
- Background: Deep dark gradient (#0a0a0f to #1a1a2e)
- Cards: Glass effect with backdrop blur
- Each weather card has a unique color accent
- Toggle buttons at the bottom
- Smooth transitions between states

Let me code this up now.Good, now let me create the complete HTML file. Let me design a premium Apple-style weather application.

Here's my plan:

**Design Concept**: Apple-inspired premium weather app with dark mode
- Deep space-like dark gradient background
- Frosted glass cards with subtle borders
- Each weather type has its own color palette
- Smooth, fluid animations
- Button group to switch between weather states (or view all at once)

**Weather Cards & Animations**:

1. **Wind** (#6366f1 - indigo)
- Drifting clouds moving horizontally with varying speeds
- Wind lines/streaks flowing across
- A tree silhouette swaying

2. **Rain** (#3b82f6 - blue)
- Falling raindrops in diagonal lines
- Puddle with ripple effects at the bottom
- Cloud overhead

3. **Sunny** (#f59e0b - amber/gold)
- Radiant sun with pulsing glow
- Light rays emanating outward
- Bright, warm feel

4. **Snow** (#e2e8f0 - icy white)
- Gently falling snowflakes
- Accumulating snow at bottom
- Soft, peaceful atmosphere

**Layout**:
- 4 cards in a row (responsive)
- Each card shows weather icon, temperature, weather name, and animated scene
- Bottom control bar with toggle buttons

Let me build this now with TailwindCSS and custom CSS animations.

Actually, since this is an Apple design, let me use custom CSS more heavily to get that premium feel. I'll use Tailwind for basic layout and custom CSS for the animations and glass effects.

Let me think about the font choices - I want something that feels Apple-like. SF Pro Display isn't available on Google Fonts, so I'll use a combination that feels premium:
- Headings: "Plus Jakarta Sans" - modern, geometric
- Or "Instrument Sans" - very clean
- Or "Outfit" - elegant and modern

Let me go with "Outfit" for headings and "Inter" for body text - both clean and Apple-like.

Actually, let me think more creatively. I want to make this feel like a $20/month premium app. Let me use:
- "Syne" for display/headings - has a very premium, editorial feel
- "Plus Jakarta Sans" for body text

No wait, let me go with something clean and Apple-like but distinctive:
- "Space Grotesk" for headings - modern, slightly techy
- "Inter" for body

Actually, the design system suggests Inter. Let me go with something more unique and premium. I'll use "Outfit" for titles and "Inter" for body text.

Let me structure the code:

1. HTML structure with 4 weather cards
2. Custom CSS with all animations
3. JavaScript for state management and interactivity

Here's my detailed implementation plan:

The page will show all 4 weather cards simultaneously, with a toggle to switch which weather animation is active (or show all active). Each card will have:
- Animated background/scene
- Weather icon (Font Awesome)
- Temperature display
- Weather condition name
- Location/date info

Let me now write the complete code.

I'm going to create something really premium. Let me think about every detail:

Background: A deep gradient from #0a0a12 to #12121a with subtle noise texture
Cards: Glass effect with rgba(255,255,255,0.05) background, backdrop-blur, subtle border
Typography: Clean, well-spaced, with careful hierarchy

For the animations, I want them to be continuous and beautiful, not just triggered once.

Let me code this up now.Let me use the dark teal geometric background as inspiration for the dark theme. Now let me write the complete code.

I'll create a single HTML file with all CSS and JS embedded. Let me make this really polished and premium.

Let me think about the animation details more carefully:

**Wind Card**:
- Multiple clouds moving horizontally at different speeds and heights
- Wind streaks (thin lines) flowing across
- A tree silhouette gently swaying
- Color: Cool indigo/slate tones

**Rain Card**:
- Raindrops falling in diagonal streaks
- Puddle at the bottom with expanding ripple rings
- Dark stormy clouds above
- Color: Deep blue tones

**Sunny Card**:
- Central sun with rotating/pulsing glow
- Radiating light beams with animation
- Warm golden/amber tones
- Maybe some small floating particles (dust motes)

**Snow Card**:
- Snowflakes falling gently, some drifting horizontally
- Snow accumulating at the bottom
- Ice crystals visible
- Color: Cool icy blue/white

For the control panel, I'll create a set of buttons that allow:
- "All" - show all weather cards with their animations
- "Wind" - only wind card active, others dimmed
- "Rain" - only rain card active
- "Sunny" - only sunny card active
- "Snow" - only snow card active

Let me now write this comprehensive code.Now let me create the complete HTML file. I'll design an Apple-inspired premium weather application with stunning animations.

Let me think carefully about the design:

**Color Palette:**
- Background: Deep dark gradient (#0a0a12 → #141420 → #1a1a2e)
- Cards: Glass effect with subtle borders
- Accent colors per weather type:
- Wind: #818cf8 (indigo)
- Rain: #38bdf8 (sky blue)
- Sunny: #fbbf24 (amber/gold)
- Snow: #cbd5e1 (slate/ice)

**Typography:**
- Headings: "Outfit" - premium, clean, geometric
- Body: "Inter" - readable, Apple-like

**Animations:**
1. **Wind**: Clouds drifting at different speeds, wind streaks, tree swaying
2. **Rain**: Falling drops, puddle ripples
3. **Sunny**: Pulsing sun, light rays, warm glow
4. **Snow**: Falling snowflakes, accumulation

Let me create this now.No existing conversations. Let me now create the complete HTML file. I'll design a premium, Apple-inspired weather application with all four weather animations.

Let me write the complete code now.Good, now I have enough context. Let me create the complete HTML file. I'll focus on making it look premium and Apple-like with beautiful animations.

Let me write the complete code now.
角色
assistant
content

```json
{
"summary": "设计并构建了一个苹果风格的高级天气应用HTML文件,包含4张动画天气卡片(风力、降雨、晴天、下雪),深色玻璃态背景,每组天气拥有独立精细的粒子/流体动画系统。底部提供切换按钮组,支持显示全部或单独展示某一天气状态。采用Outfit+Inter字体组合,Font Awesome图标,TailwindCSS辅助布局,自定义CSS实现全部动画效果。",
"widget": {
"type": "full-html",
"html": "<!DOCTYPE html>\n<html lang=\"zh-Hans\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>天气 · Weather+</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;70…\" rel=\"stylesheet\">\n <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.c…\">\n\n <style>\n :root {\n --wind-accent: #818cf8;\n --rain-accent: #38bdf8;\n --sunny-accent: #fbbf24;\n --snow-accent: #cbd5e1;\n --card-radius: 24px;\n }\n\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n background: #06060e;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 24px;\n overflow-x: hidden;\n position: relative;\n }\n\n /* --- 背景噪点 & 渐变 --- */\n body::before {\n content: '';\n position: fixed;\n inset: 0;\n background: \n radial-gradient(ellipse 80% 60% at 20% 20%, rgba(99,102,241,0.06), transparent),\n radial-gradient(ellipse 70% 50% at 80% 80%, rgba(56,189,248,0.05), transparent),\n radial-gradient(ellipse 60% 60% at 50% 50%, rgba(20,20,40,0.3), transparent);\n pointer-events: none;\n z-index: 0;\n }\n body::after {\n content: '';\n position: fixed;\n inset: 0;\n background-image: url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E\");\n pointer-events: none;\n z-index: 0;\n }\n\n .app-container {\n position: relative;\n z-index: 1;\n width: 100%;\n max-width: 1440px;\n margin: 0 auto;\n }\n\n /* --- 头部 --- */\n .app-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 40px;\n padding: 0 4px;\n }\n .app-header h1 {\n font-family: 'Outfit', sans-serif;\n font-weight: 700;\n font-size: 28px;\n color: #ffffff;\n letter-spacing: -0.5px;\n display: flex;\n align-items: center;\n gap: 12px;\n }\n .app-header h1 span {\n background: linear-gradient(135deg, #818cf8, #38bdf8);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n }\n .header-meta {\n color: rgba(255,255,255,0.3);\n font-size: 14px;\n font-weight: 400;\n letter-spacing: 0.3px;\n }\n\n /* --- 卡片网格 --- */\n .cards-grid {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 20px;\n margin-bottom: 32px;\n }\n\n /* --- 单张卡片 --- */\n .weather-card {\n position: relative;\n border-radius: var(--card-radius);\n background: rgba(255,255,255,0.04);\n backdrop-filter: blur(24px) saturate(1.2);\n -webkit-backdrop-filter: blur(24px) saturate(1.2);\n border: 1px solid rgba(255,255,255,0.06);\n box-shadow: 0 8px 32px rgba(0,0,0,0.4);\n padding: 28px 24px 24px;\n min-height: 460px;\n display: flex;\n flex-direction: column;\n transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);\n overflow: hidden;\n }\n .weather-card::before {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--card-radius);\n padding: 1px;\n background: linear-gradient(135deg, rgba(255,255,255,0.08), rgba(255,255,255,0.01));\n -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask-composite: exclude;\n pointer-events: none;\n }\n\n .weather-card.dimmed {\n opacity: 0.25;\n transform: scale(0.96);\n filter: blur(2px);\n pointer-events: none;\n }\n .weather-card.active-highlight {\n transform: scale(1.02);\n box-shadow: 0 12px 48px rgba(0,0,0,0.6);\n }\n\n /* 卡头 */\n .card-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n margin-bottom: 12px;\n position: relative;\n z-index: 2;\n }\n .card-icon-circle {\n width: 48px;\n height: 48px;\n border-radius: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 22px;\n background: rgba(255,255,255,0.06);\n backdrop-filter: blur(4px);\n border: 1px solid rgba(255,255,255,0.06);\n }\n .card-temp {\n font-family: 'Outfit', sans-serif;\n font-weight: 700;\n font-size: 42px;\n line-height: 1;\n color: #fff;\n letter-spacing: -1px;\n }\n .card-temp sup {\n font-size: 20px;\n font-weight: 400;\n color: rgba(255,255,255,0.4);\n margin-left: 2px;\n }\n\n .card-title {\n font-family: 'Outfit', sans-serif;\n font-weight: 600;\n font-size: 18px;\n color: #fff;\n margin-top: 4px;\n margin-bottom: 2px;\n }\n .card-sub {\n font-size: 13px;\n color: rgba(255,255,255,0.35);\n font-weight: 400;\n letter-spacing: 0.2px;\n }\n\n /* --- 动画场景容器 --- */\n .weather-scene {\n position: relative;\n flex: 1;\n min-height: 200px;\n margin: 12px -24px -24px;\n border-radius: 0 0 var(--card-radius) var(--card-radius);\n overflow: hidden;\n isolation: isolate;\n }\n\n /* ========== 风力 (Wind) ========== */\n .wind-scene {\n background: linear-gradient(180deg, #0f0f2a 0%, #1a1a3a 40%, #12122a 100%);\n }\n .wind-cloud {\n position: absolute;\n background: rgba(255,255,255,0.06);\n border-radius: 100px;\n box-shadow: 0 0 40px rgba(129,140,248,0.05);\n }\n .wind-cloud::before {\n content: '';\n position: absolute;\n background: inherit;\n border-radius: 100px;\n }\n .wind-streak {\n position: absolute;\n height: 1.5px;\n background: linear-gradient(90deg, transparent, rgba(129,140,248,0.3), transparent);\n border-radius: 2px;\n }\n .wind-tree {\n position: absolute;\n bottom: 20px;\n width: 6px;\n height: 60px;\n background: linear-gradient(to top, #2d1b0e, #3d2b1a);\n border-radius: 3px;\n transform-origin: bottom center;\n }\n .wind-tree::before {\n content: '';\n position: absolute;\n bottom: 50px;\n left: -18px;\n width: 40px;\n height: 32px;\n background: radial-gradient(ellipse at center, rgba(20,83,45,0.5), transparent);\n border-radius: 50%;\n }\n .wind-tree::after {\n content: '';\n position: absolute;\n bottom: 42px;\n left: -8px;\n width: 24px;\n height: 20px;\n background: radial-gradient(ellipse at center, rgba(30,100,55,0.4), transparent);\n border-radius: 50%;\n }\n\n /* ========== 降雨 (Rain) ========== */\n .rain-scene {\n background: linear-gradient(180deg, #0a0e1a 0%, #0d1525 35%, #0a0e1a 100%);\n }\n .rain-drop {\n position: absolute;\n width: 2px;\n background: linear-gradient(180deg, transparent, rgba(56,189,248,0.6));\n border-radius: 1px;\n }\n .rain-puddle {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 28px;\n background: linear-gradient(180deg, transparent, rgba(56,189,248,0.12));\n }\n .rain-ripple {\n position: absolute;\n bottom: 10px;\n width: 4px;\n height: 4px;\n border-radius: 50%;\n border: 1.5px solid rgba(56,189,248,0.25);\n animation: ripple-expand 2.4s ease-out infinite;\n }\n\n /* ========== 晴天 (Sunny) ========== */\n .sunny-scene {\n background: linear-gradient(180deg, #1a1408 0%, #221a0a 35%, #1a1408 100%);\n }\n .sun-disk {\n position: absolute;\n width: 72px;\n height: 72px;\n background: radial-gradient(circle at 40% 35%, #fde68a, #f59e0b 60%, #b45309);\n border-radius: 50%;\n box-shadow: \n 0 0 80px rgba(251,191,36,0.25),\n 0 0 160px rgba(251,191,36,0.1);\n }\n .sun-ray {\n position: absolute;\n width: 2px;\n background: linear-gradient(180deg, rgba(251,191,36,0.4), transparent);\n border-radius: 1px;\n transform-origin: bottom center;\n }\n .sun-glow {\n position: absolute;\n width: 160px;\n height: 160px;\n border-radius: 50%;\n background: radial-gradient(circle, rgba(251,191,36,0.06) 0%, transparent 70%);\n }\n .sun-sparkle {\n position: absolute;\n width: 3px;\n height: 3px;\n background: #fde68a;\n border-radius: 50%;\n box-shadow: 0 0 6px rgba(251,191,36,0.6);\n }\n\n /* ========== 下雪 (Snow) ========== */\n .snow-scene {\n background: linear-gradient(180deg, #0e0e18 0%, #12121e 40%, #0e0e18 100%);\n }\n .snow-flake {\n position: absolute;\n width: 5px;\n height: 5px;\n background: rgba(255,255,255,0.7);\n border-radius: 50%;\n box-shadow: 0 0 8px rgba(203,213,225,0.1);\n }\n .snow-flake.alt {\n width: 3px;\n height: 3px;\n opacity: 0.5;\n }\n .snow-ground {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 20px;\n background: linear-gradient(180deg, transparent, rgba(203,213,225,0.08));\n border-radius: 0 0 var(--card-radius) var(--card-radius);\n }\n .snow-mound {\n position: absolute;\n bottom: 0;\n border-radius: 50%;\n background: rgba(203,213,225,0.04);\n }\n\n /* ========== 动画关键帧 ========== */\n @keyframes cloud-drift {\n 0% { transform: translateX(-120px) scale(1); opacity: 0; }\n 10% { opacity: 1; }\n 90% { opacity: 1; }\n 100% { transform: translateX(calc(100vw - 20px)) scale(1); opacity: 0; }\n }\n @keyframes cloud-drift-slow {\n 0% { transform: translateX(-100px) scale(0.85); opacity: 0; }\n 15% { opacity: 0.7; }\n 85% { opacity: 0.7; }\n 100% { transform: translateX(calc(100vw + 20px)) scale(0.85); opacity: 0; }\n }\n @keyframes wind-streak {\n 0% { transform: translateX(-80px); opacity: 0; }\n 20% { opacity: 1; }\n 80% { opacity: 1; }\n 100% { transform: translateX(calc(100% + 120px)); opacity: 0; }\n }\n @keyframes tree-sway {\n 0%, 100% { transform: rotate(-3deg); }\n 50% { transform: rotate(3deg); }\n }\n @keyframes drop-fall {\n 0% { transform: translateY(-40px); opacity: 1; }\n 90% { opacity: 0.8; }\n 100% { transform: translateY(280px); opacity: 0; }\n }\n @keyframes ripple-expand {\n 0% { transform: scale(1); opacity: 0.6; }\n 100% { transform: scale(18); opacity: 0; }\n }\n @keyframes sun-pulse {\n 0%, 100% { transform: scale(1); box-shadow: 0 0 80px rgba(251,191,36,0.25), 0 0 160px rgba(251,191,36,0.1); }\n 50% { transform: scale(1.04); box-shadow: 0 0 100px rgba(251,191,36,0.35), 0 0 200px rgba(251,191,36,0.15); }\n }\n @keyframes ray-rotate {\n 0% { transform: rotate(var(--ray-angle)) scaleY(1); opacity: 0.5; }\n 50% { opacity: 0.9; }\n 100% { transform: rotate(var(--ray-angle)) scaleY(1); opacity: 0.5; }\n }\n @keyframes sparkle-twinkle {\n 0%, 100% { opacity: 0; transform: scale(0.5); }\n 50% { opacity: 1; transform: scale(1.2); }\n }\n @keyframes snow-fall {\n 0% { transform: translateY(-30px) translateX(0) rotate(0deg); opacity: 0; }\n 20% { opacity: 1; }\n 80% { opacity: 0.8; }\n 100% { transform: translateY(280px) translateX(var(--drift)) rotate(360deg); opacity: 0.2; }\n }\n @keyframes snow-fall-alt {\n 0% { transform: translateY(-20px) translateX(0) rotate(0deg); opacity: 0; }\n 30% { opacity: 0.6; }\n 70% { opacity: 0.6; }\n 100% { transform: translateY(260px) translateX(var(--drift)) rotate(-180deg); opacity: 0; }\n }\n\n /* ========== 控制面板 ========== */\n .controls {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 10px 20px;\n background: rgba(255,255,255,0.03);\n backdrop-filter: blur(16px);\n -webkit-backdrop-filter: blur(16px);\n border-radius: 60px;\n border: 1px solid rgba(255,255,255,0.06);\n margin: 0 auto;\n width: fit-content;\n flex-wrap: wrap;\n }\n .btn-weather {\n font-family: 'Inter', sans-serif;\n font-size: 13px;\n font-weight: 500;\n padding: 8px 20px;\n border-radius: 40px;\n border: none;\n background: transparent;\n color: rgba(255,255,255,0.3);\n cursor: pointer;\n transition: all 0.35s cubic-bezier(0.23, 1, 0.32, 1);\n display: flex;\n align-items: center;\n gap: 8px;\n letter-spacing: 0.2px;\n }\n .btn-weather i {\n font-size: 14px;\n }\n .btn-weather:hover {\n color: rgba(255,255,255,0.6);\n background: rgba(255,255,255,0.04);\n }\n .btn-weather.active {\n color: #fff;\n background: rgba(255,255,255,0.08);\n box-shadow: 0 0 0 1px rgba(255,255,255,0.08);\n }\n .btn-weather.active.wind-btn { background: rgba(129,140,248,0.15); color: #818cf8; box-shadow: 0 0 0 1px rgba(129,140,248,0.2); }\n .btn-weather.active.rain-btn { background: rgba(56,189,248,0.15); color: #38bdf8; box-shadow: 0 0 0 1px rgba(56,189,248,0.2); }\n .btn-weather.active.sunny-btn { background: rgba(251,191,36,0.15); color: #fbbf24; box-shadow: 0 0 0 1px rgba(251,191,36,0.2); }\n .btn-weather.active.snow-btn { background: rgba(203,213,225,0.12); color: #cbd5e1; box-shadow: 0 0 0 1px rgba(203,213,225,0.2); }\n .btn-weather.active.all-btn { background: rgba(255,255,255,0.08); color: #fff; }\n .btn-divider {\n width: 1px;\n height: 24px;\n background: rgba(255,255,255,0.06);\n }\n\n /* ========== 响应式 ========== */\n @media (max-width: 1024px) {\n .cards-grid {\n grid-template-columns: repeat(2, 1fr);\n gap: 16px;\n }\n .weather-card { min-height: 400px; }\n }\n @media (max-width: 640px) {\n body { padding: 16px; }\n .cards-grid {\n grid-template-columns: 1fr;\n gap: 14px;\n }\n .weather-card { min-height: 380px; padding: 20px 16px 16px; }\n .app-header { flex-direction: column; align-items: flex-start; gap: 6px; margin-bottom: 24px; }\n .app-header h1 { font-size: 22px; }\n .controls { gap: 4px; padding: 8px 12px; border-radius: 40px; }\n .btn-weather { font-size: 12px; padding: 6px 14px; }\n .card-temp { font-size: 34px; }\n .weather-scene { margin: 8px -16px -16px; min-height: 160px; }\n .sun-disk { width: 56px; height: 56px; }\n .sun-glow { width: 120px; height: 120px; }\n }\n @media (max-width: 400px) {\n .btn-weather { font-size: 11px; padding: 5px 10px; }\n .btn-weather i { font-size: 12px; }\n }\n </style>\n</head>\n<body>\n\n<div class=\"app-container\">\n \n <!-- 头部 -->\n <header class=\"app-header\">\n <h1>\n <i class=\"fa-solid fa-cloud-sun\" style=\"background: linear-gradient(135deg, #818cf8, #38bdf8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-size: 26px;\"></i>\n Weather<span>+</span>\n </h1>\n <span class=\"header-meta\"><i class=\"fa-regular fa-clock\" style=\"margin-right: 6px;\"></i>实时动态 · 沉浸体验</span>\n </header>\n\n <!-- 卡片网格 -->\n <div class=\"cards-grid\" id=\"cardsGrid\">\n\n <!-- === 风力 === -->\n <div class=\"weather-card\" data-weather=\"wind\" id=\"cardWind\">\n <div class=\"card-header\">\n <div class=\"card-icon-circle\" style=\"color: var(--wind-accent);\">\n <i class=\"fa-solid fa-wind\"></i>\n </div>\n <div class=\"card-temp\" style=\"color: var(--wind-accent);\">14<sup>°C</sup></div>\n </div>\n <div class=\"card-title\">风力 · Wind</div>\n <div class=\"card-sub\"><i class=\"fa-regular fa-location-dot\" style=\"margin-right: 4px;\"></i>沿海区域 · 阵风 35 km/h</div>\n <div class=\"weather-scene wind-scene\" id=\"sceneWind\">\n <!-- 由JS动态生成粒子 -->\n </div>\n </div>\n\n <!-- === 降雨 === -->\n <div class=\"weather-card\" data-weather=\"rain\" id=\"cardRain\">\n <div class=\"card-header\">\n <div class=\"card-icon-circle\" style=\"color: var(--rain-accent);\">\n <i class=\"fa-solid fa-cloud-rain\"></i>\n </div>\n <div class=\"card-temp\" style=\"color: var(--rain-accent);\">8<sup>°C</sup></div>\n </div>\n <div class=\"card-title\">降雨 · Rain</div>\n <div class=\"card-sub\"><i class=\"fa-regular fa-location-dot\" style=\"margin-right: 4px;\"></i>城市中心 · 降水量 12 mm</div>\n <div class=\"weather-scene rain-scene\" id=\"sceneRain\">\n </div>\n </div>\n\n <!-- === 晴天 === -->\n <div class=\"weather-card\" data-weather=\"sunny\" id=\"cardSunny\">\n <div class=\"card-header\">\n <div class=\"card-icon-circle\" style=\"color: var(--sunny-accent);\">\n <i class=\"fa-solid fa-sun\"></i>\n </div>\n <div class=\"card-temp\" style=\"color: var(--sunny-accent);\">27<sup>°C</sup></div>\n </div>\n <div class=\"card-title\">晴天 · Sunny</div>\n <div class=\"card-sub\"><i class=\"fa-regular fa-location-dot\" style=\"margin-right: 4px;\"></i>海滨度假区 · 紫外线指数 7</div>\n <div class=\"weather-scene sunny-scene\" id=\"sceneSunny\">\n </div>\n </div>\n\n <!-- === 下雪 === -->\n <div class=\"weather-card\" data-weather=\"snow\" id=\"cardSnow\">\n <div class=\"card-header\">\n <div class=\"card-icon-circle\" style=\"color: var(--snow-accent);\">\n <i class=\"fa-regular fa-snowflake\"></i>\n </div>\n <div class=\"card-temp\" style=\"color: var(--snow-accent);\">-3<sup>°C</sup></div>\n </div>\n <div class=\"card-title\">下雪 · Snow</div>\n <div class=\"card-sub\"><i class=\"fa-regular fa-location-dot\" style=\"margin-right: 4px;\"></i>高山雪场 · 积雪 18 cm</div>\n <div class=\"weather-scene snow-scene\" id=\"sceneSnow\">\n </div>\n </div>\n\n </div>\n\n <!-- 控制按钮组 -->\n <div class=\"controls\" id=\"controls\">\n <button class=\"btn-weather all-btn active\" data-target=\"all\">\n <i class=\"fa-solid fa-layer-group\"></i> 全部\n </button>\n <span class=\"btn-divider\"></span>\n <button class=\"btn-weather wind-btn\" data-target=\"wind\">\n <i class=\"fa-solid fa-wind\"></i> 风力\n </button>\n <button class=\"btn-weather rain-btn\" data-target=\"rain\">\n <i class=\"fa-solid fa-cloud-rain\"></i> 降雨\n </button>\n <button class=\"btn-weather sunny-btn\" data-target=\"sunny\">\n <i class=\"fa-solid fa-sun\"></i> 晴天\n </button>\n <button class=\"btn-weather snow-btn\" data-target=\"snow\">\n <i class=\"fa-regular fa-snowflake\"></i> 下雪\n </button>\n </div>\n\n <div style=\"text-align: center; margin-top: 20px; color: rgba(255,255,255,0.12); font-size: 12px; letter-spacing: 0.5px;\">\n <i class=\"fa-regular fa-circle-check\"></i> 实时天气模拟 · 点击按钮切换焦点\n </div>\n</div>\n\n<script>\n(function() {\n 'use strict';\n\n // ===== 工具函数 =====\n function rand(min, max) { return Math.random() * (max - min) + min; }\n function randInt(min, max) { return Math.floor(rand(min, max + 1)); }\n\n // ===== 场景粒子生成器 =====\n function buildWindScene(container) {\n container.innerHTML = '';\n const w = container.offsetWidth || 360;\n const h = container.offsetHeight || 240;\n\n // 云朵 × 3\n for (let i = 0; i < 3; i++) {\n const cloud = document.createElement('div');\n cloud.className = 'wind-cloud';\n const cw = rand(60, 120);\n const ch = rand(18, 30);\n cloud.style.width = cw + 'px';\n cloud.style.height = ch + 'px';\n cloud.style.top = rand(10, h * 0.5) + 'px';\n cloud.style.left = '0px';\n cloud.style.animation = (i === 1) ? `cloud-drift-slow ${rand(18, 26)}s linear ${rand(0, 8)}s infinite` : `cloud-drift ${rand(12, 18)}s linear ${rand(0, 5)}s infinite`;\n cloud.style.opacity = rand(0.08, 0.18);\n container.appendChild(cloud);\n }\n\n // 风线 × 6\n for (let i = 0; i < 6; i++) {\n const streak = document.createElement('div');\n streak.className = 'wind-streak';\n const sw = rand(30, 100);\n streak.style.width = sw + 'px';\n streak.style.top = rand(10, h - 20) + 'px';\n streak.style.left = '0px';\n streak.style.animation = `wind-streak ${rand(3, 6)}s linear ${rand(0, 4)}s infinite`;\n streak.style.opacity = rand(0.15, 0.35);\n container.appendChild(streak);\n }\n\n // 树木 × 2\n for (let i = 0; i < 2; i++) {\n const tree = document.createElement('div');\n tree.className = 'wind-tree';\n tree.style.left = rand(30, w - 30) + 'px';\n tree.style.height = rand(40, 70) + 'px';\n tree.style.width = rand(4, 7) + 'px';\n tree.style.animation = `tree-sway ${rand(2.5, 4)}s ease-in-out ${rand(0, 2)}s infinite`;\n container.appendChild(tree);\n }\n\n // 底部的草地线条\n const grass = document.createElement('div');\n grass.style.cssText = `position:absolute;bottom:0;left:0;right:0;height:6px;background:linear-gradient(180deg,transparent,rgba(20,83,45,0.08));`;\n container.appendChild(grass);\n }\n\n function buildRainScene(container) {\n container.innerHTML = '';\n const w = container.offsetWidth || 360;\n const h = container.offsetHeight || 240;\n\n // 雨滴 × 40\n for (let i = 0; i < 40; i++) {\n const drop = document.createElement('div');\n drop.className = 'rain-drop';\n const dh = rand(12, 28);\n drop.style.height = dh + 'px';\n drop.style.left = rand(0, w) + 'px';\n drop.style.top = '-20px';\n drop.style.animation = `drop-fall ${rand(0.6, 1.2)}s linear ${rand(0, 1.2)}s infinite`;\n drop.style.opacity = rand(0.3, 0.7);\n container.appendChild(drop);\n }\n\n // 水洼\n const puddle = document.createElement('div');\n puddle.className = 'rain-puddle';\n container.appendChild(puddle);\n\n // 涟漪 × 3\n for (let i = 0; i < 3; i++) {\n const ripple = document.createElement('div');\n ripple.className = 'rain-ripple';\n ripple.style.left = rand(20, w - 20) + 'px';\n ripple.style.animation = `ripple-expand ${rand(2, 3.5)}s ease-out ${rand(0, 3)}s infinite`;\n container.appendChild(ripple);\n }\n }\n\n function buildSunnyScene(container) {\n container.innerHTML = '';\n const w = container.offsetWidth || 360;\n const h = container.offsetHeight || 240;\n const cx = w / 2;\n const cy = h * 0.35;\n\n // 光晕\n const glow = document.createElement('div');\n glow.className = 'sun-glow';\n glow.style.left = (cx - 80) + 'px';\n glow.style.top = (cy - 80) + 'px';\n glow.style.animation = 'sun-pulse 4s ease-in-out infinite';\n container.appendChild(glow);\n\n // 太阳\n const sun = document.createElement('div');\n sun.className = 'sun-disk';\n sun.style.left = (cx - 36) + 'px';\n sun.style.top = (cy - 36) + 'px';\n sun.style.animation = 'sun-pulse 4s ease-in-out infinite';\n container.appendChild(sun);\n\n // 光线 × 12\n for (let i = 0; i < 12; i++) {\n const ray = document.createElement('div');\n ray.className = 'sun-ray';\n const angle = (i / 12) * 360;\n const rad = (angle * Math.PI) / 180;\n const len = rand(40, 80);\n ray.style.width = '2px';\n ray.style.height = len + 'px';\n ray.style.left = (cx - 1) + 'px';\n ray.style.top = (cy - len) + 'px';\n ray.style.transformOrigin = 'center bottom';\n ray.style.setProperty('--ray-angle', angle + 'deg');\n ray.style.transform = `rotate(${angle}deg)`;\n ray.style.animation = `ray-rotate ${rand(2.5, 4)}s ease-in-out ${rand(0, 2)}s infinite`;\n ray.style.opacity = rand(0.2, 0.5);\n container.appendChild(ray);\n }\n\n // 闪烁粒子 × 8\n for (let i = 0; i < 8; i++) {\n const sp = document.createElement('div');\n sp.className = 'sun-sparkle';\n sp.style.left = rand(10, w - 10) + 'px';\n sp.style.top = rand(10, h - 10) + 'px';\n sp.style.animation = `sparkle-twinkle ${rand(2, 5)}s ease-in-out ${rand(0, 4)}s infinite`;\n sp.style.opacity = 0;\n container.appendChild(sp);\n }\n }\n\n function buildSnowScene(container) {\n container.innerHTML = '';\n const w = container.offsetWidth || 360;\n const h = container.offsetHeight || 240;\n\n // 雪花 × 50\n for (let i = 0; i < 50; i++) {\n const flake = document.createElement('div');\n flake.className = 'snow-flake' + (i % 3 === 0 ? ' alt' : '');\n flake.style.left = rand(0, w) + 'px';\n flake.style.top = '-10px';\n flake.style.setProperty('--drift', rand(-30, 30) + 'px');\n const dur = rand(5, 12);\n const isAlt = i % 3 === 0;\n flake.style.animation = (isAlt ? 'snow-fall-alt' : 'snow-fall') + ` ${dur}s linear ${rand(0, dur)}s infinite`;\n flake.style.opacity = rand(0.2, 0.7);\n flake.style.width = (isAlt ? rand(2, 3) : rand(4, 7)) + 'px';\n flake.style.height = flake.style.width;\n container.appendChild(flake);\n }\n\n // 积雪地面\n const ground = document.createElement('div');\n ground.className = 'snow-ground';\n container.appendChild(ground);\n\n // 雪堆 × 3\n for (let i = 0; i < 3; i++) {\n const mound = document.createElement('div');\n mound.className = 'snow-mound';\n const mw = rand(40, 90);\n mound.style.width = mw + 'px';\n mound.style.height = (mw * 0.3) + 'px';\n mound.style.left = rand(10, w - mw - 10) + 'px';\n mound.style.bottom = '0';\n mound.style.borderRadius = '50% 50% 0 0';\n container.appendChild(mound);\n }\n }\n\n // ===== 场景构建器映射 =====\n const builders = {\n wind: buildWindScene,\n rain: buildRainScene,\n sunny: buildSunnyScene,\n snow: buildSnowScene\n };\n\n // ===== 初始化所有场景 =====\n function initAllScenes() {\n Object.keys(builders).forEach(key => {\n const container = document.getElementById('scene' + key.charAt(0).toUpperCase() + key.slice(1));\n if (container) builders[key](container);\n });\n }\n\n // ===== 切换逻辑 =====\n const cards = document.querySelectorAll('.weather-card');\n const buttons = document.querySelectorAll('.btn-weather');\n\n function switchWeather(target) {\n buttons.forEach(btn => btn.classList.remove('active'));\n\n cards.forEach(card => {\n card.classList.remove('dimmed', 'active-highlight');\n });\n\n if (target === 'all') {\n const allBtn = document.querySelector('.btn-weather.all-btn');\n if (allBtn) allBtn.classList.add('active');\n return;\n }\n\n const activeBtn = document.querySelector(`.btn-weather[data-target=\"${target}\"]`);\n if (activeBtn) activeBtn.classList.add('active');\n\n cards.forEach(card => {\n const weather = card.dataset.weather;\n if (weather === target) {\n card.classList.add('active-highlight');\n } else {\n card.classList.add('dimmed');\n }\n });\n }\n\n // 按钮点击\n buttons.forEach(btn => {\n btn.addEventListener('click', function(e) {\n const target = this.dataset.target;\n switchWeather(target);\n });\n });\n\n // ===== 窗口变化时重新生成场景 =====\n let resizeTimer;\n window.addEventListener('resize', function() {\n clearTimeout(resizeTimer);\n resizeTimer = setTimeout(initAllScenes, 400);\n });\n\n // ===== 启动 =====\n initAllScenes();\n\n})();\n</script>\n\n</body>\n</html>"
},
"next": "[\"切换晴天\",\"切换降雨\",\"切换下雪\",\"切换风力\",\"重置全部\"]",
"breakpoint": "none"
}
```

total_tokens
20815
summary
设计并构建了一个苹果风格的高级天气应用HTML文件,包含4张动画天气卡片(风力、降雨、晴天、下雪),深色玻璃态背景,每组天气拥有独立精细的粒子/流体动画系统。底部提供切换按钮组,支持显示全部或单独展示某一天气状态。采用Outfit+Inter字体组合,Font Awesome图标,TailwindCSS辅助布局,自定义CSS实现全部动画效果。
uiParsing
关闭