主题
上架与分发
皮肤有两条分发路径:内置(打包进客户端,随版本发布)和商城远端(上传到皮肤市场,用户购买/领取后应用)。本篇讲两者的差异与远端皮肤的字段约定。
内置皮肤
适合官方维护、随客户端分发的皮肤。做法见快速开始:把 Skin 对象(用 css 字符串)加进对应组件的内置数组——
- 加班机:
src/assets/data/skins/overtime-machine.ts - 礼物菜单:
src/assets/data/skins/gift-menu.ts
src/assets/data/skins/index.ts 的 skinsList 自动汇总,启动时 injectAllBuiltinSkins() 一次性注入。内置皮肤对所有用户恒为已拥有(price 仅展示,不接支付)。
新增一类组件的内置皮肤时,记得在 skin-css.ts 的 WRAPPER_CLASS_MAP 里登记它的包裹层类名,否则作用域注入拿不到前缀。
商城远端皮肤
适合第三方作者上传、可售卖的皮肤。客户端不打包它的 CSS,而是按需从服务端拉取。
数据流
商城列表 getSkins({ elementType }) → 只返回展示字段(无 CSS)
点皮肤卡 → 详情 getSkin(serverId) → 返回 cssUrl / cssContent / skinData
应用前 ensureMarketplaceSkinReady() → 每次都拉最新详情 + injectSkinCss()
应用 applyElementSkin() → 合并 skinData + 写 element.skinId客户端皮肤 id 是字符串,远端 id 是数字,落到 element.skinId 时统一加 remote: 前缀(buildRemoteSkinId),不与内置 id 冲突。
后端响应到 Skin 的字段映射
toSkin()(src/utils/marketplace-skin.ts)把后端响应映射成客户端 Skin:
| 后端字段 | 客户端 Skin 字段 | 说明 |
|---|---|---|
id(number) | id = "remote:<id>" / marketplaceId | 应用/购买/查已购都用它 |
title | name | 显示名 |
elementType | elementType | 适用组件,必须是合法 ElementType 值 |
previewImage[0] | thumbnail | 选择器/卡片缩略图 |
previewImage[] | 详情主图 gallery | 详情弹窗轮播 |
detailImages[] | 详情长图 | 详情弹窗正文图 |
detailText / description | description | 商品详情正文 |
tags | tags | 标签 chip |
price / originalPrice | price / originalPrice | 价格(元),0 = 免费 |
memberDiscounts / appliedMemberDiscount | 同名 | 会员折扣展示 |
author | author | 作者信息 |
hasPurchased | hasPurchased | 是否已购 |
cssContent | css | 内联 CSS(与 cssUrl 二选一) |
cssUrl | cssUrl | CSS 链接(推荐) |
skinData | data | 应用时合并进元素的字段 |
列表接口没有
cssUrl/cssContent/skinData,只有详情接口才有。ensureMarketplaceSkinReady每次都拉最新详情、不复用缓存——后台改了字体/CSS,客户端能立刻拿到新值。
cssUrl 的写法约定
- 加班机 / 礼物菜单:上传的 CSS 可以省略作用域前缀——客户端
ensureScoped会自动把裸.desc { ... }包成.wrapper[data-skin-id="remote:123"] .desc { ... }。当然写全前缀也兼容。 - 弹幕姬:CSS 是整张 blivechat 主题,用顶层
yt-live-chat-*选择器,不要加作用域前缀(弹幕姬走<link>整表加载,不经ensureScoped)。
skinData 要放什么
| 组件 | skinData 必填 | 说明 |
|---|---|---|
| 礼物菜单 | 一般不需要 | 纯视觉 |
| 加班机 | { displayMode, width: 280 } | 绑定版式;不要写 height(autoSize) |
| 弹幕姬 | { versionType, fonts? } | 版本决定 DOM;fonts: [{family,url}] 注册自带字体 |
资源可达性(重要)
cssUrl、缩略图、字体文件都要用公网可达的地址,且在编辑 / 预览 / 虚拟摄像头三种窗口都能加载——预览窗与推流窗无鉴权、不会走详情接口(弹幕姬把 skinCssUrl 持久化到元素上正是为此)。字体 / CSS 所在 OSS 需配好 CORS。
自测清单
上架前在客户端里逐项确认:
- [ ] 皮肤出现在对应组件的「样式 → 皮肤 → 我的皮肤」列表里,且类型匹配。
- [ ] 套用后外观正确:背景 / 颜色 / 描边 / 阴影 / 动效都生效,无需
!important也能盖过默认。 - [ ] 没有把别的皮肤或编辑器 UI 染色(作用域前缀写对、
@keyframes名带前缀没撞名)。 - [ ] 加班机:版式与
displayMode一致,高度自适应正常,不闪。 - [ ] 弹幕姬:
versionType与主题匹配,自带字体能下载(CORS 正常),切回「默认」能干净恢复。 - [ ] 切到「预览」窗口,皮肤照常显示(资源可达,无鉴权依赖)。