topfans/docs/superpowers/specs/2026-06-10-square-stargalaxy-component-design.md

277 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 星河StarGalaxy组件设计
> **日期**: 2026-06-10
> **状态**: 待评审
> **目的**: 为 square 页面的「星河」tab 设计一个 3D 倾斜椭圆轨道 + 顺时针旋转的排行榜组件
> **参考文档**: `docs/figma-analysis-xiaohongshu-stars.md`Figma 节点 94:78「星河新版本」
---
## 1. 背景
square 页面现有 3 个内容 tab星河 / 星榜 / 广场),其中「星榜」是 12 行的横向列表样式(`HotCategoryBlock.vue`。本次新增「星河」tab沿用 Figma 中的双层结构:
- **上层**TOP 1-3 颁奖台cover + 下方标签 + 钻石渐变外框)
- **下层**TOP 4-12 散落在 65° 倾斜椭圆轨道上,绕中心 3D 旋转
设计追求饭圈甜美梦幻风格(粉红渐变 + 玻璃拟态 + 暖黄光晕 + 渐变描边),不显示用户名/点赞数(保持 cover 纯净)。
---
## 2. 文件结构
```
frontend/pages/square/components/StarGalaxy/
├── index.vue ← 容器数据加载、装饰层、TOP 1-3 + ScatteredRanks 编排
├── PodiumCard.vue ← TOP 1-3 颁奖台卡片cover + 下方 TOP N 标签 + 钻石外框)
├── ScatteredRanks.vue ← TOP 4-12 9 个散落 itemcover + 上方 TOP N 标签)
└── config.js ← 9 slot 位置/translate/scale 公式
```
`square.vue` 改动:在星河 tab 分支中渲染 `<StarGalaxy @cardClick="handleCardClick" />`,复用现有 `handleCardClick`(单击跳详情 + 双击点赞)。
---
## 3. 组件职责
| 组件 | 职责 | 内部状态 | 输入 Props | 事件 |
|----|----|----|----|----|
| `StarGalaxy/index.vue` | 数据加载(`getHotRankingApi`、装饰层渲染、3D 椭圆轨道 SVG、布局编排 | `items`、`loading` | — | `cardClick(item)` |
| `PodiumCard.vue` | 单张 TOP 1-3 大卡:钻石渐变外框 + cover + 下方 TOP N 标签 + 可选皇冠 | — | `rank: 4\|5\|6` 隐式;`item: HotRankingItem`、`size: {w,h}` | `click(item)` |
| `ScatteredRanks.vue` | TOP 4-12 共 9 个 item 散落在椭圆轨道 | — | `items: HotRankingItem[]` (length=9) | `cardClick(item)` |
| `config.js` | 9 个 slot 的绝对位置/translate/scale/zIndex 公式 | — | — | — |
---
## 4. 数据流
```
StarGalaxy/index.vue (onMounted)
├── getHotRankingApi("displaying", null, 1, 12)
│ │
│ └── res.data.items[0..11]
├── Promise.all → getAssetCoverRealUrl(item.cover_url) // 复用 HotCategoryBlock 的模式
└── items 拆成两段:
├── items.slice(0,3) → PodiumCard × 3
└── items.slice(3,12) → ScatteredRanks9 个 item
```
事件冒泡:所有 card 点击 → `emit('cardClick', item)``square.vue``handleCardClick`(已有,含单击跳详情 + 双击点赞)。
---
## 5. 视觉设计
### 5.1 容器尺寸
- 总宽 750rpx × 高约 1440rpx
- 适配 iPhone 标准 (375×812)
- 整体分上下两层:上层颁奖台 y=48~700下层椭圆轨道 y=800~1440
### 5.2 装饰层(背景 overlay
- 粉红渐变:`linear-gradient(179deg, #FFE5E5 0%, #F3A0A1 0%, #FF9C9C 86%, #FF2024 100%)`(覆盖 square 现有渐变之上)
- 樱花粉光晕:圆形 `#F3D3E3` + `filter: blur(60px)`居中top 580
- 暖黄光晕:圆形 `#FFFABD` + `filter: blur(50px)`top 50
- ⚠️ 不动 square.vue 现有渐变背景,星河只画自己的装饰层
### 5.3 PodiumCard 三个实例
| 排名 | 位置 (left, top) | 卡片尺寸 | 标签尺寸 | 装饰图 |
|----|----|----|----|----|
| TOP 1 | (50%, 400) translateX(-50%) | 240×260 (cover 240×260) | 192×44 (金渐变) | 钻石外框 + 皇冠 |
| TOP 2 | (60, 120) | 200×200 (cover 200×200) | 156×36 (银渐变) | 钻石外框 |
| TOP 3 | (470, 150) | 192×192 (cover 192×192) | 156×36 (铜渐变) | 钻石外框 |
**PodiumCard 内部层级**(从下到上):
```
┌─ 钻石渐变外框filter: blur(8-12px),不规则圆角 8px 22px 8px 19px
│ ├─ 藏品主图(不规则圆角 6px 20px 6px 17px
│ ├─ 青绿色高光 overlay180deg #53F4D3 → 透明)
│ └─ 钻石渐变边框层4px 渐变描边)
└─ TOP N 标签(绝对定位 bottom: -4px居中
```
**皇冠**(仅 TOP 1绝对定位 `top: -44px; left: 50%; transform: translateX(-50%)`font-size: 44rpx2s 循环 pulse 动画。
### 5.4 ScatteredRanks 9 个 slot 位置
9 个 item 在 65° 倾斜椭圆上,等角度 40° 间隔(顺时针),起始角 180°TOP 4 在正下方 = 最前)。
椭圆参数:
- 圆心 (cx, cy) = (187, 510)
- 水平半径 rx = 130
- 垂直半径 ry = 55cos(65°) ≈ 0.423,模拟向后倾斜 65°
- 起始角 startAngle = 180°slot 0 在正下方)
- 间隔角 step = -40°顺时针 = 负方向)
| Slot | Rank | 中心 (x, y) | 渲染尺寸 (w×h) | scale | z-index |
|----|----|----|----|----|----|
| 0 | TOP 4 | (187, 565) | 46×70 | 1.15 | 10 |
| 1 | TOP 5 | (271, 552) | 46×70 | 1.05 | 9 |
| 2 | TOP 6 | (321, 488) | 46×70 | 0.95 | 6 |
| 3 | TOP 7 | (300, 482) | 46×70 | 0.85 | 3 |
| 4 | TOP 8 | (232, 458) | 46×70 | 0.75 | 0 |
| 5 | TOP 9 | (142, 458) | 46×70 | 0.75 | 0 |
| 6 | TOP 10 | (74, 482) | 46×70 | 0.85 | 3 |
| 7 | TOP 11 | (8, 488) | 46×70 | 0.95 | 6 |
| 8 | TOP 12 | (80, 518) | 46×70 | 1.05 | 9 |
**OVERRIDES**TOP 6 / TOP 11 推到边缘 (8, 488) 和 (321, 488),避免与 TOP 5/7、TOP 12/10 重叠。
### 5.5 单个 item 结构ScatteredRanks
尺寸 46×70 = label 14 + gap 2 + cover 56
```vue
<view class="ring-item">
<view class="top-label">TOP 4</view> <!-- 14rpxlabel cover 上方 -->
<image class="cover" src="..." /> <!-- 56rpxcover label 下方 -->
</view>
```
- **label**(顶部 14rpx
- 背景:`radial-gradient(ellipse, #C8E6FF, #fff 50%, #4D9AF8)`(蓝白钻石)
- TOP 4 label 用金渐变:`#FFFFFF, #FFFABD 30%, #4D9AF8 100%`
- 圆角 7rpx
- 文字14rpx Baloo Bhaifallback monospace`#FFFABD``text-shadow: -1px 1px 2px rgba(206,9,9,0.84)`
- **cover**(下方 56rpx
- 圆角 5rpx
- box-shadow`3px 3px 6px rgba(198,13,13,0.45)`(按 scale 调整)
### 5.6 颜色 / 渐变 / 阴影
| Token | 值 | 用途 |
|----|----|----|
| 主背景 | `linear-gradient(179deg, #FFE5E5 0%, #F3A0A1 0%, #FF9C9C 86%, #FF2024 100%)` | 装饰层渐变 |
| 樱花粉光晕 | `#F3D3E3` + `blur(60px)` | 椭圆中心光晕 |
| 暖黄光晕 | `#FFFABD` + `blur(50px)` | 顶部装饰光晕 |
| 钻石渐变(外框) | `radial-gradient(ellipse at -10% 5%, #86BEFF, #FF3939 32%, #88FFCE 59%, #4D9AF8)` | PodiumCard 描边 |
| 蓝白钻石(标签) | `radial-gradient(ellipse, #C8E6FF, #fff 50%, #4D9AF8)` | 9 item label |
| 金渐变TOP 4 label | `radial-gradient(ellipse, #FFFFFF, #FFFABD 30%, #4D9AF8 100%)` | TOP 4 label |
| 金渐变TOP 1 标签) | `radial-gradient(ellipse, #FFD700, #FFF6A8 30%, #DAA520 100%)` | TOP 1 标签 |
| 银渐变TOP 2 标签) | `radial-gradient(ellipse, #C0C0C0, #E8E8E8 50%, #7A7A7A)` | TOP 2 标签 |
| 铜渐变TOP 3 标签) | `radial-gradient(ellipse, #CD7F32, #E8A45C 50%, #A0522D)` | TOP 3 标签 |
| TOP 数字字色 | `#FFFABD` + `text-shadow: -1px 1px 2px rgba(206,9,9,0.84)` | label 文字 |
| 卡片阴影 | `3px 3px 6px rgba(198,13,13,0.45)`(随 scale 微调) | cover 阴影 |
### 5.7 字体
| 字体 | 用途 | Fallback |
|----|----|----|
| Baloo Bhai | label 数字TOP 4、TOP 5 等) | monospace |
| HYQiHei | 不使用(已移除用户名显示) | PingFang |
| C800 | 不使用(已移除 TOP 排名文字) | system bold sans |
---
## 6. 动画设计
### 6.1 9 item 顺时针旋转
```
@keyframes orbit {
0% { transform: translate(0,0) scale(1.15); } /* slot 0: front */
11.11% { transform: translate(84px,-13px) scale(1.05); } /* slot 1 */
22.22% { transform: translate(157px,-43px) scale(0.95); } /* slot 2 */
33.33% { transform: translate(113px,-83px) scale(0.85); } /* slot 3 */
44.44% { transform: translate(45px,-107px) scale(0.75); } /* slot 4 */
55.55% { transform: translate(-45px,-107px) scale(0.75); } /* slot 5 */
66.66% { transform: translate(-113px,-83px) scale(0.85); } /* slot 6 */
77.77% { transform: translate(-156px,-43px) scale(0.95); } /* slot 7 */
88.88% { transform: translate(-84px,-13px) scale(1.05); } /* slot 8 */
100% { transform: translate(0,0) scale(1.15); } /* loop */
}
```
- 总周期 36s4s 一步
- 线性 timing匀速旋转
- infinite 循环
### 6.2 9 个 item 的 delay 错开
```
.ring-item { animation: orbit 36s linear infinite; }
.r0 { animation-delay: 0s; } /* TOP 4 起始 slot 0 */
.r1 { animation-delay: -4s; } /* TOP 5 起始 slot 1 */
.r2 { animation-delay: -8s; } /* TOP 6 起始 slot 2 */
...
.r8 { animation-delay: -32s; } /* TOP 12 起始 slot 8 */
```
负值 delay 让每个 item 起始位置 = 对应的 slot9 个 item 静态分布在 9 个 slot 上。
### 6.3 皇冠脉冲
```css
@keyframes crownPulse {
0%, 100% { transform: translateX(-50%) scale(1); }
50% { transform: translateX(-50%) scale(1.15); }
}
.crown { animation: crownPulse 2s ease-in-out infinite; }
```
### 6.4 可访问性
```css
@media (prefers-reduced-motion: reduce) {
.ring-item { animation: none; }
.crown { animation: none; }
/* item 用 inline transform 写到元素上,作为静态位置 */
}
```
---
## 7. 状态管理
| 状态 | 显示 |
|----|----|
| Loading | 12 个骨架占位3 大 + 9 小)+ shimmer 动画 |
| Success | 完整布局 |
| Empty (items.length < 3) | 数据加载中」+ 重试按钮 |
| Error | 居中提示加载失败点击重试」+ 重试按钮 |
---
## 8. 交互细节
- **单击 PodiumCard / ScatteredRank 缩略图**emit('cardClick', item) square.vue 已有逻辑 `pages/asset-detail/asset-detail`
- **双击**同样走 square.vue `handleCardClick` `doubleTapLike` + 波纹动画
- **TOP 1 奖牌/皇冠**CSS keyframes 微脉冲scale 1.0 1.15 1.02s 循环
---
## 9. 性能/可访问性
- 图片懒加载用 uni-app `<image lazy-load>`与现有 HotCategoryBlock 一致
- `prefers-reduced-motion: reduce` 关闭旋转和脉冲
- 9 item 动画使用 GPU 加速transform
- 椭圆轨道 SVG 装饰层 `pointer-events: none`
---
## 10. 风险与备选
| 风险 | 缓解 |
|----|----|
| 旋转动画在低端机上卡顿 | 可降级到 60s 周期或加 `will-change: transform` |
| 椭圆轨道的虚线 SVG 在某些小程序不渲染 | 提供 `v-if` 兜底 H5 显示 |
| 数据 < 9 条时 ScatteredRanks 渲染异常 | v-for 配合 length 校验< 9 时只渲染实际条数 |
---
## 11. 验收标准
1. square 页点击星河tab显示 12 item 3D 排行榜
2. TOP 1-3 在上方颁奖台位置cover 下方有金// TOP N 标签
3. TOP 4-12 在下方椭圆轨道9 item 等大 46×70标签在 cover 上方
4. 9 item 顺时针连续旋转36s 一圈
5. 近大远小scale 0.751.15表达 3D 透视
6. 点击任一卡片跳 asset-detail
7. 双击点赞 + 波纹动画
8. Loading / Empty / Error 状态都正常显示
9. `prefers-reduced-motion: reduce` 时关闭旋转
10. H5 + 小程序 + APP 三端视觉一致