HTML&CSS&JS:基于定位的实时天气卡片

这个 HTML 页面实现了一个基于用户地理位置的实时天气信息卡片 ,界面美观、交互流畅。页面加载自动定位→请求天气数据→渲染卡片,支持手动刷新,全流程有加载 / 错误状态提示,动效过渡自然。赶快收藏学习吧。


大家复制代码时,可能会因格式转换出现错乱,导致样式失效。建议先少量复制代码进行测试,若未能解决问题,私信回复源码两字,我会发送完整的压缩包给你。

演示效果

HTML&CSS

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>实时天气卡片</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            padding: 20px;
        }

        .weather-card {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 20px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
            padding: 24px;
            max-width: 280px;
            width: 100%;
            backdrop-filter: blur(10px);
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }

        .weather-card:hover {
            transform: translateY(-3px);
            box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
        }

        .location {
            text-align: center;
            margin-bottom: 16px;
        }

        .location-icon {
            font-size: 24px;
            margin-bottom: 6px;
        }

        .city-name {
            font-size: 20px;
            font-weight: 700;
            color: #333;
            margin-bottom: 4px;
        }

        .current-time {
            font-size: 12px;
            color: #888;
            font-weight: 500;
        }

        .weather-main {
            text-align: center;
            margin: 20px 0;
        }

        .weather-icon {
            font-size: 56px;
            margin-bottom: 8px;
        }

        .temperature {
            font-size: 42px;
            font-weight: 300;
            color: #333;
            line-height: 1;
        }

        .temperature span {
            font-size: 22px;
            vertical-align: top;
        }

        .weather-description {
            font-size: 16px;
            color: #666;
            margin-top: 6px;
            text-transform: capitalize;
            margin-right: 20px;
        }

        .weather-details {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 12px;
            margin-top: 20px;
            padding-top: 16px;
            border-top: 1px solid #f0f0f0;
        }

        .detail-item {
            text-align: center;
            padding: 8px 6px;
            background: #f8f9fa;
            border-radius: 10px;
            transition: background 0.3s ease;
        }

        .detail-item:hover {
            background: #e9ecef;
        }

        .detail-icon {
            font-size: 18px;
            margin-bottom: 4px;
        }

        .detail-value {
            font-size: 14px;
            font-weight: 600;
            color: #333;
            margin-bottom: 2px;
        }

        .detail-label {
            font-size: 10px;
            color: #999;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .loading {
            text-align: center;
            color: #666;
            font-size: 14px;
        }

        .error {
            background: #fee;
            color: #c33;
            padding: 12px;
            border-radius: 10px;
            text-align: center;
            margin-top: 16px;
            font-size: 14px;
        }

        .refresh-btn {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 10px 24px;
            border-radius: 20px;
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            margin-top: 16px;
            width: 100%;
            transition: transform 0.2s ease, box-shadow 0.2s ease;
        }

        .refresh-btn:hover {
            transform: scale(1.02);
            box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
        }

        .refresh-btn:active {
            transform: scale(0.98);
        }
    </style>
</head>
<body>
    <div class="weather-card" id="weatherCard">
        <div class="location">
            <div class="location-icon">📍</div>
            <div class="city-name" id="cityName">获取位置中...</div>
            <div class="current-time" id="currentTime">--:--:--</div>
        </div>

        <div id="weatherContent">
            <div class="loading">正在获取天气信息...</div>
        </div>
    </div>

    <script>
        // 天气图标映射
        const weatherIcons = {
            '01d': '☀️', '01n': '🌙',
            '02d': '⛅', '02n': '☁️',
            '03d': '☁️', '03n': '☁️',
            '04d': '☁️', '04n': '☁️',
            '09d': '🌧️', '09n': '🌧️',
            '10d': '🌦️', '10n': '🌧️',
            '11d': '⛈️', '11n': '⛈️',
            '13d': '❄️', '13n': '❄️',
            '50d': '🌫️', '50n': '🌫️'
        };

        // 更新时间
        function updateTime() {
            const now = new Date();
            const options = {
                year: 'numeric',
                month: 'long',
                day: 'numeric',
                weekday: 'long',
                hour: '2-digit',
                minute: '2-digit',
                second: '2-digit',
                hour12: false
            };
            document.getElementById('currentTime').textContent = now.toLocaleDateString('zh-CN', options);
        }

        // 通过经纬度获取中文城市名称
        async function getChineseCityName(latitude, longitude) {
            try {
                // 使用免费的反向地理编码 API
                const response = await fetch(
                    `https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${latitude}&longitude=${longitude}&localityLanguage=zh`
                );
                const data = await response.json();

                // 优先使用市级名称,如果没有则使用区级名称
                if (data.city) {
                    return data.city;
                } else if (data.locality) {
                    return data.locality;
                } else if (data.principalSubdivision) {
                    return data.principalSubdivision;
                }
                return null;
            } catch (error) {
                console.error('获取中文城市名称失败:', error);
                return null;
            }
        }

        // 获取位置和天气
        async function getWeather() {
            const weatherContent = document.getElementById('weatherContent');
            const cityName = document.getElementById('cityName');

            if (!navigator.geolocation) {
                weatherContent.innerHTML = '<div class="error">您的浏览器不支持地理位置定位</div>';
                return;
            }

            try {
                // 获取地理位置
                const position = await new Promise((resolve, reject) => {
                    navigator.geolocation.getCurrentPosition(resolve, reject, {
                        enableHighAccuracy: true,
                        timeout: 10000,
                        maximumAge: 0
                    });
                });

                const { latitude, longitude } = position.coords;

                // 获取中文城市名称
                const chineseCity = await getChineseCityName(latitude, longitude);
                if (chineseCity) {
                    cityName.textContent = chineseCity;
                } else {
                    cityName.textContent = '获取位置中...';
                }

                // 调用 OpenWeatherMap API(使用免费的 API key,实际使用时需要替换)
                const apiKey = '4d8fb5b93d4af21d66a2948710284366'; // 这是一个公开的 demo key
                const response = await fetch(
                    `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apiKey}&units=metric&lang=zh_cn`
                );

                if (!response.ok) {
                    throw new Error('获取天气信息失败');
                }

                const data = await response.json();

                // 显示天气信息(传入中文城市名称)
                displayWeather(data, chineseCity);

            } catch (error) {
                console.error('Error:', error);
                weatherContent.innerHTML = `
                    <div class="error">
                        ${error.message || '无法获取天气信息,请检查网络连接'}
                    </div>
                    <button class="refresh-btn" onclick="getWeather()">重试</button>
                `;
            }
        }

        // 显示天气信息
        function displayWeather(data, chineseCityName) {
            const weatherContent = document.getElementById('weatherContent');
            const cityName = document.getElementById('cityName');

            // 更新城市名称(优先使用中文城市名称)
            if (chineseCityName) {
                cityName.textContent = chineseCityName;
            } else if (data.name) {
                cityName.textContent = data.name;
            } else {
                cityName.textContent = '当前位置';
            }

            const iconCode = data.weather[0].icon;
            const icon = weatherIcons[iconCode] || '🌤️';

            weatherContent.innerHTML = `
                <div class="weather-main">
                    <div class="weather-icon">${icon}</div>
                    <div class="temperature">
                        ${Math.round(data.main.temp)}<span>°C</span>
                    </div>
                    <div class="weather-description">
                        ${data.weather[0].description || '未知天气'}
                    </div>
                </div>

                <div class="weather-details">
                    <div class="detail-item">
                        <div class="detail-icon">💧</div>
                        <div class="detail-value">${data.main.humidity}%</div>
                        <div class="detail-label">湿度</div>
                    </div>
                    <div class="detail-item">
                        <div class="detail-icon">💨</div>
                        <div class="detail-value">${Math.round(data.wind.speed)} m/s</div>
                        <div class="detail-label">风速</div>
                    </div>
                    <div class="detail-item">
                        <div class="detail-icon">🌡️</div>
                        <div class="detail-value">${Math.round(data.main.feels_like)}°C</div>
                        <div class="detail-label">体感</div>
                    </div>
                </div>

                <button class="refresh-btn" onclick="getWeather()">🔄 刷新天气</button>
            `;
        }

        // 初始化
        document.addEventListener('DOMContentLoaded', () => {
            updateTime();
            setInterval(updateTime, 1000); // 每秒更新时间
            getWeather(); // 获取天气
        });
    </script>
</body>
</html>

HTML

  • div weather-card weatherCard:容器标签。天气卡片核心容器。毛玻璃效果 + 圆角 + 阴影,hover 时有上浮动效
  • div location:容器标签。位置 / 时间展示区。包含定位图标、城市名称、当前时间
  • div weatherContent:容器标签。天气内容动态展示区 初始显示「加载中」,后续通过 JS 替换为天气数据
  • div loading:容器标签。加载状态提示 初始显示「正在获取天气信息...」
  • div error:容器标签。错误提示容器 定位 / 接口失败时显示错误信息
  • button refresh-btn:按钮标签。刷新天气按钮。点击触发重新获取天气数据,有 hover/active 动效
  • script:脚本标签。内嵌 JavaScript。实现定位、接口请求、数据渲染、时间更新等动态逻辑

CSS

1. 全局重置与基础布局

css 复制代码
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box; /* 统一盒模型,避免 padding 撑大元素 */
}
body {
  min-height: 100vh; /* 占满屏幕高度 */
  display: flex;
  justify-content: center;
  align-items: center; /* 卡片垂直水平居中 */
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); /* 渐变背景 */
  font-family: 'Segoe UI', sans-serif; /* 现代无衬线字体 */
  padding: 20px; /* 移动端留白,避免卡片贴边 */
}

核心:全局重置默认边距,弹性布局实现卡片居中,渐变背景提升视觉质感。

2. 天气卡片核心样式

css 复制代码
.weather-card {
  background: rgba(255, 255, 255, 0.95); /* 半透明白色 */
  border-radius: 20px; /* 大圆角提升现代感 */
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); /* 柔和阴影 */
  padding: 24px;
  max-width: 280px; /* 固定最大宽度,适配移动端 */
  width: 100%;
  backdrop-filter: blur(10px); /* 毛玻璃核心效果 */
  transition: transform 0.3s ease, box-shadow 0.3s ease; /* 过渡动效 */
}
.weather-card:hover {
  transform: translateY(-3px); /* 鼠标悬浮上浮 */
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); /* 阴影放大 */
}

核心:backdrop-filter: blur(10px) 实现毛玻璃效果,hover 上浮 + 阴影增强交互反馈,固定最大宽度适配移动端。

3. 天气数据布局样式

css 复制代码
/* 天气主信息区(温度/图标/描述) */
.weather-main {
  text-align: center;
  margin: 20px 0;
}
.temperature {
  font-size: 42px;
  font-weight: 300; /* 轻量级字体,更现代 */
  line-height: 1; /* 消除行高冗余 */
}
.temperature span {
  font-size: 22px;
  vertical-align: top; /* 摄氏度符号上对齐 */
}

/* 天气详情网格(湿度/风速/体感) */
.weather-details {
  display: grid;
  grid-template-columns: repeat(3, 1fr); /* 三等分网格 */
  gap: 12px;
  border-top: 1px solid #f0f0f0; /* 分隔线 */
  padding-top: 16px;
}
.detail-item {
  text-align: center;
  background: #f8f9fa; /* 浅灰背景 */
  border-radius: 10px;
  padding: 8px 6px;
  transition: background 0.3s ease;
}
.detail-item:hover {
  background: #e9ecef; /* hover 加深背景 */
}

核心:网格布局实现「湿度 / 风速 / 体感」三等分展示,轻量级字体 + 对齐优化提升温度显示的视觉层次,细节项 hover 背景变化增强交互。

4. 按钮与状态样式

css 复制代码
/* 刷新按钮 */
.refresh-btn {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); /* 与页面背景呼应 */
  color: white;
  border: none;
  padding: 10px 24px;
  border-radius: 20px; /* 胶囊按钮 */
  width: 100%; /* 全屏宽按钮,适配移动端点击 */
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.refresh-btn:hover {
  transform: scale(1.02); /* 轻微放大 */
  box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4); /* 发光阴影 */
}
.refresh-btn:active {
  transform: scale(0.98); /* 点击下压 */
}

/* 加载/错误状态 */
.loading {
  text-align: center;
  color: #666;
}
.error {
  background: #fee; /* 浅红背景 */
  color: #c33; /* 深红文字 */
  padding: 12px;
  border-radius: 10px;
  text-align: center;
}

核心:按钮渐变背景与页面呼应,hover 放大 + 发光阴影,active 下压模拟物理按钮反馈;错误状态用红系配色提示,加载状态居中显示,视觉层级清晰。


各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!

相关推荐
H0483 小时前
symbol为什么说是为了解决全局变量冲突的问题
javascript
Always_Passion4 小时前
FE视角下的Referrer全面解析
javascript·面试
七牛云行业应用4 小时前
大模型接入踩坑录:被 Unexpected end of JSON 折磨三天,我重写了SSE流解析
javascript·人工智能·代码规范
程序员阿耶4 小时前
5 个让 CSS 起飞的新特性,设计师看了直呼内行
css
_AaronWong4 小时前
Vue3+Element Plus 通用表格组件封装与使用实践
前端·javascript·vue.js
代码煮茶4 小时前
JS 异步编程实战 | 从回调地狱到 Promise/Async/Await(附代码 + 面试题)
javascript·面试
前端Hardy5 小时前
HTML&CSS:纯CSS实现随机转盘抽奖机——无JS,全靠现代CSS黑科技!
css·html
全栈老石5 小时前
手写一个无限画布 #3:如何在Canvas 层上建立事件体系
前端·javascript·canvas
csdn飘逸飘逸5 小时前
Autojs基础-device(设备)
javascript