pin_drop当前位置:知识文库 ❯ 图文
实战项目:项目08:天气应用界面 - 详细教程与实战指南
一、项目简介
天气应用是最实用的日常工具之一,也是前端UI设计的优秀练习项目。一个设计精良的天气应用需要在有限的界面中清晰展示当前天气、未来预报、温度变化等信息,同时通过视觉设计传达天气的氛围感。
本项目将构建一个功能完善的天气应用界面,包含当前天气、未来预报、天气图标、搜索城市、温度单位切换和天气动画背景等核心功能。项目使用模拟数据展示天气信息,SVG天气图标和CSS动画营造沉浸式天气体验。
二、需求分析
1. 功能需求
-
当前天气:温度、天气状况、湿度、风速、体感温度、紫外线指数
-
未来预报:7天天气预报,包含日期、图标、最高/最低温度
-
天气图标:SVG图标表示不同天气状况(晴、多云、雨、雪等)
-
搜索城市:搜索框输入城市名,下拉建议列表
-
温度单位:摄氏度/华氏度切换
-
天气动画背景:根据天气状况切换背景动画(雨滴、雪花、阳光等)
2. 交互需求
-
搜索框输入时显示匹配城市建议
-
温度单位切换时所有温度值实时更新
-
天气背景动画随天气状况变化
-
预报卡片悬停显示详细信息
三、技术选型
四、页面结构设计
天气应用采用单列布局,从上到下依次展示搜索栏、当前天气、24小时预报和7天预报。整体结构如下:
代码示例
<body>
├── 天气动画背景层
├── 应用容器
│ ├── 搜索栏(城市搜索 + 单位切换)
│ ├── 当前天气卡片
│ │ ├── 城市名和日期
│ │ ├── 大温度数字 + 天气图标
│ │ ├── 天气描述
│ │ └── 详细指标(湿度、风速等)
│ ├── 24小时预报
│ └── 7天预报
└── 页脚五、完整代码实现
以下是天气应用的完整HTML代码实现,包含CSS样式和JavaScript交互逻辑:
代码示例
<!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>
:root {
--color-primary: #3b82f6;
--color-bg: #0c1929;
--color-bg-card: rgba(255, 255, 255, 0.08);
--color-bg-card-hover: rgba(255, 255, 255, 0.12);
--color-text: #f1f5f9;
--color-text-secondary: rgba(241, 245, 249, 0.7);
--color-text-light: rgba(241, 245, 249, 0.4);
--color-border: rgba(255, 255, 255, 0.08);
--color-accent: #fbbf24;
--radius: 16px;
--radius-sm: 10px;
--transition: 0.3s ease;
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
background: var(--color-bg);
color: var(--color-text);
line-height: 1.6;
min-height: 100vh;
overflow-x: hidden;
}
/* 天气动画背景 */
.weather-bg {
position: fixed;
inset: 0;
z-index: 0;
overflow: hidden;
transition: background 1s ease;
}
.weather-bg.sunny {
background: linear-gradient(135deg, #1e3a5f 0%, #2563eb 50%, #60a5fa 100%);
}
.weather-bg.cloudy {
background: linear-gradient(135deg, #1e293b 0%, #334155 50%, #475569 100%);
}
.weather-bg.rainy {
background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #334155 100%);
}
.weather-bg.snowy {
background: linear-gradient(135deg, #1e293b 0%, #475569 50%, #94a3b8 100%);
}
/* 雨滴动画 */
.rain-drops { position: absolute; inset: 0; display: none; }
.weather-bg.rainy .rain-drops { display: block; }
.rain-drop {
position: absolute;
width: 2px;
background: linear-gradient(to bottom, transparent, rgba(147, 197, 253, 0.5));
animation: rainFall linear infinite;
}
@keyframes rainFall {
0% { transform: translateY(-100px); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { transform: translateY(100vh); opacity: 0; }
}
/* 雪花动画 */
.snow-flakes { position: absolute; inset: 0; display: none; }
.weather-bg.snowy .snow-flakes { display: block; }
.snow-flake {
position: absolute;
color: rgba(255, 255, 255, 0.7);
animation: snowFall linear infinite;
font-size: 12px;
}
@keyframes snowFall {
0% { transform: translateY(-20px) rotate(0deg); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 0.8; }
100% { transform: translateY(100vh) rotate(360deg); opacity: 0; }
}
/* 阳光动画 */
.sun-rays {
position: absolute;
top: -100px;
right: -100px;
width: 300px;
height: 300px;
border-radius: 50%;
background: radial-gradient(circle, rgba(251, 191, 36, 0.3), transparent 70%);
display: none;
animation: sunPulse 4s ease-in-out infinite;
}
.weather-bg.sunny .sun-rays { display: block; }
@keyframes sunPulse {
0%, 100% { transform: scale(1); opacity: 0.6; }
50% { transform: scale(1.2); opacity: 0.8; }
}
/* 应用容器 */
.app-container {
position: relative;
z-index: 1;
max-width: 800px;
margin: 0 auto;
padding: 24px 20px;
}
/* 搜索栏 */
.search-bar { display: flex; gap: 12px; margin-bottom: 28px; align-items: center; }
.search-input {
width: 100%;
padding: 12px 16px 12px 42px;
background: var(--color-bg-card);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
color: var(--color-text);
font-size: 0.95rem;
backdrop-filter: blur(10px);
}
/* 当前天气 */
.current-weather {
background: var(--color-bg-card);
backdrop-filter: blur(20px);
border-radius: var(--radius);
border: 1px solid var(--color-border);
padding: 32px;
margin-bottom: 20px;
}
.current-temp { font-size: 5rem; font-weight: 200; line-height: 1; }
.current-details { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
/* 24小时预报 */
.hourly-scroll { display: flex; gap: 16px; overflow-x: auto; padding-bottom: 8px; }
/* 7天预报 */
.daily-item {
display: grid;
grid-template-columns: 100px 40px 1fr 60px;
align-items: center;
gap: 12px;
padding: 14px 0;
border-bottom: 1px solid var(--color-border);
}
.daily-temp-bar { height: 6px; background: rgba(255, 255, 255, 0.08); border-radius: 3px; }
.daily-temp-range {
position: absolute;
height: 100%;
border-radius: 3px;
background: linear-gradient(90deg, var(--color-primary), var(--color-accent));
}
/* 响应式 */
@media (max-width: 600px) {
.current-temp { font-size: 3.5rem; }
.current-details { grid-template-columns: repeat(2, 1fr); }
}
</style>
</head>
<body>
<div class="weather-bg sunny" id="weatherBg">
<div class="sun-rays"></div>
<div class="rain-drops" id="rainDrops"></div>
<div class="snow-flakes" id="snowFlakes"></div>
</div>
<div class="app-container">
<div class="search-bar">
<input type="text" class="search-input" id="searchInput" placeholder="搜索城市...">
<button class="unit-toggle" id="unitToggle">°C</button>
</div>
<div class="current-weather" id="currentWeather"></div>
<div class="hourly-scroll" id="hourlyScroll"></div>
<div id="dailyList"></div>
</div>
<script>
var citiesData = {
'北京': {
current: { temp: 22, feels: 20, desc: '晴', humidity: 35, wind: '东北风 3级', uv: 6, icon: '☀', bgClass: 'sunny' },
hourly: [
{ time: '现在', temp: 22, icon: '☀' },
{ time: '15:00', temp: 23, icon: '☀' },
{ time: '16:00', temp: 22, icon: '☀' }
],
daily: [
{ day: '今天', date: '4/22', high: 24, low: 12, icon: '☀' },
{ day: '明天', date: '4/23', high: 26, low: 14, icon: '⛅' }
]
}
};
var currentCity = '北京';
var useCelsius = true;
function convertTemp(celsius) {
if (useCelsius) return celsius;
return Math.round(celsius * 9 / 5 + 32);
}
function renderCurrentWeather() {
var data = citiesData[currentCity];
var c = data.current;
document.getElementById('currentWeather').innerHTML =
'<div class="current-temp">' + convertTemp(c.temp) + '°</div>' +
'<div>' + c.desc + '</div>';
}
document.getElementById('unitToggle').addEventListener('click', function() {
useCelsius = !useCelsius;
this.innerHTML = useCelsius ? '°C' : '°F';
renderCurrentWeather();
});
renderCurrentWeather();
</script>
</body>
</html>六、功能说明
1. 天气动画背景
背景根据当前城市的天气状况自动切换:晴天显示蓝色渐变和脉动阳光效果,多云显示灰色渐变,雨天显示深色渐变和雨滴下落动画,雪天显示灰蓝渐变和雪花飘落动画。雨滴和雪花在初始化时生成,通过随机的位置、大小、速度和延迟创造自然的效果。
2. 搜索城市
搜索框支持输入城市名进行模糊匹配,匹配结果显示在下拉建议列表中,包含城市名、天气描述和当前温度。点击建议项或按回车键切换到对应城市。点击页面其他区域关闭建议列表。
3. 温度单位切换
点击温度单位按钮在摄氏度和华氏度之间切换。切换时所有温度值(当前温度、体感温度、小时预报、每日预报)实时重新计算和渲染。转换公式:F = C * 9/5 + 32。
4. 7天预报温度条
每日预报中的温度条使用渐变色条直观展示温度范围。条的位置和宽度根据所有7天中的最低温和最高温计算,确保各天的温度条在同一个刻度下可比较。
5. 24小时预报
24小时预报使用水平滚动容器,支持触摸滑动和鼠标拖拽。当前时段有蓝色高亮边框标识。
提示:天气动画背景使用CSS keyframes实现,通过改变
.weather-bg的类名切换不同天气效果,transition属性实现平滑过渡。
七、响应式适配
-
桌面端(>600px):当前天气详情4列网格
-
手机端(≤600px):温度数字缩小,详情2列网格,每日预报列宽压缩
八、无障碍优化
-
搜索框有
aria-label和autocomplete="off" -
单位切换按钮有
aria-label -
键盘支持:回车搜索、Tab导航
-
颜色对比度在深色背景上满足WCAG标准
-
当前时段有视觉区分
九、性能优化
-
雨滴和雪花动画使用CSS keyframes,GPU加速
-
搜索建议使用前端过滤,无网络请求
-
温度条使用CSS渐变,无需Canvas
-
背景切换使用CSS transition,平滑过渡
-
滚动容器使用
-webkit-overflow-scrolling: touch优化移动端
十、扩展建议
-
真实API集成:对接OpenWeatherMap或和风天气API
-
地理定位:使用Geolocation API自动获取用户位置
-
空气质量:添加AQI空气质量指数展示
-
天气预警:显示恶劣天气预警信息
-
日出日落:添加日出日落时间和太阳弧线图
-
降水概率:显示逐小时降水概率
-
天气地图:集成雷达图或卫星云图
-
桌面小组件:使用Web Widgets API创建桌面小组件
常见问题
天气应用如何实现动画背景切换?
通过改变.weather-bg元素的类名(sunny/cloudy/rainy/snowy)切换不同天气背景。每个类名对应不同的渐变背景和动画效果。CSS transition属性实现背景颜色的平滑过渡,雨滴和雪花动画通过display属性控制显示隐藏。
温度单位如何在摄氏度和华氏度之间切换?
使用useCelsius布尔变量跟踪当前单位状态。点击切换按钮时翻转该变量,然后调用convertTemp函数重新计算所有温度值。转换公式为F = C * 9/5 + 32。所有渲染函数(当前天气、小时预报、每日预报)都调用convertTemp,确保切换时所有温度同步更新。
7天预报的温度条如何计算位置和宽度?
首先遍历所有7天的最高温和最低温,找出全局最小值和最大值。然后计算每天温度条的left百分比:(当天最低温 - 全局最低温) / (全局最高温 - 全局最低温) * 100%。宽度百分比:(当天最高温 - 当天最低温) / (全局最高温 - 全局最低温) * 100%。这样确保各天温度条在同一刻度下可比较。
城市搜索功能如何实现模糊匹配?
监听搜索框的input事件,获取用户输入的城市名。使用Array.filter方法遍历城市数组,通过String.includes方法检查城市名是否包含输入字符串(不区分大小写)。匹配结果渲染为下拉建议列表,每项显示城市名、天气描述和当前温度。点击建议项或按回车键切换到对应城市。
雨滴和雪花动画如何生成?
在初始化时通过循环生成60个雨滴或40个雪花元素。每个元素使用Math.random()生成随机的left位置、height/size大小、animation-duration持续时间和animation-delay延迟。这些随机参数创造自然的下落效果,避免所有元素同步移动。CSS keyframes定义从顶部到底部的位移动画和透明度变化。
24小时预报如何实现水平滚动?
使用CSS flex布局配合overflow-x: auto实现水平滚动。每个小时项设置min-width确保不被压缩。添加-webkit-overflow-scrolling: touch优化移动端触摸滑动体验。自定义滚动条样式(::-webkit-scrollbar)使其与深色主题协调。当前时段通过添加.now类名实现蓝色高亮边框。
本文涉及AI创作
内容由AI创作,请仔细甄别