iOS 18风格天气卡片edit icon

创建者:
Jason_ghost
Fork(复制)
下载
嵌入
BUG反馈
index.html
index.html
            
            <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>iOS 18 天气卡片</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
            overflow-x: hidden;
        }

        .weather-container {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
            gap: 25px;
            max-width: 1400px;
            width: 100%;
            padding: 20px;
        }

        .weather-card {
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(20px);
            -webkit-backdrop-filter: blur(20px);
            border-radius: 30px;
            padding: 30px;
            min-height: 400px;
            position: relative;
            overflow: hidden;
            border: 1px solid rgba(255, 255, 255, 0.2);
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
            transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            cursor: pointer;
        }

        .weather-card:hover {
            transform: translateY(-10px) scale(1.02);
            box-shadow: 0 30px 60px rgba(0, 0, 0, 0.2);
        }

        .weather-card::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%);
            border-radius: 30px;
            pointer-events: none;
        }

        /* 晴天卡片 */
        .sunny {
            background: linear-gradient(135deg, #FFD93D 0%, #FFB344 100%);
        }

        /* 大风卡片 */
        .windy {
            background: linear-gradient(135deg, #6DD5FA 0%, #2980B9 100%);
        }

        /* 暴雨卡片 */
        .rainy {
            background: linear-gradient(135deg, #536976 0%, #292E49 100%);
        }

        /* 暴雪卡片 */
        .snowy {
            background: linear-gradient(135deg, #E6DADA 0%, #274046 100%);
        }

        .weather-icon {
            font-size: 80px;
            margin-bottom: 20px;
            animation: float 3s ease-in-out infinite;
            filter: drop-shadow(0 10px 20px rgba(0,0,0,0.2));
        }

        @keyframes float {
            0%, 100% { transform: translateY(0px); }
            50% { transform: translateY(-20px); }
        }

        .temperature {
            font-size: 72px;
            font-weight: 200;
            color: white;
            margin-bottom: 10px;
            text-shadow: 0 4px 10px rgba(0,0,0,0.2);
        }

        .weather-type {
            font-size: 24px;
            font-weight: 500;
            color: rgba(255, 255, 255, 0.95);
            margin-bottom: 20px;
            letter-spacing: 1px;
        }

        .location {
            font-size: 18px;
            color: rgba(255, 255, 255, 0.8);
            margin-bottom: 30px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .details {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 15px;
            margin-top: auto;
        }

        .detail-item {
            background: rgba(255, 255, 255, 0.15);
            padding: 12px;
            border-radius: 15px;
            backdrop-filter: blur(10px);
            transition: all 0.3s ease;
        }

        .detail-item:hover {
            background: rgba(255, 255, 255, 0.25);
            transform: scale(1.05);
        }

        .detail-label {
            font-size: 12px;
            color: rgba(255, 255, 255, 0.7);
            text-transform: uppercase;
            letter-spacing: 1px;
            margin-bottom: 4px;
        }

        .detail-value {
            font-size: 18px;
            font-weight: 600;
            color: white;
        }

        /* 动画效果 */
        .sun-icon {
            animation: rotate 20s linear infinite;
        }

        @keyframes rotate {
            from { transform: rotate(0deg); }
            to { transform: rotate(360deg); }
        }

        .wind-lines {
            position: absolute;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            pointer-events: none;
            overflow: hidden;
        }

        .wind-line {
            position: absolute;
            height: 2px;
            background: rgba(255, 255, 255, 0.3);
            animation: wind 3s linear infinite;
        }

        @keyframes wind {
            from { transform: translateX(-100%); }
            to { transform: translateX(200%); }
        }

        .rain-container {
            position: absolute;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            pointer-events: none;
            overflow: hidden;
        }

        .rain {
            position: absolute;
            width: 2px;
            height: 100px;
            background: linear-gradient(to bottom, transparent, rgba(255,255,255,0.4));
            animation: rain 1s linear infinite;
        }

        @keyframes rain {
            from { transform: translateY(-100px); }
            to { transform: translateY(calc(100% + 100px)); }
        }

        .snow-container {
            position: absolute;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            pointer-events: none;
            overflow: hidden;
        }

        .snowflake {
            position: absolute;
            color: white;
            font-size: 1em;
            animation: snowfall linear infinite;
        }

        @keyframes snowfall {
            from {
                transform: translateY(-100px);
            }
            to {
                transform: translateY(calc(100vh + 100px));
            }
        }

        /* 响应式设计 */
        @media (max-width: 768px) {
            .weather-container {
                grid-template-columns: 1fr;
            }
            
            .temperature {
                font-size: 60px;
            }
        }

        /* 脉冲动画 */
        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.6; }
            100% { opacity: 1; }
        }

        .pulse {
            animation: pulse 2s ease-in-out infinite;
        }
    </style>
</head>
<body>
    <div class="weather-container">
        <!-- 晴天卡片 -->
        <div class="weather-card sunny" onclick="animateCard(this)">
            <div class="weather-icon sun-icon">☀️</div>
            <div class="temperature">28°</div>
            <div class="weather-type">晴天</div>
            <div class="location">
                <span>📍</span>
                <span>北京</span>
            </div>
            <div class="details">
                <div class="detail-item">
                    <div class="detail-label">体感温度</div>
                    <div class="detail-value">30°</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">紫外线</div>
                    <div class="detail-value">强</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">湿度</div>
                    <div class="detail-value">45%</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">能见度</div>
                    <div class="detail-value">10km</div>
                </div>
            </div>
        </div>

        <!-- 大风卡片 -->
        <div class="weather-card windy" onclick="animateCard(this)">
            <div class="wind-lines">
                <div class="wind-line" style="top: 20%; width: 60%; animation-delay: 0s;"></div>
                <div class="wind-line" style="top: 40%; width: 80%; animation-delay: 0.5s;"></div>
                <div class="wind-line" style="top: 60%; width: 70%; animation-delay: 1s;"></div>
                <div class="wind-line" style="top: 80%; width: 90%; animation-delay: 1.5s;"></div>
            </div>
            <div class="weather-icon">💨</div>
            <div class="temperature">22°</div>
            <div class="weather-type">大风</div>
            <div class="location">
                <span>📍</span>
                <span>上海</span>
            </div>
            <div class="details">
                <div class="detail-item">
                    <div class="detail-label">风速</div>
                    <div class="detail-value">35km/h</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">风向</div>
                    <div class="detail-value">西北</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">气压</div>
                    <div class="detail-value">1008hPa</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">阵风</div>
                    <div class="detail-value">45km/h</div>
                </div>
            </div>
        </div>

        <!-- 暴雨卡片 -->
        <div class="weather-card rainy" onclick="animateCard(this)">
            <div class="rain-container" id="rainContainer"></div>
            <div class="weather-icon">⛈️</div>
            <div class="temperature">18°</div>
            <div class="weather-type">暴雨</div>
            <div class="location">
                <span>📍</span>
                <span>广州</span>
            </div>
            <div class="details">
                <div class="detail-item">
                    <div class="detail-label">降雨量</div>
                    <div class="detail-value">25mm/h</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">雷暴概率</div>
                    <div class="detail-value">85%</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">湿度</div>
                    <div class="detail-value">92%</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">能见度</div>
                    <div class="detail-value">2km</div>
                </div>
            </div>
        </div>

        <!-- 暴雪卡片 -->
        <div class="weather-card snowy" onclick="animateCard(this)">
            <div class="snow-container" id="snowContainer"></div>
            <div class="weather-icon">❄️</div>
            <div class="temperature">-5°</div>
            <div class="weather-type">暴雪</div>
            <div class="location">
                <span>📍</span>
                <span>哈尔滨</span>
            </div>
            <div class="details">
                <div class="detail-item">
                    <div class="detail-label">降雪量</div>
                    <div class="detail-value">15cm</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">风寒指数</div>
                    <div class="detail-value">-12°</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">湿度</div>
                    <div class="detail-value">78%</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">能见度</div>
                    <div class="detail-value">500m</div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 创建雨滴效果
        function createRain() {
            const rainContainer = document.getElementById('rainContainer');
            for (let i = 0; i < 20; i++) {
                const rain = document.createElement('div');
                rain.className = 'rain';
                rain.style.left = Math.random() * 100 + '%';
                rain.style.animationDelay = Math.random() * 2 + 's';
                rain.style.animationDuration = 0.5 + Math.random() * 0.5 + 's';
                rainContainer.appendChild(rain);
            }
        }

        // 创建雪花效果
        function createSnow() {
            const snowContainer = document.getElementById('snowContainer');
            const snowflakes = ['❄', '❅', '❆'];
            
            for (let i = 0; i < 30; i++) {
                const snowflake = document.createElement('div');
                snowflake.className = 'snowflake';
                snowflake.innerHTML = snowflakes[Math.floor(Math.random() * snowflakes.length)];
                snowflake.style.left = Math.random() * 100 + '%';
                snowflake.style.animationDuration = 5 + Math.random() * 10 + 's';
                snowflake.style.animationDelay = Math.random() * 5 + 's';
                snowflake.style.fontSize = 10 + Math.random() * 20 + 'px';
                snowflake.style.opacity = 0.4 + Math.random() * 0.6;
                snowContainer.appendChild(snowflake);
            }
        }

        // 卡片点击动画
        function animateCard(card) {
            card.style.animation = 'none';
            setTimeout(() => {
                card.style.animation = 'pulse 0.5s ease-in-out';
            }, 10);
            
            // 添加涟漪效果
            const ripple = document.createElement('div');
            ripple.style.position = 'absolute';
            ripple.style.width = '20px';
            ripple.style.height = '20px';
            ripple.style.background = 'rgba(255,255,255,0.5)';
            ripple.style.borderRadius = '50%';
            ripple.style.transform = 'translate(-50%, -50%)';
            ripple.style.pointerEvents = 'none';
            ripple.style.animation = 'ripple 0.6s ease-out';
            
            const rect = card.getBoundingClientRect();
            ripple.style.left = '50%';
            ripple.style.top = '50%';
            
            card.appendChild(ripple);
            
            setTimeout(() => {
                ripple.remove();
            }, 600);
        }

        // 添加涟漪动画样式
        const style = document.createElement('style');
        style.textContent = `
            @keyframes ripple {
                from {
                    width: 20px;
                    height: 20px;
                    opacity: 0.5;
                }
                to {
                    width: 400px;
                    height: 400px;
                    opacity: 0;
                }
            }
        `;
        document.head.appendChild(style);

        // 初始化天气效果
        createRain();
        createSnow();

                // 添加视差滚动效果
        document.addEventListener('mousemove', (e) => {
            const cards = document.querySelectorAll('.weather-card');
            const mouseX = e.clientX / window.innerWidth - 0.5;
            const mouseY = e.clientY / window.innerHeight - 0.5;
            
            cards.forEach((card, index) => {
                const depth = (index + 1) * 0.5;
                const moveX = mouseX * depth * 10;
                const moveY = mouseY * depth * 10;
                
                card.style.transform = `translateX(${moveX}px) translateY(${moveY}px)`;
            });
        });

        // 添加触摸设备支持
        let touchStartX = 0;
        let touchStartY = 0;
        
        document.addEventListener('touchstart', (e) => {
            touchStartX = e.touches[0].clientX;
            touchStartY = e.touches[0].clientY;
        });
        
        document.addEventListener('touchmove', (e) => {
            if (!touchStartX || !touchStartY) return;
            
            const touchX = e.touches[0].clientX;
            const touchY = e.touches[0].clientY;
            const diffX = (touchX - touchStartX) / window.innerWidth;
            const diffY = (touchY - touchStartY) / window.innerHeight;
            
            const cards = document.querySelectorAll('.weather-card');
            cards.forEach((card, index) => {
                const depth = (index + 1) * 0.5;
                const moveX = diffX * depth * 20;
                const moveY = diffY * depth * 20;
                
                card.style.transform = `translateX(${moveX}px) translateY(${moveY}px)`;
            });
        });

        // 实时更新时间
        function updateTime() {
            const now = new Date();
            const hours = now.getHours();
            
            // 根据时间改变背景
            const body = document.body;
            if (hours >= 6 && hours < 12) {
                // 早晨
                body.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
            } else if (hours >= 12 && hours < 18) {
                // 下午
                body.style.background = 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)';
            } else if (hours >= 18 && hours < 21) {
                // 傍晚
                body.style.background = 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)';
            } else {
                // 夜晚
                body.style.background = 'linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%)';
            }
        }

        // 添加加载动画
        window.addEventListener('load', () => {
            const cards = document.querySelectorAll('.weather-card');
            cards.forEach((card, index) => {
                card.style.opacity = '0';
                card.style.transform = 'translateY(50px)';
                
                setTimeout(() => {
                    card.style.transition = 'all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
                    card.style.opacity = '1';
                    card.style.transform = 'translateY(0)';
                }, index * 150);
            });
        });

        // 添加卡片切换动画
        let currentCard = 0;
        const cards = document.querySelectorAll('.weather-card');

        // 为每个卡片添加双击事件展开详情
        cards.forEach((card, index) => {
            card.addEventListener('dblclick', function() {
                if (this.classList.contains('expanded')) {
                    this.classList.remove('expanded');
                    this.style.position = 'relative';
                    this.style.zIndex = 'auto';
                    this.style.transform = 'scale(1)';
                    document.body.style.overflow = 'auto';
                } else {
                    // 关闭其他展开的卡片
                    cards.forEach(c => {
                        c.classList.remove('expanded');
                        c.style.position = 'relative';
                        c.style.zIndex = 'auto';
                        c.style.transform = 'scale(1)';
                    });
                    
                    this.classList.add('expanded');
                    this.style.position = 'fixed';
                    this.style.zIndex = '1000';
                    this.style.transform = 'scale(1.2)';
                    this.style.top = '50%';
                    this.style.left = '50%';
                    this.style.transform = 'translate(-50%, -50%) scale(1.2)';
                    document.body.style.overflow = 'hidden';
                }
            });
        });

        // 添加键盘导航
        document.addEventListener('keydown', (e) => {
            if (e.key === 'ArrowLeft') {
                currentCard = (currentCard - 1 + cards.length) % cards.length;
                cards[currentCard].scrollIntoView({ behavior: 'smooth', block: 'center' });
                animateCard(cards[currentCard]);
            } else if (e.key === 'ArrowRight') {
                currentCard = (currentCard + 1) % cards.length;
                cards[currentCard].scrollIntoView({ behavior: 'smooth', block: 'center' });
                animateCard(cards[currentCard]);
            } else if (e.key === 'Escape') {
                cards.forEach(card => {
                    card.classList.remove('expanded');
                    card.style.position = 'relative';
                    card.style.zIndex = 'auto';
                    card.style.transform = 'scale(1)';
                });
                document.body.style.overflow = 'auto';
            }
        });

        // 添加温度单位切换功能
        let isCelsius = true;
        cards.forEach(card => {
            const tempElement = card.querySelector('.temperature');
            tempElement.addEventListener('click', (e) => {
                e.stopPropagation();
                const currentTemp = parseInt(tempElement.textContent);
                
                if (isCelsius) {
                    const fahrenheit = Math.round(currentTemp * 9/5 + 32);
                    tempElement.textContent = fahrenheit + '°F';
                } else {
                    const celsius = Math.round((currentTemp - 32) * 5/9);
                    tempElement.textContent = celsius + '°';
                }
            });
        });

        // 添加自动轮播功能(可选)
        let autoPlayInterval;
        let isAutoPlaying = false;

        function startAutoPlay() {
            if (!isAutoPlaying) {
                isAutoPlaying = true;
                autoPlayInterval = setInterval(() => {
                    currentCard = (currentCard + 1) % cards.length;
                    cards[currentCard].scrollIntoView({ behavior: 'smooth', block: 'center' });
                    
                    // 添加聚焦效果
                    cards.forEach(c => c.style.opacity = '0.7');
                    cards[currentCard].style.opacity = '1';
                }, 3000);
            }
        }

        function stopAutoPlay() {
            if (isAutoPlaying) {
                isAutoPlaying = false;
                clearInterval(autoPlayInterval);
                cards.forEach(c => c.style.opacity = '1');
            }
        }

        // 添加刷新动画
        function refreshWeather(card) {
            const icon = card.querySelector('.weather-icon');
            icon.style.animation = 'none';
            
            setTimeout(() => {
                icon.style.animation = 'rotate 1s ease-in-out';
            }, 10);
            
            // 模拟数据更新
            setTimeout(() => {
                const temp = card.querySelector('.temperature');
                const currentTemp = parseInt(temp.textContent);
                const newTemp = currentTemp + Math.floor(Math.random() * 5 - 2);
                temp.textContent = newTemp + '°';
                
                // 添加更新提示
                const updateNotice = document.createElement('div');
                updateNotice.textContent = '已更新';
                updateNotice.style.position = 'absolute';
                updateNotice.style.top = '10px';
                updateNotice.style.right = '10px';
                updateNotice.style.background = 'rgba(76, 217, 100, 0.9)';
                updateNotice.style.color = 'white';
                updateNotice.style.padding = '5px 10px';
                updateNotice.style.borderRadius = '15px';
                updateNotice.style.fontSize = '12px';
                updateNotice.style.animation = 'fadeInOut 2s ease-in-out';
                
                card.appendChild(updateNotice);
                
                setTimeout(() => {
                    updateNotice.remove();
                }, 2000);
            }, 1000);
        }

        // 为每个卡片添加下拉刷新手势
        cards.forEach(card => {
            let startY = 0;
            let isPulling = false;
            
            card.addEventListener('touchstart', (e) => {
                startY = e.touches[0].clientY;
                isPulling = true;
            });
            
            card.addEventListener('touchmove', (e) => {
                if (!isPulling) return;
                
                const currentY = e.touches[0].clientY;
                const pullDistance = currentY - startY;
                
                if (pullDistance > 0 && pullDistance < 100) {
                    card.style.transform = `translateY(${pullDistance * 0.5}px)`;
                }
            });
            
            card.addEventListener('touchend', (e) => {
                if (!isPulling) return;
                
                const endY = e.changedTouches[0].clientY;
                const pullDistance = endY - startY;
                
                if (pullDistance > 80) {
                    refreshWeather(card);
                }
                
                card.style.transform = 'translateY(0)';
                isPulling = false;
            });
        });

        // 添加淡入淡出动画样式
        const fadeStyle = document.createElement('style');
        fadeStyle.textContent = `
            @keyframes fadeInOut {
                0% { opacity: 0; transform: translateY(-10px); }
                20% { opacity: 1; transform: translateY(0); }
                80% { opacity: 1; transform: translateY(0); }
                100% { opacity: 0; transform: translateY(-10px); }
            }
        `;
        document.head.appendChild(fadeStyle);

        // 初始化时间更新
        updateTime();
        setInterval(updateTime, 60000); // 每分钟更新一次

        // 添加页面可见性变化监听
        document.addEventListener('visibilitychange', () => {
            if (document.hidden) {
                stopAutoPlay();
            }
        });

        // 性能优化:使用 Intersection Observer 来优化动画
        const observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    entry.target.style.animationPlayState = 'running';
                } else {
                    entry.target.style.animationPlayState = 'paused';
                }
            });
        }, { threshold: 0.1 });

        // 观察所有动画元素
        document.querySelectorAll('.weather-icon, .rain, .snowflake, .wind-line').forEach(el => {
            observer.observe(el);
        });

        console.log('iOS 18 风格天气卡片已加载完成!');
        console.log('操作提示:');
        console.log('- 单击卡片:脉冲动画');
        console.log('- 双击卡片:展开/收起详情');
        console.log('- 点击温度:切换摄氏度/华氏度');
        console.log('- 方向键:切换卡片');
        console.log('- ESC键:退出展开模式');
        console.log('- 鼠标移动:视差效果');
    </script>
</body>
</html>


        
编辑器加载中
预览
控制台