1. 项目概述:为什么需要桌面天气应用
早上起床第一件事,你会不会习惯性摸手机看天气?我发现自己每天至少要解锁手机十几次查看温度变化,这种高频操作实在影响工作效率。于是决定开发一款常驻桌面的天气应用,让气象信息像时钟一样随时可见。
现代人获取天气的方式主要有三种:手机自带应用、网页搜索和智能音箱查询。但这几种方式都存在明显痛点:手机需要主动解锁操作,网页查询步骤繁琐,语音交互在办公场景又不适用。桌面端天气应用恰好填补了这个空白——它像电子台历一样常驻屏幕角落,零交互获取信息,特别适合程序员、文字工作者等需要保持专注的群体。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 跨平台方案对比
开发桌面应用首先面临框架选择问题。经过实测对比三个主流方案:
- Electron:使用HTML/CSS/JS构建,适合Web开发者。但内存占用高(基础约120MB),打包体积大
- Tauri:Rust构建的轻量替代品,打包后仅10MB左右,但中文资料较少
- PyQt:Python生态成熟,但界面美化需要额外工作量
最终选择Electron,因为:
- 可直接复用网页天气API调用逻辑
- 社区有现成的窗口置顶、透明背景等插件
- 调试方便(Chrome开发者工具)
2.2 核心功能模块设计
应用架构分为四个层次:
code复制┌─────────────────┐
│ 用户界面层 │ <- 透明窗口/温度曲线图
├─────────────────┤
│ 业务逻辑层 │ <- 数据缓存/单位转换
├─────────────────┤
│ 数据服务层 │ <- API请求/数据解析
├─────────────────┤
│ 系统适配层 │ <- 开机启动/通知中心
└─────────────────┘
3. 关键实现细节
3.1 天气数据获取方案
测试了三个免费API源:
- 和风天气:每小时1000次免费调用,返回数据最规范
- OpenWeatherMap:需要信用卡验证,免费版有延迟
- 高德地图API:国内访问快,但需要企业认证
推荐使用和风天气的开发者免费套餐,注册后获取API Key即可调用。核心请求示例:
javascript复制const API_KEY = 'your_key';
const url = `https://devapi.qweather.com/v7/weather/now?location=101010100&key=${API_KEY}`;
fetch(url)
.then(res => res.json())
.then(data => {
console.log(data.now.temp); // 当前温度
console.log(data.now.text); // 天气状况
});
3.2 透明窗口实现技巧
使用electron-transparent-window插件创建无边框窗口:
javascript复制const { BrowserWindow } = require('electron-transparent-window');
let win = new BrowserWindow({
width: 300,
height: 200,
transparent: true,
frame: false,
alwaysOnTop: true
});
关键参数说明:
transparent: true启用透明背景frame: false隐藏标题栏alwaysOnTop: true窗口置顶
注意:在Windows系统需要额外设置
win.setIgnoreMouseEvents(true)让点击穿透窗口
3.3 数据缓存策略
为避免频繁调用API,采用三级缓存:
- 内存缓存:最近一次数据(有效期10分钟)
- localStorage:最近三天的历史数据
- 磁盘备份:每周天气数据归档
缓存更新逻辑流程图:
code复制[用户打开应用] →
检查内存缓存 →
有有效数据? → 是 → 直接显示
否 →
检查本地存储 →
有今日数据? → 是 → 更新内存缓存
否 →
调用API获取新数据
4. 界面优化实战
4.1 温度可视化方案
抛弃传统的数字显示,改用温度曲线图。使用Chart.js实现24小时温度变化可视化:
javascript复制const ctx = document.getElementById('tempChart').getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: ['0h', '3h', '6h', '9h', '12h', '15h', '18h', '21h'],
datasets: [{
data: [22, 20, 19, 23, 27, 26, 24, 22],
borderColor: 'rgba(255, 99, 132, 0.8)',
tension: 0.3
}]
},
options: {
plugins: { legend: { display: false } }
}
});
4.2 天气图标动画
根据天气状况添加动态效果:
- 晴天:闪烁的太阳光晕
- 雨天:下落的水滴动画
- 雪天:飘落的雪花
使用CSS关键帧实现雨滴动画:
css复制.raindrop {
animation: fall 1s linear infinite;
}
@keyframes fall {
from { transform: translateY(-10px); opacity: 0; }
to { transform: translateY(30px); opacity: 0.6; }
}
5. 系统集成技巧
5.1 开机自启动配置
不同平台的自启动实现方式:
Windows:
javascript复制const { app } = require('electron');
const path = require('path');
const { exec } = require('child_process');
const exePath = path.join(process.env.APPDATA, 'Microsoft\\Windows\\Start Menu\\Programs\\Startup\\weather.lnk');
exec(`wscript.exe "${path.join(__dirname, 'createShortcut.vbs')}" "${exePath}" "${process.execPath}"`);
macOS:
javascript复制app.setLoginItemSettings({
openAtLogin: true,
path: process.execPath
});
5.2 通知提醒功能
当天气突变时发送系统通知:
javascript复制const { Notification } = require('electron');
function showAlert(title, body) {
new Notification({
title: title,
body: body,
silent: false
}).show();
}
// 检测温度骤降
if (prevTemp - currentTemp > 5) {
showAlert('温度预警', `气温下降${prevTemp - currentTemp}°C,注意添衣`);
}
6. 打包与分发
6.1 多平台打包配置
使用electron-builder配置打包参数(package.json片段):
json复制"build": {
"appId": "com.example.weather",
"win": {
"target": "nsis",
"icon": "build/icon.ico"
},
"mac": {
"target": "dmg",
"category": "public.app-category.weather"
},
"linux": {
"target": "AppImage"
}
}
6.2 体积优化技巧
通过以下方式将安装包从180MB缩减到80MB:
- 使用
electron-packager的prune选项移除devDependencies - 压缩图片资源(PNG→WebP)
- 排除未使用的语言包
- 启用electron的ASAR归档
7. 常见问题排查
7.1 API请求失败处理
典型错误及解决方案:
| 错误码 | 原因 | 解决方法 |
|---|---|---|
| 401 | Key无效 | 检查API控制台配额 |
| 404 | 城市ID错误 | 使用/v2/city/lookup接口验证 |
| 429 | 请求频繁 | 增加缓存时间,限制刷新频率 |
7.2 透明窗口渲染异常
Windows平台常见问题:
- 黑边问题:检查CSS中是否设置了
background-color: transparent - 点击穿透失效:确保调用
setIgnoreMouseEvents后未其他元素拦截事件 - 文字模糊:启用
win.setContentProtection(true)防止DWM缩放
8. 扩展功能建议
- 空气质量预警:接入PM2.5数据,当污染严重时变红色边框
- 穿衣建议:根据温度湿度给出搭配推荐(需要机器学习模型)
- 多城市切换:使用IP定位自动切换所在地
- 历史数据对比:展示去年同期温度变化
我在实际开发中发现,窗口拖拽体验对用户非常重要。最终采用这样的实现方案:
javascript复制let dragging = false;
let offsetX, offsetY;
window.addEventListener('mousedown', (e) => {
dragging = true;
offsetX = e.clientX;
offsetY = e.clientY;
});
window.addEventListener('mousemove', (e) => {
if (dragging) {
const { screenX, screenY } = e;
win.setPosition(screenX - offsetX, screenY - offsetY);
}
});
window.addEventListener('mouseup', () => {
dragging = false;
});
