用lm arena测了天气卡片,感觉前端提升很大

提示词:你是 Apple Inc 的 UI 设计师,以 iOS 18 的设计风格做一个带有动画效果的天气卡片,要求是使用 HTML、CSS 和基础 JavaScript,使用横板天气页面(拥有 4 个天气卡片 (晴天,大风,暴雨,暴雪))。应足够美观,实现一定的交互效果。
html代码
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>iOS 18 风格 - 天气卡片</title>
<style>
/* ======= 基础设定 / iOS 18 风格基因 ======= */
:root{
--radius: 22px;
--glass: rgba(255,255,255,0.12);
--glass-strong: rgba(255,255,255,0.18);
--glass-border: rgba(255,255,255,0.35);
--text: rgba(255,255,255,0.92);
--subtext: rgba(255,255,255,0.72);
--shadow: 0 10px 30px rgba(0,0,0,0.25), inset 0 0 0 1px rgba(255,255,255,0.08);
/* 动态背景色带(跟随时间缓慢变色) */
--bg-1: #131a2a;
--bg-2: #0b1220;
--bg-3: #1c2a4a;
/* 主题色(不同卡片) */
--sunny-1: #FFB82B; --sunny-2: #FF7A00; --sunny-3: #FF5E62;
--windy-1: #46E4D4; --windy-2: #3399FF; --windy-3: #5F7FFF;
--rain-1: #0EA5E9; --rain-2: #2563EB; --rain-3: #1E3A8A;
--snow-1: #8ED1FF; --snow-2: #6EC3FF; --snow-3: #9AA9FF;
}
@media (prefers-color-scheme: light){
:root{
--text: rgba(14,14,16,0.95);
--subtext: rgba(14,14,16,0.65);
--glass: rgba(255,255,255,0.7);
--glass-strong: rgba(255,255,255,0.9);
--glass-border: rgba(0,0,0,0.08);
--shadow: 0 10px 30px rgba(0,0,0,0.12), inset 0 0 0 1px rgba(0,0,0,0.06);
--bg-1: #E9F0FF; --bg-2: #EFF6FF; --bg-3: #E8ECFF;
}
}
*{ box-sizing: border-box; }
html, body { height: 100%; }
body{
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft Yahei", sans-serif;
color: var(--text);
background: radial-gradient(1200px 800px at 20% 10%, var(--bg-3), transparent 60%),
radial-gradient(1200px 800px at 80% 90%, var(--bg-1), transparent 60%),
radial-gradient(1200px 800px at 50% 50%, var(--bg-2), transparent 60%),
linear-gradient(180deg, #0b0f1a, #0b0f1a);
background-attachment: fixed;
overflow: hidden;
}
/* 背景缓慢变色 */
@keyframes bgShift {
0% { filter: hue-rotate(0deg) saturate(1); }
50% { filter: hue-rotate(10deg) saturate(1.1); }
100% { filter: hue-rotate(0deg) saturate(1); }
}
body::before{
content:"";
position: fixed; inset: -20vh -20vw;
background: radial-gradient(60% 80% at 30% 10%, rgba(255,255,255,0.06), transparent 60%),
radial-gradient(60% 80% at 70% 90%, rgba(255,255,255,0.06), transparent 60%);
pointer-events: none;
animation: bgShift 16s ease-in-out infinite;
}
/* ======= 顶部栏 ======= */
.topbar{
position: fixed; inset: 14px 14px auto 14px;
height: 56px;
display: flex; align-items: center; justify-content: space-between;
padding: 0 12px;
border-radius: 18px;
background: var(--glass);
-webkit-backdrop-filter: blur(12px) saturate(1.2);
backdrop-filter: blur(12px) saturate(1.2);
box-shadow: var(--shadow);
z-index: 10;
}
.title{
display: flex; align-items: center; gap: 10px; padding-left: 4px;
letter-spacing: 0.2px; font-weight: 600;
}
.title .dot{
width: 10px; height: 10px; border-radius: 50%;
background: linear-gradient(135deg, #26d07c, #2dd4bf);
box-shadow: 0 0 0 4px rgba(45,212,191,0.15);
}
/* iOS 分段控制器(℃/℉ 切换) */
.segment{
position: relative; display: inline-flex; padding: 4px;
gap: 4px; border-radius: 14px;
background: rgba(255,255,255,0.06);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border: 1px solid var(--glass-border);
}
.seg-btn{
border: 0; outline: 0; user-select: none; cursor: pointer;
padding: 6px 12px; min-width: 44px; height: 32px; border-radius: 10px;
color: var(--subtext); background: transparent; font-weight: 600;
transition: color .2s ease, background .2s ease, transform .2s cubic-bezier(.2,.8,.2,1);
}
.seg-btn.active{
color: var(--text);
background: var(--glass-strong);
box-shadow: inset 0 0 0 1px var(--glass-border), 0 6px 20px rgba(0,0,0,0.12);
transform: translateZ(0);
}
/* ======= 横向卡片容器 ======= */
.wrap{
position: absolute; inset: 0;
padding: 92px 24px 24px;
display: grid; grid-template-rows: 1fr auto;
gap: 18px;
}
.hint{
color: var(--subtext); font-size: 13px; letter-spacing: .2px;
display: flex; align-items: center; gap: 8px;
}
.hint .kbd{
padding: 2px 6px; border-radius: 6px; border: 1px solid var(--glass-border);
background: rgba(255,255,255,0.08);
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
font-weight: 600; font-size: 12px; color: var(--text);
}
.carousel{
display: grid; grid-auto-flow: column;
grid-auto-columns: minmax(290px, 360px);
gap: 16px; overflow-x: auto; overflow-y: visible; padding-bottom: 10px;
scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
}
.carousel::-webkit-scrollbar{ height: 8px; }
.carousel::-webkit-scrollbar-thumb{
background: rgba(255,255,255,0.15); border-radius: 8px;
}
.carousel > *{ scroll-snap-align: center; }
/* ======= 卡片通用 ======= */
.card{
position: relative; min-height: 240px;
border-radius: var(--radius);
background: var(--glass);
-webkit-backdrop-filter: blur(18px) saturate(1.2);
backdrop-filter: blur(18px) saturate(1.2);
box-shadow: var(--shadow);
overflow: hidden; isolation: isolate;
transform-style: preserve-3d;
transform: perspective(1000px) rotateX(var(--tiltX,0deg)) rotateY(var(--tiltY,0deg));
transition: transform .3s cubic-bezier(.2,.8,.2,1), box-shadow .3s ease;
cursor: pointer;
}
.card:active{ transform: perspective(1000px) scale(.985) rotateX(var(--tiltX,0deg)) rotateY(var(--tiltY,0deg)); }
.card .chrome{
position: absolute; inset: 0; pointer-events: none;
background: radial-gradient(120% 120% at -10% -10%, rgba(255,255,255,.18), transparent 40%),
radial-gradient(120% 120% at 110% 110%, rgba(255,255,255,.16), transparent 40%);
mix-blend-mode: soft-light;
opacity: .8;
}
.card .tint{
position: absolute; inset: 0; z-index: 0; opacity: .5; filter: saturate(1.2) blur(0);
transition: opacity .3s ease;
}
.card:hover .tint{ opacity: .6; }
.card .body{
position: relative; z-index: 1; padding: 16px; display: grid; gap: 12px;
}
.row{
display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.tag{
display: inline-flex; align-items: center; gap: 8px;
padding: 6px 10px; border-radius: 12px; font-weight: 600; letter-spacing: .2px;
color: var(--text); background: rgba(255,255,255,0.14);
border: 1px solid var(--glass-border);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
.temp{
font-weight: 800; font-size: 44px; letter-spacing: -1px; display: flex; align-items: baseline; gap: 2px;
}
.sub{
color: var(--subtext); font-size: 13px; letter-spacing: .2px;
}
.metrics{
display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
}
.chip{
display: grid; gap: 4px; padding: 10px; border-radius: 12px;
background: rgba(255,255,255,0.10);
border: 1px solid var(--glass-border);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
text-align: center;
}
.chip b{ font-weight: 700; }
.chip .u{ color: var(--subtext); font-size: 12px; }
/* 展开详情 */
.details{
max-height: 0; overflow: hidden; transition: max-height .5s cubic-bezier(.2,.8,.2,1);
}
.card.expanded .details{ max-height: 220px; }
.hours{
margin-top: 10px; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 8px;
}
.pill{
border-radius: 12px; padding: 8px; display: grid; gap: 6px; place-items: center;
background: rgba(255,255,255,0.10);
border: 1px solid var(--glass-border);
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
min-width: 64px;
}
/* ======= 每个天气的主题色与动画层 ======= */
/* sunny */
.card.sunny .tint{
background: radial-gradient(100% 120% at 20% 15%, var(--sunny-1), transparent 40%),
radial-gradient(100% 120% at 90% 100%, var(--sunny-2), transparent 40%),
linear-gradient(180deg, var(--sunny-3), transparent);
}
.sun{
position: absolute; right: -12px; top: -16px; width: 180px; height: 180px; border-radius: 50%;
background: radial-gradient(circle at 50% 50%, #fff6bd 0%, #ffd166 35%, #ffb200 70%, rgba(255,178,0,0) 72%);
box-shadow: 0 0 60px 20px rgba(255, 188, 45, 0.45);
animation: floatSun 6s ease-in-out infinite alternate;
filter: saturate(1.1);
transform: translateZ(40px);
}
@keyframes floatSun{
0%{ transform: translateZ(40px) translateY(0) rotate(0deg); }
100%{ transform: translateZ(40px) translateY(6px) rotate(3deg); }
}
.sun::before{
content:""; position: absolute; inset: -26px; border-radius: 50%;
background: conic-gradient(from 0deg, rgba(255,210,80,.65), rgba(255,216,120,.0) 20% 30%, rgba(255,210,80,.65) 40% 41%, rgba(255,216,120,.0) 60% 70%, rgba(255,210,80,.65) 90% 91%, rgba(255,216,120,.0) 100%);
filter: blur(2px);
animation: rays 12s linear infinite;
}
@keyframes rays { to { transform: rotate(360deg); } }
/* windy */
.card.windy .tint{
background: radial-gradient(120% 120% at 15% 20%, var(--windy-1), transparent 40%),
radial-gradient(120% 120% at 85% 90%, var(--windy-2), transparent 40%),
linear-gradient(180deg, var(--windy-3), transparent);
}
.wind-lines{ position: absolute; inset: 0; overflow: hidden; z-index: 0; }
.wind-line{
position: absolute; height: 2px; width: 140px; border-radius: 2px;
background: linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,.9), rgba(255,255,255,0));
left: -160px;
filter: drop-shadow(0 0 8px rgba(255,255,255,.45));
animation: blow 4s linear infinite;
transform: translateZ(30px);
}
@keyframes blow{
0% { transform: translateX(0) translateZ(30px); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100%{ transform: translateX(140% ) translateZ(30px); opacity: 0; }
}
/* rain */
.card.rain .tint{
background: radial-gradient(120% 120% at 20% 20%, var(--rain-1), transparent 40%),
radial-gradient(120% 120% at 85% 100%, var(--rain-2), transparent 40%),
linear-gradient(180deg, var(--rain-3), transparent);
}
.cloud{
position: absolute; top: 8px; left: -14px; width: 180px; height: 90px;
border-radius: 50px; background: rgba(255,255,255,0.85);
filter: blur(0.2px) saturate(1.1); opacity: .9;
box-shadow: inset 0 -8px 20px rgba(0,0,0,0.05);
animation: drift 10s ease-in-out infinite alternate;
transform: translateZ(18px);
}
.cloud::before, .cloud::after{
content: ""; position: absolute; background: inherit; border-radius: 50%;
}
.cloud::before{ width: 90px; height: 90px; left: 30px; top: -36px; }
.cloud::after{ width: 120px; height: 120px; left: 80px; top: -50px; }
@keyframes drift{ from{ transform: translateZ(18px) translateX(0); } to{ transform: translateZ(18px) translateX(18px); } }
.rain-wrap{ position: absolute; inset: 0; overflow: hidden; }
.drop{
position: absolute; width: 2px; height: 18px; border-radius: 1px;
background: linear-gradient(180deg, rgba(255,255,255,.2), rgba(255,255,255,.9));
filter: drop-shadow(0 0 4px rgba(255,255,255,.6));
animation: fall var(--spd,900ms) linear infinite;
top: -24px;
transform: translateZ(22px);
}
@keyframes fall{
to { transform: translateY(240px) translateZ(22px); opacity: .85; }
}
/* snow */
.card.snow .tint{
background: radial-gradient(120% 120% at 20% 20%, var(--snow-1), transparent 40%),
radial-gradient(120% 120% at 85% 90%, var(--snow-2), transparent 40%),
linear-gradient(180deg, var(--snow-3), transparent);
}
.snow-wrap{ position: absolute; inset: 0; overflow: hidden; }
.flake{
position: absolute; width: var(--sz,8px); height: var(--sz,8px);
border-radius: 50%; background: rgba(255,255,255,.95);
filter: drop-shadow(0 0 6px rgba(255,255,255,.7));
top: -20px; left: 50%;
animation: snowFall var(--spd,6s) linear infinite, sway var(--swy,2.4s) ease-in-out infinite;
transform: translateZ(22px);
}
@keyframes snowFall{ to { transform: translateY(270px) translateZ(22px); opacity: .95; } }
@keyframes sway{ 0%,100% { margin-left: -6px; } 50% { margin-left: 6px; } }
/* 小图标(小时预报用) */
.ico{
width: 22px; height: 22px; flex: 0 0 22px;
}
.ico.sunny{
background: radial-gradient(circle at 50% 50%, #fff6bd 0%, #ffd166 55%, #ffb200 60%);
border-radius: 50%;
box-shadow: 0 0 10px 2px rgba(255,188,45,0.5);
}
.ico.wind{
background: linear-gradient(90deg, rgba(255,255,255,0), #fff, rgba(255,255,255,0));
height: 2px; margin-top: 10px;
box-shadow: 0 4px 0 0 rgba(255,255,255,.8);
}
.ico.rain{
background: linear-gradient(180deg, rgba(255,255,255,.2), #fff);
width: 4px; height: 12px; border-radius: 2px;
box-shadow: 6px 0 0 rgba(255,255,255,.9), -6px 0 0 rgba(255,255,255,.6);
}
.ico.snow{
background: #fff; width: 6px; height: 6px; border-radius: 50%;
box-shadow: 6px 0 0 #fff, -6px 0 0 #fff;
}
/* 运动减弱支持 */
@media (prefers-reduced-motion: reduce){
.sun, .cloud, .wind-line, .drop, .flake, body::before{ animation: none !important; }
.card{ transition: none !important; }
}
</style>
</head>
<body>
<div class="topbar">
<div class="title">
<span class="dot"></span>
<div>天气 · iOS 18 风格</div>
</div>
<div class="segment" role="tablist" aria-label="单位切换">
<button class="seg-btn active" id="btnC" role="tab" aria-selected="true">℃</button>
<button class="seg-btn" id="btnF" role="tab" aria-selected="false">℉</button>
</div>
</div>
<div class="wrap">
<div class="carousel" id="carousel">
<!-- 晴天 -->
<article class="card sunny" data-c="28" data-label="晴天">
<div class="tint"></div><div class="chrome"></div>
<div class="sun" aria-hidden="true"></div>
<div class="body">
<div class="row">
<span class="tag">晴天</span>
<div class="temp"><span class="val">28</span>°<span class="unit">C</span></div>
</div>
<div class="row sub">
<span>杭州 · 体感 30°</span>
<span>紫外线 强</span>
</div>
<div class="metrics">
<div class="chip">
<div class="sub">湿度</div><b>45%</b>
<div class="u">较舒适</div>
</div>
<div class="chip">
<div class="sub">风速</div><b>3.2 m/s</b>
<div class="u">东北风</div>
</div>
<div class="chip">
<div class="sub">能见度</div><b>16 km</b>
<div class="u">良好</div>
</div>
</div>
<div class="details">
<div class="hours">
<div class="pill"><div class="sub">现在</div><div class="ico sunny"></div><b>28°</b></div>
<div class="pill"><div class="sub">+1时</div><div class="ico sunny"></div><b>29°</b></div>
<div class="pill"><div class="sub">+2时</div><div class="ico sunny"></div><b>30°</b></div>
<div class="pill"><div class="sub">+3时</div><div class="ico sunny"></div><b>30°</b></div>
</div>
</div>
</div>
</article>
<!-- 大风 -->
<article class="card windy" data-c="19" data-label="大风">
<div class="tint"></div><div class="chrome"></div>
<div class="wind-lines" aria-hidden="true"></div>
<div class="body">
<div class="row">
<span class="tag">大风</span>
<div class="temp"><span class="val">19</span>°<span class="unit">C</span></div>
</div>
<div class="row sub">
<span>青海 · 阵风 24 m/s</span>
<span>体感 更冷</span>
</div>
<div class="metrics">
<div class="chip">
<div class="sub">湿度</div><b>30%</b>
<div class="u">干燥</div>
</div>
<div class="chip">
<div class="sub">风速</div><b>22.4 m/s</b>
<div class="u">注意防风</div>
</div>
<div class="chip">
<div class="sub">气压</div><b>880 hPa</b>
<div class="u">偏低</div>
</div>
</div>
<div class="details">
<div class="hours">
<div class="pill"><div class="sub">现在</div><div class="ico wind"></div><b>19°</b></div>
<div class="pill"><div class="sub">+1时</div><div class="ico wind"></div><b>18°</b></div>
<div class="pill"><div class="sub">+2时</div><div class="ico wind"></div><b>18°</b></div>
<div class="pill"><div class="sub">+3时</div><div class="ico wind"></div><b>17°</b></div>
</div>
</div>
</div>
</article>
<!-- 暴雨 -->
<article class="card rain" data-c="23" data-label="暴雨">
<div class="tint"></div><div class="chrome"></div>
<div class="cloud" aria-hidden="true"></div>
<div class="rain-wrap" aria-hidden="true"></div>
<div class="body">
<div class="row">
<span class="tag">暴雨</span>
<div class="temp"><span class="val">23</span>°<span class="unit">C</span></div>
</div>
<div class="row sub">
<span>广州 · 降雨 > 50 mm</span>
<span>雷达回波 强</span>
</div>
<div class="metrics">
<div class="chip">
<div class="sub">降雨概率</div><b>95%</b>
<div class="u">请携带雨具</div>
</div>
<div class="chip">
<div class="sub">风速</div><b>12.6 m/s</b>
<div class="u">阵风偏强</div>
</div>
<div class="chip">
<div class="sub">云量</div><b>92%</b>
<div class="u">厚云</div>
</div>
</div>
<div class="details">
<div class="hours">
<div class="pill"><div class="sub">现在</div><div class="ico rain"></div><b>23°</b></div>
<div class="pill"><div class="sub">+1时</div><div class="ico rain"></div><b>22°</b></div>
<div class="pill"><div class="sub">+2时</div><div class="ico rain"></div><b>22°</b></div>
<div class="pill"><div class="sub">+3时</div><div class="ico rain"></div><b>21°</b></div>
</div>
</div>
</div>
</article>
<!-- 暴雪 -->
<article class="card snow" data-c="-6" data-label="暴雪">
<div class="tint"></div><div class="chrome"></div>
<div class="snow-wrap" aria-hidden="true"></div>
<div class="body">
<div class="row">
<span class="tag">暴雪</span>
<div class="temp"><span class="val">-6</span>°<span class="unit">C</span></div>
</div>
<div class="row sub">
<span>哈尔滨 · 路面结冰</span>
<span>寒潮预警</span>
</div>
<div class="metrics">
<div class="chip">
<div class="sub">体感</div><b>-12°</b>
<div class="u">风冷效应</div>
</div>
<div class="chip">
<div class="sub">风速</div><b>35 m/s</b>
<div class="u">出行谨慎</div>
</div>
<div class="chip">
<div class="sub">降雪强度</div><b>大到暴</b>
<div class="u">能见度差</div>
</div>
</div>
<div class="details">
<div class="hours">
<div class="pill"><div class="sub">现在</div><div class="ico snow"></div><b>-6°</b></div>
<div class="pill"><div class="sub">+1时</div><div class="ico snow"></div><b>-7°</b></div>
<div class="pill"><div class="sub">+2时</div><div class="ico snow"></div><b>-7°</b></div>
<div class="pill"><div class="sub">+3时</div><div class="ico snow"></div><b>-8°</b></div>
</div>
</div>
</div>
</article>
</div>
<div class="hint">
轻触卡片展开 · 鼠标移动/手指滑动体验视差与横向滚动
<span class="kbd">⌘</span><span class="sub">可搭配 Trackpad 惯性滑动</span>
</div>
</div>
<script>
// ======= 基础交互:温度单位切换、卡片展开、视差倾斜 =======
const btnC = document.getElementById('btnC');
const btnF = document.getElementById('btnF');
const cards = document.querySelectorAll('.card');
let unit = 'C'; // 默认摄氏
function c2f(c){ return Math.round(c * 9/5 + 32); }
function updateTemps(){
cards.forEach(card=>{
const c = parseFloat(card.dataset.c);
const elVal = card.querySelector('.temp .val');
const elUnit = card.querySelector('.temp .unit');
if(!isFinite(c) || !elVal || !elUnit) return;
if(unit === 'C'){ elVal.textContent = Math.round(c); elUnit.textContent = 'C'; }
else { elVal.textContent = c2f(c); elUnit.textContent = 'F'; }
// 同步小时预报里的温度:简单查找所有 pill 中的度数(带°)
card.querySelectorAll('.pill b').forEach((b,i)=>{
const base = (card.dataset.label === '晴天' ? [28,29,30,30] :
card.dataset.label === '大风' ? [19,18,18,17] :
card.dataset.label === '暴雨' ? [23,22,22,21] :
[-6,-7,-7,-8])[i] || 0;
const value = unit === 'C' ? base : c2f(base);
b.textContent = value + '°';
});
});
}
btnC.addEventListener('click', ()=>{
if(unit !== 'C'){
unit = 'C';
btnC.classList.add('active'); btnC.setAttribute('aria-selected','true');
btnF.classList.remove('active'); btnF.setAttribute('aria-selected','false');
updateTemps();
}
});
btnF.addEventListener('click', ()=>{
if(unit !== 'F'){
unit = 'F';
btnF.classList.add('active'); btnF.setAttribute('aria-selected','true');
btnC.classList.remove('active'); btnC.setAttribute('aria-selected','false');
updateTemps();
}
});
// 卡片展开/收起
cards.forEach(card=>{
card.addEventListener('click', (e)=>{
// 避免拖动时立即触发:简单阈值判断
const wasDragging = Math.abs((card._dragDX||0)) > 6;
if(wasDragging){ card._dragDX = 0; return; }
card.classList.toggle('expanded');
});
// 3D 视差倾斜
const MAX = 10; // 最大倾斜角
function setTilt(x, y){
const rect = card.getBoundingClientRect();
const px = (x - rect.left) / rect.width; // 0..1
const py = (y - rect.top) / rect.height; // 0..1
const tiltY = (0.5 - px) * (MAX * 2); // 左右
const tiltX = (py - 0.5) * (MAX * 2); // 上下
card.style.setProperty('--tiltX', tiltX.toFixed(2) + 'deg');
card.style.setProperty('--tiltY', tiltY.toFixed(2) + 'deg');
}
card.addEventListener('mousemove', e=> setTilt(e.clientX, e.clientY));
card.addEventListener('mouseleave', ()=> {
card.style.removeProperty('--tiltX');
card.style.removeProperty('--tiltY');
});
});
// 轻量拖动识别(用于阻止点击误触)
const carousel = document.getElementById('carousel');
let downX = 0, dragging = false;
carousel.addEventListener('pointerdown', e=>{ downX = e.clientX; dragging = true; });
carousel.addEventListener('pointermove', e=>{
if(dragging){
const dx = e.clientX - downX;
const hovered = document.elementFromPoint(e.clientX, e.clientY);
const card = hovered?.closest?.('.card');
if(card) card._dragDX = dx;
}
});
window.addEventListener('pointerup', ()=> dragging = false);
// ======= 动画元素生成:风线 / 雨滴 / 雪花 =======
// 风线:每张 windy 卡生成多条,时长和位置随机
document.querySelectorAll('.card.windy .wind-lines').forEach(layer=>{
const rows = 8;
for(let i=0;i<rows;i++){
const line = document.createElement('span');
line.className = 'wind-line';
const y = 30 + i * 20 + Math.random()*10; // 垂直位置
const dur = 3.2 + Math.random()*2.2; // 时长
const delay = -Math.random()*dur; // 负延迟以打散
const w = 120 + Math.random()*120; // 宽度浮动
line.style.top = y+'px';
line.style.width = w+'px';
line.style.animationDuration = dur+'s';
line.style.animationDelay = delay+'s';
layer.appendChild(line);
}
});
// 雨滴:暴雨卡片生成大量雨滴
document.querySelectorAll('.card.rain .rain-wrap').forEach(layer=>{
const drops = 70; // 密度
const rectHeight = 260; // 估计区域高度
for(let i=0;i<drops;i++){
const d = document.createElement('span');
d.className = 'drop';
const left = Math.random()*100; // 百分比位置
const spd = 600 + Math.random()*900; // 时长
const delay = -Math.random()*spd; // 负延迟
const skew = -5 + Math.random()*10; // 风偏斜
const h = 14 + Math.random()*14; // 雨滴长度
d.style.left = left+'%';
d.style.height = h+'px';
d.style.transform = `translateZ(22px) skewX(${skew}deg)`;
d.style.animationDuration = spd+'ms';
d.style.animationDelay = delay+'ms';
layer.appendChild(d);
}
});
// 雪花:大小、摆动、速度随机
document.querySelectorAll('.card.snow .snow-wrap').forEach(layer=>{
const flakes = 46;
for(let i=0;i<flakes;i++){
const f = document.createElement('span');
f.className = 'flake';
const size = 4 + Math.random()*8;
const left = Math.random()*100;
const spd = 5 + Math.random()*6;
const sway = 1.8 + Math.random()*2.4;
const delay = -Math.random()*spd;
f.style.setProperty('--sz', size+'px');
f.style.left = left+'%';
f.style.animationDuration = `${spd}s, ${sway}s`;
f.style.animationDelay = `${delay}s, ${delay/2}s`;
layer.appendChild(f);
}
});
// 初始温度渲染
updateTemps();
</script>
</body>
</html>