PC 画布 1440×900(×0.36)、移动画布 390×844(×0.74),真实像素绘制后等比缩放;全部 CSS/SVG + 少量原生 JS 生成网格,无截图、无位图、无外链。
让用户做一件事:把文件拖进来就发。(左:码 + 翻牌器;中:大拖入区;右:最近传输)
flex:1 吃掉全部剩余高度(900 高不再留空);左栏统计用翻牌器(等宽数字 tabular-nums,翻转 360ms,位数变化不跳动);背景是 48px 网格以 26s 匀速平移,只动 transform,prefers-reduced-motion 下静止。移动端把拖入区降级为 180 高整块点击区,主操作 56 高贴底。让用户做一件事:一眼看出传到哪、还剩多久。(四态色 + 聚合 + 大号速率)
| 块数 n | 网格 | 每格代表 | 格色规则 |
|---|---|---|---|
| n ≤ 256 | 16 × 16(移动 10 × ceil(n/10)) | 1 块 | 四态直取:等待 / 传输中 / 完成 / 失败 |
| 257 ≤ n ≤ 1024 | 32 × 32(移动 10 × 10) | ceil(n / 1024) 块(n=1024 时正好 1 块/格) | 任一失败 → 失败色;全部完成 → 完成色;有在传 → 传输中色(呼吸动画);部分完成 → 按完成比例渐变填充;否则等待色 |
| n > 1024 | 固定 32 × 32(移动 10 × 10) | ceil(n / 1024) 块(如 4096 块 → 每格 4 块) | 同上;渐变填充比例 = 该格已完成块数 / 每格块数 |
ceil(n/格数) 块,格色按「任一失败→失败、全部完成→完成、有在传→传输中、其余按完成比例渐变」取;DOM 节点数恒 ≤1024,更新走 requestAnimationFrame 批量(每帧最多改 64 格),网格不承担点击(纯展示,避免误触)。让用户做一件事:单手点得到、点得准。(列表密度 + 空态填满)
| 元素 | 最小尺寸 | 实际取值 | 说明 |
|---|---|---|---|
| 图标按钮 | 44×44 | 48×48 | 顶栏锁 / 刷新 / 下载 / 举报;间隔 ≥8px |
| 主操作 | ≥48 高 | 56 高 · 整宽 | 贴底,距屏底 34px safe-area |
| 列表行 | ≥56 高 | 72 高 | 两行文字 + 右侧 48 图标,整行可点 |
| 块矩阵 | 视觉 ≥8px | 10 列 / 格 ≈ 24px | 不承担点击(纯展示,防误触) |
让用户做一件事:动作有回应,但不被动画打扰。(时长 / 缓动 / 触发 / 降级)
| 动效 | 时长 / 缓动 | 触发条件 | 降级(reduced-motion) |
|---|---|---|---|
| 统计翻牌器 | 360ms · cubic-bezier(.2,.7,.3,1) | 数值变化时(发送/接收成功后);首屏仅一次入场 | 直接显示最终数字,无翻转 |
| 拖入区悬停 / 拖入 | 180ms · ease-out | :hover / dragover(边框与底色变化,1.004 缩放) | 只保留边框变色,取消缩放 |
| 文件落点 | 420ms · cubic-bezier(.2,.8,.2,1) | 文件被放下 / 选中 | 无位移,直接出现 |
| 进度块(传输中) | 1100ms · ease-in-out 循环 | 该格状态为「传输中」 | 静态实色,不呼吸 |
| 空态插画 | 入场 420ms,之后静止 | 首次进入空态 | 无入场,直接显示 |
| 背景网格 | 26s 线性循环 | 仅 PC 首屏;移动端关闭 | 完全静止(不绘制动画层) |
blink 类高频闪烁,频率上限 ≤3 次/秒且不用于吸引注意);② 不自动循环大动画(整屏位移、整卡缩放、自动轮播一律禁止);③ 数字不做无限跳动(只有真实数值变化才翻);④ 滚动不劫持(不做视差/吸顶动画);⑤ 进度条不做流光扫过。
requestAnimationFrame 批量,单帧最多改 64 个格(1024 格全量刷新分 16 帧完成);transform / opacity,不改 width/height/top/left(避免重排);