拍照打卡小程序
【摘要】 用纯前端实现一个拍照打卡小工具:从想法到上架华为云开发者作品展览馆> 一台浏览器摄像头,一个 localStorage,852 行代码,零依赖。这篇文章记录了「拍照打卡小工具」从构思到开发、再到发布至华为云开发者作品展览馆的全过程。## 一、为什么做这个工具打卡这件事,大家都不陌生。健身打卡、学习打卡、早起打卡……市面上的打卡 App 很多,但大多数都需要注册账号、下载安装、还要忍受各种广...
用纯前端实现一个拍照打卡小工具:从想法到上架华为云开发者作品展览馆
> 一台浏览器摄像头,一个 localStorage,852 行代码,零依赖。这篇文章记录了「拍照打卡小工具」从构思到开发、再到发布至华为云开发者作品展览馆的全过程。
## 一、为什么做这个工具
打卡这件事,大家都不陌生。健身打卡、学习打卡、早起打卡……市面上的打卡 App 很多,但大多数都需要注册账号、下载安装、还要忍受各种广告。有时候你只是想随手拍张照片,记一笔备注,留个时间戳,就这么简单。
于是我想:能不能做一个**零安装、零注册、打开即用**的拍照打卡工具?答案是肯定的——浏览器原生就提供了摄像头 API,localStorage 可以做本地持久化,不需要任何后端服务。
## 二、功能设计
在动手之前,我先梳理了核心功能:
| 功能 | 说明 |
|------|------|
| **拍照打卡** | 调用浏览器摄像头,拍照截图,支持前后摄像头切换 |
| **备注记录** | 每次拍照后可添加文字备注,自动记录时间戳 |
| **历史记录** | 展示所有打卡记录,含照片缩略图、时间、备注,按时间倒序 |
| **数据持久化** | 使用 localStorage 保存,照片以 Base64 存储,刷新不丢失 |
| **记录管理** | 单条删除(带确认弹窗)、一键清空所有记录 |
| **统计信息** | 总打卡次数 + 今日打卡次数 |
界面设计上,我选择了**移动端优先**的思路:
- 底部 Tab 导航:「打卡」和「记录」两个页面
- 打卡页:摄像头预览区域 + 大圆形拍照按钮 + 备注输入框
- 记录页:统计卡片 + 打卡历史列表
- 配色用清新的**蓝绿色系**(teal/cyan),卡片式布局
## 三、技术实现
### 3.1 整体架构
应用采用三层架构,简洁清晰:
```
┌─────────────────────────────────────────┐
│ 用户界面层 (UI Layer) │
│ 打卡页面 | 记录页面 | 底部Tab导航 │
├─────────────────────────────────────────┤
│ 功能逻辑层 (Logic Layer) │
│ 拍照模块 | 记录管理 | 统计模块 │
├─────────────────────────────────────────┤
│ 数据存储层 (Storage Layer) │
│ localStorage | Base64 编码 │
└─────────────────────────────────────────┘
```
### 3.2 拍照功能:getUserMedia + Canvas
拍照的核心是浏览器的 `getUserMedia` API。请求摄像头权限后,将视频流绑定到 `<video>` 元素上实时预览。拍照时,把当前视频帧绘制到 `<canvas>` 上,再通过 `canvas.toDataURL()` 转为 Base64 图片数据。
```javascript
// 请求摄像头
async function startCamera(facingMode = 'environment') {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: facingMode }
});
videoElement.srcObject = stream;
}
// 拍照截图
function capturePhoto() {
const canvas = document.createElement('canvas');
canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight;
canvas.getContext('2d').drawImage(videoElement, 0, 0);
return canvas.toDataURL('image/jpeg', 0.8); // Base64 图片
}
```
**前后摄像头切换**通过 `facingMode` 参数实现:`environment` 为后摄(默认),`user` 为前摄。移动端切换时需要先停止当前视频流,再重新请求新方向的流。
### 3.3 数据持久化:localStorage + Base64
每条打卡记录的数据结构如下:
```javascript
{
id: "checkin-1725350400000", // 唯一 ID(时间戳生成)
photo: "data:image/jpeg;base64,/9j/4AAQ...", // Base64 照片
note: "今天完成了 5 公里跑步", // 备注文字
timestamp: 1725350400000 // 时间戳
}
```
所有记录以 JSON 数组形式存入 `localStorage`:
```javascript
// 保存记录
function saveRecord(record) {
const records = JSON.parse(localStorage.getItem('checkin-records') || '[]');
records.unshift(record); // 最新的排前面
localStorage.setItem('checkin-records', JSON.stringify(records));
}
// 读取记录
function loadRecords() {
return JSON.parse(localStorage.getItem('checkin-records') || '[]');
}
```
**为什么用 Base64 而不是 Blob?** 因为 localStorage 只能存储字符串。Base64 编码后照片直接嵌在 JSON 里,读写简单,无需额外管理 Blob URL。代价是存储体积增大约 33%,但对于打卡场景的小尺寸照片完全可接受。
### 3.4 统计信息
统计逻辑很轻量——遍历记录数组,对比日期即可:
```javascript
function getStats() {
const records = loadRecords();
const today = new Date().toDateString();
const todayCount = records.filter(r =>
new Date(r.timestamp).toDateString() === today
).length;
return { total: records.length, today: todayCount };
}
```
### 3.5 响应式布局
使用 CSS Flexbox + 媒体查询实现移动端优先的响应式布局:
```css
/* 移动端优先 */
.container {
display: flex;
flex-direction: column;
max-width: 480px; /* 手机宽度 */
margin: 0 auto;
}
/* 平板及以上 */
@media (min-width: 768px) {
.container {
max-width: 720px;
}
}
```
底部 Tab 导航固定在视口底部,拍照按钮居中大圆形设计,这些都是移动端的经典交互模式。
## 四、开发体验:华为云 CodeArts
这个项目是在 **华为云 AI DevSpace** 的 CodeArts 沙箱中开发的。整个开发流程非常顺畅:
1. **沙箱环境即开即用**:不需要本地配置开发环境,浏览器打开就能写代码
2. **实时预览**:代码写完后直接在沙箱里启动 HTTP 服务,通过预览链接查看效果
3. **一键部署**:开发完成后可以直接通过 DevBridge 隧道分享给外部用户访问
CodeArts 沙箱的工作目录在宿主机上直接可读写,git 操作也没有任何障碍,和本地开发体验几乎一致。
## 五、发布到开发者作品展览馆
开发完成后,我使用 **publish-work-to-gallery** 技能将作品发布到了华为云高校运营平台的开发者作品展览馆。发布流程设计得很周到:
### 5.1 身份认证:STS 自委托
发布前需要验证身份,这里用到了一个很巧妙的设计——**IAM 自委托(SELF_VERIFY)**:
- 创建一个委托方和被委托方都是自己账号的 IAM 委托
- 通过 `STS AssumeAgency` 生成最小权限临时凭证(仅允许 `GetCallerIdentity` 和 `listAuthDomains`)
- 临时凭证有效期 900 秒,过期可自动刷新
- **永久 AK/SK 始终留在本地**,只传递临时凭证,安全可靠
### 5.2 作品元数据生成
发布需要几样东西:
- **作品封面**:自动截图 + PPT 风格合成,支持多种色调方案
- **作品简介**:15-50 字的一句话描述
- **详情文章**:Markdown 格式,含架构图,打包成 zip 上传
- **Git 仓库地址**:代码需先推送到 GitCode
### 5.3 门禁检查
发布前有多道门禁,确保质量:
| 门禁 | 检查内容 |
|------|---------|
| 渲染门禁 | CJK + Emoji 字体安装、fontconfig 中文映射 |
| 编码门禁 | HTML 声明编码与实际编码一致(UTF-8) |
| 截图门禁 | 页面内字形自检 + 字体兜底注入 |
| 像素级字形终检 | 比对文字行墨量,防止渲染出豆腐块 |
这些门禁由脚本强制执行,不依赖开发者自觉,有效防止了中文乱码、字体缺失等问题混入发布。
### 5.4 最终结果
作品成功发布,获得了作品陈列馆页面和 200 成长积分:
- **作品链接**:https://gallery.developer.huaweicloud.com/gallery/002c354ad0c00000
- **代码仓库**:https://gitcode.com/gcw_O7wBDy8J/photo-checkin.git
## 六、总结与思考
### 做对了什么
- **纯前端零依赖**:单个 HTML 文件,不需要构建工具、不需要框架、不需要后端,部署成本为零
- **移动端优先**:打卡场景天然适合手机,从交互到布局都针对触摸操作优化
- **隐私安全**:所有数据存在本地浏览器,不上传任何服务器,用户完全掌控自己的数据
### 可以改进的方向
- **存储容量限制**:localStorage 通常有 5-10MB 限制,大量打卡记录可能超限。可以考虑用 IndexedDB 替代,或提供导出/导入功能
- **PWA 离线支持**:加一个 Service Worker 和 manifest,就能实现离线访问和「添加到主屏幕」
- **照片压缩**:目前直接存 JPEG Base64,可以在 Canvas 绘制时调整尺寸和质量,进一步减小存储占用
- **多设备同步**:如果需要跨设备同步,可以接入华为云 OBS 或 FunctionGraph 做后端存储
### 关于华为云开发体验
这次体验了华为云 AI DevSpace + CodeArts + GitCode + 开发者作品展览馆的完整链路,整体感受是:
- **AI DevSpace** 沙箱环境开箱即用,省去了环境配置的麻烦
- **CodeArts** 代码编写体验流畅,实时预览很方便
- **GitCode** 代码托管与 GitCode OAuth 登录工具链完整
- **作品展览馆** 发布流程设计严谨,门禁检查保证了作品质量,STS 自委托的身份认证方式兼顾了安全与便捷
如果你也想快速开发并发布一个小工具,这条链路值得试试。
---
*本文涉及的完整代码已开源至 GitCode:[photo-checkin](https://gitcode.com/gcw_O7wBDy8J/photo-checkin.git),欢迎 Star ⭐*
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)