feat(ui): 商店分类卡补齐两种列布局与官方 hover
This commit is contained in:
1 parent
26484e4f73
commit
5d0cc51e75
3 files changed
+129
-21
No files matched your search
@@ -206,6 +206,36 @@ position:absolute; top:1em; z-index:3;
|
||||
|
||||
离线实现:没有 `dsw_1` 分类美术 URL,保留官方 `z1so` 的背景色/尺寸/圆角,图像区域用 `stub.shop.card.art` 虚线占位;标题仍按 `b1sp` 定位。
|
||||
|
||||
#### 两种列布局
|
||||
|
||||
原版 `MGi.tap` 先渲染 `specialCategories`,再渲染 `generalCategories`:
|
||||
|
||||
| 类型 | 列样式 | 卡片样式 | 高度 |
|
||||
|---|---|---|---|
|
||||
| 普通分类(一排两行) | `w1hz`:`flex column; justify-content:space-between` | `commonCard` | `calc(100% / 2 - 1.2em)` |
|
||||
| 特殊/限时(一排一行) | `f1if`:`flex column; justify-content:flex-start` | `limitedOffersCommon` | `calc(100% - .5em)` |
|
||||
|
||||
两种列都是 `width:22.5em; flex:0 0 22.5em; height:calc(100% - .5em); margin-right:1em`。
|
||||
|
||||
离线数据没有原版 `esw_1` 特殊分类标记;当前用 `KIT(套件)` 作为特殊分类演示全高卡(代码内有 `STUB` 注释)。
|
||||
|
||||
#### Hover 细节
|
||||
|
||||
| 目标 | 原版 | CSS |
|
||||
|---|---|---|
|
||||
| 分类卡图像 | `VGi` / `KGi` | `transform:scale(1.05,1.05); transition:all .2s ease-in-out` |
|
||||
| 分类菜单项(未激活) | `pHi` / `wHi` | `box-shadow:0 0 0 2px rgb(255,255,255); border-radius:.5em; margin-left:1.25em; transition:all .1s ease-in-out` |
|
||||
| 分类菜单项(激活) | `aHi` / `cHi` | `box-shadow:none` |
|
||||
| 物品卡 | `TYi` | `box-shadow:0 0 0 2px rgb(255,255,255)` |
|
||||
|
||||
这些都已用无头探针确认生成的 CSS,例如:
|
||||
|
||||
```css
|
||||
.css-1vz2w25:hover { transform: scale(1.05, 1.05); transition: 0.2s ease-in-out; }
|
||||
.css-1owq6vw:hover { box-shadow: rgb(255, 255, 255) 0em 0em 0em 2px; }
|
||||
.css-1vberae:hover { box-shadow: none; }
|
||||
```
|
||||
|
||||
## 3. 资源
|
||||
|
||||
| 用途 | 资源 |
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
| 子页面外壳 | `LobbyView.kt` + `lobby3.css` | `ms-top` 始终显示;`#lobby-content` 从 6em 开始;不再使用独立 Breadcrumbs 顶栏 |
|
||||
| 大厅首页 | `app/src/jsMain/kotlin/App/Browser/Ui/HomeScreen.kt` | Emotion;Play / 主菜单 / Footer / BattlePass / 公告 |
|
||||
| 车库 | `app/src/jsMain/kotlin/App/Browser/Ui/GarageScreen.kt` | React;默认「总览」+ 右侧 4 个已装配件;分类栏、底盘/炮塔选择条、迷彩网格、左右面板、底部选择条 |
|
||||
| 商店 | `app/src/jsMain/kotlin/App/Browser/Ui/LobbyScreens.kt` | 先进入 `ShopSelectedSectionComponentStyle-commonBlock` 分类卡(22.5em,每列 2 张,横向滚动;官方 `z1so` 背景色/尺寸保留,`dsw_1` 分类美术用 `stub.shop.card.art` 虚线占位),点卡后进入 `NewShopCommonComponentStyle-commonContainer`:左侧 25em 分类菜单 + 右侧 37.25em 物品区(分类标题/描述 + 2 列 17.5em 物品卡)+ 底部 3.125em 报告栏;详见 `ShopStyleTable.md` |
|
||||
| 商店 | `app/src/jsMain/kotlin/App/Browser/Ui/LobbyScreens.kt` | 先进入 `ShopSelectedSectionComponentStyle-commonBlock` 分类卡(22.5em;特殊分类一排一行 `limitedOffersCommon`,普通分类一排两行 `commonCard`;卡内 `z1so` 背景色保留,`dsw_1` 分类美术用 `stub.shop.card.art` 虚线占位;已接图像 hover 放大、菜单项 hover 白边/位移、物品卡 hover 白边),点卡后进入 `NewShopCommonComponentStyle-commonContainer`:左侧 25em 分类菜单 + 右侧 37.25em 物品区(分类标题/描述 + 2 列 17.5em 物品卡)+ 底部 3.125em 报告栏;详见 `ShopStyleTable.md` |
|
||||
| 幸运礼盒 | 同上 | 4 种礼盒美术、钥匙图标/数量、可能奖励稀有度、右侧详情/开启确认 |
|
||||
| 战斗选择 | 同上 | 模式筛选(DM/TDM/CTF/CP/AS/JGR/SGE/RUGBY 图标)+ 战场列表 + 右侧详情/加入/创建入口 |
|
||||
| 任务 | 同上 | 分类侧栏(主要/每日/每周/星级挑战/合约)+ 任务卡(进度、奖励图标、领取/去完成)+ 星级挑战横幅 + 离线示例 |
|
||||
|
||||
@@ -659,11 +659,36 @@ private fun RarityColor(Rarity: Int): String = when (Rarity) {
|
||||
else -> "rgba(232, 242, 255, 0.78)"
|
||||
}
|
||||
|
||||
/** 原版 `commonBlockMenuShop`。 */
|
||||
// ---------------------------------------------------------------- 商店(NewShopCommonComponentStyle)
|
||||
|
||||
/** 原版 `commonBlockMenuShop`。 */
|
||||
// ---------------------------------------------------------------- 商店(NewShopCommonComponentStyle)
|
||||
/** 原版 `wHi`:菜单项 hover。 */
|
||||
private fun csstype.PropertiesBuilder.ShopMenuItemHover(Active: Boolean) {
|
||||
val Style: dynamic = js("({})")
|
||||
if (Active) {
|
||||
Style["boxShadow"] = "none"
|
||||
} else {
|
||||
Style["boxShadow"] = "0em 0em 0em 2px rgb(255, 255, 255)"
|
||||
Style["borderRadius"] = "0.5em"
|
||||
Style["marginLeft"] = "1.25em"
|
||||
Style["transition"] = "all 0.1s ease-in-out"
|
||||
}
|
||||
asDynamic()["&:hover"] = Style
|
||||
}
|
||||
|
||||
/** 原版 `TYi`:物品卡 hover。 */
|
||||
private fun csstype.PropertiesBuilder.ShopItemHover() {
|
||||
val Style: dynamic = js("({})")
|
||||
Style["boxShadow"] = "0em 0em 0em 2px rgb(255, 255, 255)"
|
||||
asDynamic()["&:hover"] = Style
|
||||
}
|
||||
|
||||
/** 原版 `VGi` / `KGi`:分类卡图像 hover 放大。 */
|
||||
private fun csstype.PropertiesBuilder.ShopCardImageHover() {
|
||||
val Style: dynamic = js("({})")
|
||||
Style["transform"] = "scale(1.05, 1.05)"
|
||||
Style["transition"] = "all 0.2s ease-in-out"
|
||||
asDynamic()["&:hover"] = Style
|
||||
}
|
||||
|
||||
/** 原版 `commonBlockMenuShop`。 */
|
||||
private val ShopMenu: csstype.PropertiesBuilder.() -> Unit = {
|
||||
@@ -707,6 +732,7 @@ private fun ShopMenuItemStyle(Active: Boolean): csstype.PropertiesBuilder.() ->
|
||||
Style("background-color", "rgba(255, 204, 0, 0.25)")
|
||||
Style("border-radius", "0.5rem")
|
||||
}
|
||||
ShopMenuItemHover(Active)
|
||||
}
|
||||
|
||||
private fun ShopMenuIconStyle(Active: Boolean, Icon: String): csstype.PropertiesBuilder.() -> Unit = {
|
||||
@@ -822,6 +848,7 @@ private fun ShopItemStyle(Selected: Boolean): csstype.PropertiesBuilder.() -> Un
|
||||
"box-shadow",
|
||||
if (Selected) "0em 0em 0em 1px rgb(255, 204, 0)" else "0em 0em 0em 1px rgba(255, 255, 255, 0.3)",
|
||||
)
|
||||
ShopItemHover()
|
||||
}
|
||||
|
||||
private val ShopItemHeader: csstype.PropertiesBuilder.() -> Unit = {
|
||||
@@ -931,6 +958,18 @@ private val ShopCardsColumn: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("flex", "0 0 22.5em")
|
||||
}
|
||||
|
||||
/** 原版 `TGi` / `f1if`:一列 1 张全高卡(special / limited)。 */
|
||||
private val ShopCardsColumnFull: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("display", "flex")
|
||||
Style("flex-direction", "column")
|
||||
Style("justify-content", "flex-start")
|
||||
Style("align-items", "stretch")
|
||||
Style("height", "calc(100% - 0.5em)")
|
||||
Style("margin-right", "1em")
|
||||
Style("width", "22.5em")
|
||||
Style("flex", "0 0 22.5em")
|
||||
}
|
||||
|
||||
/** 原版 `commonCard`。 */
|
||||
private val ShopCard: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("display", "flex")
|
||||
@@ -947,6 +986,22 @@ private val ShopCard: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("background", "transparent")
|
||||
}
|
||||
|
||||
/** 原版 `limitedOffersCommon`:全高单卡。 */
|
||||
private val ShopCardFull: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("display", "flex")
|
||||
Style("align-items", "center")
|
||||
Style("justify-content", "center")
|
||||
Style("border-radius", "0.625em")
|
||||
Style("cursor", "pointer")
|
||||
Style("height", "calc(100% - 0.5em)")
|
||||
Style("overflow", "hidden")
|
||||
Style("padding", "0.4em")
|
||||
Style("position", "relative")
|
||||
Style("width", "22.5em")
|
||||
Style("border", "0")
|
||||
Style("background", "transparent")
|
||||
}
|
||||
|
||||
/** 原版 `z1so`:分类卡背景图。
|
||||
* STUB: 原版 `dsw_1` 分类美术 URL 未接入,只保留官方背景色与尺寸。 */
|
||||
private val ShopCardImage: csstype.PropertiesBuilder.() -> Unit = {
|
||||
@@ -957,6 +1012,7 @@ private val ShopCardImage: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("position", "relative")
|
||||
Style("overflow", "hidden")
|
||||
Style("z-index", "1")
|
||||
ShopCardImageHover()
|
||||
}
|
||||
|
||||
/** STUB: 分类美术占位。 */
|
||||
@@ -974,6 +1030,28 @@ private val ShopCardStub: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("border-radius", "0.625em")
|
||||
}
|
||||
|
||||
/** 原版 `$Gi`:分类卡。`Full=true` 使用 `limitedOffersCommon`。 */
|
||||
private fun react.ChildrenBuilder.ShopCategoryCard(
|
||||
C: GarageCategory,
|
||||
Full: Boolean,
|
||||
OnPick: () -> Unit,
|
||||
) {
|
||||
div {
|
||||
css(if (Full) ShopCardFull else ShopCard)
|
||||
Ui("shop.card.${C.Id}")
|
||||
onClick = { OnPick() }
|
||||
div {
|
||||
css(ShopCardImage)
|
||||
div {
|
||||
css(ShopCardStub)
|
||||
Ui("stub.shop.card.art")
|
||||
+"分类美术"
|
||||
}
|
||||
}
|
||||
span { css(ShopCardTitle); +C.Label }
|
||||
}
|
||||
}
|
||||
|
||||
/** 原版 `b1sp`:分类卡标题。 */
|
||||
private val ShopCardTitle: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("font-family", "BaseFontMedium, FallbackFontMedium")
|
||||
@@ -1069,6 +1147,9 @@ private val ShopScreen = FC<Props> {
|
||||
var Category by useState("all")
|
||||
val Items = UiCatalog.Items.filter { it.Buyable && it.Name.isNotBlank() }
|
||||
val Categories = GarageCategories.filter { C -> Items.any { it.Category == C.Id } }
|
||||
// STUB: 离线数据没有原版 esw_1 特殊分类标记;用 KIT(套件) 演示 limitedOffersCommon 全高卡。
|
||||
val SpecialCategories = Categories.filter { it.Id == 5 }
|
||||
val GeneralCategories = Categories.filter { it.Id != 5 }
|
||||
val Shown = (if (Category == "all") Items else Items.filter { it.Category.toString() == Category })
|
||||
.distinctBy { it.Name }
|
||||
val ActiveLabel = Categories.firstOrNull { it.Id.toString() == Category }?.Label ?: "全部"
|
||||
@@ -1085,27 +1166,24 @@ private val ShopScreen = FC<Props> {
|
||||
div {
|
||||
css(ShopCardsScroll)
|
||||
Ui("shop.cards.scroll")
|
||||
for (Pair in Categories.chunked(2)) {
|
||||
for (C in SpecialCategories) {
|
||||
div {
|
||||
css(ShopCardsColumnFull)
|
||||
Ui("shop.cards.column.full")
|
||||
ShopCategoryCard(C, true) {
|
||||
Category = C.Id.toString()
|
||||
Entered = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Pair in GeneralCategories.chunked(2)) {
|
||||
div {
|
||||
css(ShopCardsColumn)
|
||||
Ui("shop.cards.column")
|
||||
for (C in Pair) {
|
||||
div {
|
||||
css(ShopCard)
|
||||
Ui("shop.card.${C.Id}")
|
||||
onClick = {
|
||||
Category = C.Id.toString()
|
||||
Entered = true
|
||||
}
|
||||
div {
|
||||
css(ShopCardImage)
|
||||
div {
|
||||
css(ShopCardStub)
|
||||
Ui("stub.shop.card.art")
|
||||
+"分类美术"
|
||||
}
|
||||
}
|
||||
span { css(ShopCardTitle); +C.Label }
|
||||
ShopCategoryCard(C, false) {
|
||||
Category = C.Id.toString()
|
||||
Entered = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user