纯前端显示天气

• 12 分钟阅读 • web

纯前端实现在网站上挂一个漂亮的天气标签。

天气来源

使用openweather提供的免费api,需注册帐号,生成api-key。
数据获取方式:
https://api.openweathermap.org/data/2.5/weather?q=beijing&appid=xxxxxxxx&units=metric&lang=zh_cn

https://api.openweathermap.org/data/2.5/weather?lat=39.91&lon=116.40&appid=xxxxxxxx&units=metric&lang=zh_cn
返回:
{"coord":{"lon":116.3972,"lat":39.9075},"weather":[{"id":800,"main":"Clear","description":"晴","icon":"01n"}],"base":"stations","main":{"temp":12.94,"feels_like":10.66,"temp_min":12.94,"temp_max":12.94,"pressure":1015,"humidity":14,"sea_level":1015,"grnd_level":1010},"visibility":10000,"wind":{"speed":2.96,"deg":297,"gust":8.78},"clouds":{"all":0},"dt":1745764328,"sys":{"type":1,"id":9609,"country":"CN","sunrise":1745702400,"sunset":1745751833},"timezone":28800,"id":1816670,"name":"Beijing","cod":200}

代码

徽章显示的地方:
<img id="weather-badge" src="">

<script>
    // 配置
    const LAT = 39.93;  //经纬度
    const LON = 119.59;
    const API_KEY = "xxxxxxxx"; // 替换为你的API密钥
    const CITY_NAME = "今日天气"; // 自定义显示名称

    async function updateWeatherBadge() {
        try {
            // 1. 获取天气数据
            const apiUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${LAT}&lon=${LON}&appid=${API_KEY}&units=metric&lang=zh_cn`;
            const response = await fetch(apiUrl);
            const data = await response.json();
            
            const temp = Math.round(data.main.temp);
            const description = data.weather[0].description;
            
            // 2. 根据温度选择颜色
            let color;
            if (temp < 0) color = "lightblue";
            else if (temp < 10) color = "blue";
            else if (temp < 20) color = "green";
            else if (temp < 30) color = "orange";
            else color = "red";
            
            // 3. 生成 Shields.io 徽章 URL
            const badgeUrl = `https://img.shields.io/badge/${encodeURIComponent(CITY_NAME)}-${temp}°C_${encodeURIComponent(description)}-${color}.svg`;
            
            // 4. 更新徽章
            document.getElementById("weather-badge").src = badgeUrl;
            
        } catch (error) {
            console.error("获取天气失败:", error);
            // 显示错误徽章
            document.getElementById("weather-badge").src = 
                "https://img.shields.io/badge/天气-加载失败-red.svg";
        }
    }

    // 初始加载 + 每30分钟更新一次
    updateWeatherBadge();
    setInterval(updateWeatherBadge, 30 * 60 * 1000); 
</script>

优化

30分钟缓存:

   <!-- 徽章容器(支持点击刷新) -->
    <a href="javascript:void(0)" onclick="forceRefreshWeather()" title="点击刷新">
        <img id="weather-badge" 
             src="https://img.shields.io/badge/天气-加载中-lightgrey.svg" 
             alt="实时天气">
    </a>

    <script>
        // ===== 配置 =====
        const CONFIG = {
            LAT: 39.93,
            LON: 119.59,
            API_KEY: "xxxxxxxxxxx",
            CACHE_KEY: "weather_cache_v2",
            CACHE_TTL: 30 * 60 * 1000, // 30分钟缓存
            RETRY_DELAY: 5000 // 失败后5秒重试
        };

        // ===== 核心函数 =====
        async function updateWeather() {
            try {
                // 1. 检查缓存是否有效
                const cachedData = getValidCache();
                if (cachedData) {
                    renderBadge(cachedData);
                    return;
                }

                // 2. 调用API获取新数据
                const apiUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${CONFIG.LAT}&lon=${CONFIG.LON}&appid=${CONFIG.API_KEY}&units=metric&lang=zh_cn`;
                const response = await fetchWithTimeout(apiUrl, { timeout: 3000 });
                
                if (!response.ok) throw new Error(`HTTP ${response.status}`);
                
                const data = await response.json();
                const processedData = processData(data);
                
                // 3. 更新缓存和界面
                saveCache(processedData);
                renderBadge(processedData);

            } catch (error) {
                console.error("[天气] 更新失败:", error);
                handleError(error);
            }
        }

        // ===== 工具函数 =====
        function processData(apiData) {
            const temp = Math.round(apiData.main.temp);
            return {
                temp,
                desc: apiData.weather[0].description,
                color: getTempColor(temp),
                timestamp: Date.now()
            };
        }

        function getTempColor(temp) {
            if (temp < 0) return "lightblue";
            if (temp < 10) return "blue";
            if (temp < 20) return "green";
            if (temp < 30) return "orange";
            return "red";
        }

        function renderBadge(data) {
            const badgeUrl = `https://img.shields.io/badge/天气-${data.temp}°C_${encodeURIComponent(data.desc)}-${data.color}.svg?cacheBuster=${Date.now()}`;
            document.getElementById("weather-badge").src = badgeUrl;
        }

        // ===== 缓存管理 =====
        function getValidCache() {
            const raw = localStorage.getItem(CONFIG.CACHE_KEY);
            if (!raw) return null;
            
            try {
                const data = JSON.parse(raw);
                const isExpired = (Date.now() - data.timestamp) > CONFIG.CACHE_TTL;
                return isExpired ? null : data;
            } catch {
                return null;
            }
        }

        function saveCache(data) {
            localStorage.setItem(CONFIG.CACHE_KEY, JSON.stringify(data));
        }

        // ===== 错误处理 =====
        function handleError(error) {
            // 尝试显示缓存(即使过期)
            const cached = getValidCache() || JSON.parse(localStorage.getItem(CONFIG.CACHE_KEY));
            if (cached) {
                renderBadge(cached);
                console.warn("[天气] 使用缓存数据");
            } else {
                document.getElementById("weather-badge").src = 
                    "https://img.shields.io/badge/天气-服务异常-red.svg";
            }
            
            // 自动重试
            setTimeout(updateWeather, CONFIG.RETRY_DELAY);
        }

        async function fetchWithTimeout(resource, { timeout = 5000 } = {}) {
            const controller = new AbortController();
            const id = setTimeout(() => controller.abort(), timeout);
            
            const response = await fetch(resource, {
                signal: controller.signal
            });
            clearTimeout(id);
            
            return response;
        }

        // ===== 用户交互 =====
        function forceRefreshWeather() {
            localStorage.removeItem(CONFIG.CACHE_KEY);
            document.getElementById("weather-badge").src = 
                "https://img.shields.io/badge/天气-刷新中-yellow.svg";
            updateWeather();
        }

        // ===== 初始化 =====
        document.addEventListener('DOMContentLoaded', () => {
            updateWeather();
            // 定时检查(实际更新取决于缓存TTL)
            setInterval(updateWeather, CONFIG.CACHE_TTL / 2); 
        });
    </script>
文章标签: web

上一篇 : alpine安装python3
下一篇 : BitPlay:Torrent流媒体网络应用程序
留言
阅读进度 0%