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

11 KiB
Raw Blame History

星河StarGalaxy组件设计

日期: 2026-06-10 状态: 待评审 目的: 为 square 页面的「星河」tab 设计一个 3D 倾斜椭圆轨道 + 顺时针旋转的排行榜组件 参考文档: docs/figma-analysis-xiaohongshu-stars.mdFigma 节点 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、布局编排 itemsloading cardClick(item)
PodiumCard.vue 单张 TOP 1-3 大卡:钻石渐变外框 + cover + 下方 TOP N 标签 + 可选皇冠 rank: 4|5|6 隐式;item: HotRankingItemsize: {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.vuehandleCardClick(已有,含单击跳详情 + 双击点赞)。


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

OVERRIDESTOP 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

<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#FFFABDtext-shadow: -1px 1px 2px rgba(206,9,9,0.84)
  • cover(下方 56rpx
    • 圆角 5rpx
    • box-shadow3px 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 皇冠脉冲

@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 可访问性

@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 的 handleCardClickdoubleTapLike + 波纹动画
  • 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.75→1.15)表达 3D 透视
  6. 点击任一卡片跳 asset-detail
  7. 双击点赞 + 波纹动画
  8. Loading / Empty / Error 状态都正常显示
  9. prefers-reduced-motion: reduce 时关闭旋转
  10. H5 + 小程序 + APP 三端视觉一致