h5周星优化

This commit is contained in:
170-carry 2026-04-30 23:13:48 +08:00
parent bb0a31f601
commit 89534f7bf3

View File

@ -62,8 +62,9 @@
}
.page {
width: min(100%, var(--page-width));
width: 100%;
min-height: 100vh;
min-height: 100dvh;
margin: 0 auto;
background: linear-gradient(180deg, #163a07 0%, #123708 38%, #0d2f08 100%);
overflow: hidden;
@ -113,7 +114,7 @@
width: min(72%, 230px);
max-width: calc(100% - 44px);
aspect-ratio: 665 / 88;
padding: 0 24px 6px;
padding: 0 24px 2px;
background: center / 100% 100% no-repeat var(--subtitle-frame-image);
color: #f8e29b;
font-size: 13px;
@ -158,6 +159,22 @@
object-fit: contain;
}
.gift-card.gift-skeleton .gift-inner {
position: relative;
overflow: hidden;
border-radius: 16px;
}
.gift-card.gift-skeleton .gift-inner::before {
content: "";
width: 70%;
aspect-ratio: 1;
border-radius: 24px;
background: linear-gradient(90deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 248, 198, 0.35) 48%, rgba(255, 255, 255, 0.12) 100%);
background-size: 220% 100%;
animation: rank-skeleton-loading 1.1s ease-in-out infinite;
}
.gift-empty {
opacity: 0.3;
}
@ -165,14 +182,16 @@
.tab-row {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
padding: 14px 15px 0;
gap: 12px;
padding: 12px 17px 14px;
}
.tab-button {
position: relative;
display: block;
width: 100%;
aspect-ratio: 336 / 126;
aspect-ratio: 336 / 108;
padding: 0;
border: 0;
appearance: none;
background: none;
@ -191,8 +210,8 @@
.tab-button.active::before {
background-image: var(--tab-active-image);
width: 103.4%;
height: 109%;
width: 103.57%;
height: 108.73%;
}
.tab-button span {
@ -216,15 +235,7 @@
.tab-button.active span {
color: #c15f17;
}
.period-strip {
padding: 18px 18px 16px;
color: #fff8c6;
text-align: center;
font-size: 18px;
line-height: 1.1;
font-weight: 700;
transform: translate(-50%, calc(-50% + 1px));
}
.rank-stage {
@ -271,6 +282,48 @@
color: transparent;
}
.rank-row.skeleton {
color: transparent;
}
.rank-row.skeleton .rank-mark,
.rank-row.skeleton .rank-avatar,
.rank-row.skeleton .rank-name,
.rank-row.skeleton .rank-score {
position: relative;
overflow: hidden;
background: linear-gradient(90deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 248, 198, 0.28) 48%, rgba(255, 255, 255, 0.1) 100%);
background-size: 220% 100%;
animation: rank-skeleton-loading 1.1s ease-in-out infinite;
}
.rank-row.skeleton .rank-mark {
height: 18px;
border-radius: 999px;
}
.rank-row.skeleton .rank-name {
height: 16px;
max-width: 54%;
border-radius: 999px;
}
.rank-row.skeleton .rank-score {
width: 48px;
height: 16px;
border-radius: 999px;
}
@keyframes rank-skeleton-loading {
0% {
background-position: 120% 0;
}
100% {
background-position: -120% 0;
}
}
.rank-mark {
width: 30px;
flex: 0 0 30px;
@ -485,9 +538,10 @@
<div class="gift-grid">
<template x-for="gift in giftSlots()" :key="gift.key">
<div class="gift-card" :class="{ 'gift-empty': gift.placeholder }" :title="giftTitle(gift)">
<div class="gift-card" :class="{ 'gift-empty': gift.placeholder, 'gift-skeleton': gift.skeleton }"
:title="giftTitle(gift)">
<div class="gift-inner">
<template x-if="!gift.placeholder">
<template x-if="!gift.placeholder && !gift.skeleton">
<img :src="giftCover(gift)" :alt="giftTitle(gift)" />
</template>
</div>
@ -497,37 +551,33 @@
<nav class="tab-row">
<button type="button" class="tab-button" :class="{ active: activeTab === 'last' }" @click="activeTab = 'last'">
<span>Previous Top 3 Records</span>
<span>Previous Top 3</span>
</button>
<button type="button" class="tab-button" :class="{ active: activeTab === 'current' }"
@click="activeTab = 'current'">
<span>This Week's rank list</span>
<span>This Week</span>
</button>
<button type="button" class="tab-button" :class="{ active: activeTab === 'reward' }"
@click="activeTab = 'reward'">
<span>Reward Description</span>
<span>Reward</span>
</button>
</nav>
</section>
<template x-if="activeTab !== 'reward'">
<div class="period-strip" x-text="activePeriodText()"></div>
</template>
<section class="rank-stage" x-show="activeTab !== 'reward'">
<ul class="rank-list">
<template x-for="(item, index) in displayRows()" :key="item.key">
<li class="rank-row" :class="rowClass(index, item)">
<div class="rank-mark">
<template x-if="isTopRank(item.rank)">
<template x-if="!item.skeleton && isTopRank(item.rank)">
<img class="rank-badge-icon" :src="topRankIcon(item.rank)" :alt="'Top ' + item.rank" />
</template>
<template x-if="!isTopRank(item.rank)">
<template x-if="!item.skeleton && !isTopRank(item.rank)">
<span class="plain" x-text="item.rank"></span>
</template>
</div>
<div class="rank-avatar">
<template x-if="!item.placeholder">
<template x-if="!item.placeholder && !item.skeleton">
<img :src="avatarOf(item)" :alt="item.userNickname || 'avatar'" />
</template>
</div>
@ -537,11 +587,11 @@
</template>
</ul>
<div class="empty-message" x-show="!hasRealRows() && !error">
<div class="empty-message" x-show="!loading && !hasRealRows() && !error">
No ranking data yet. Send the selected gifts and the leaderboard will update
here.
</div>
<div class="empty-message" x-show="error" x-text="error"></div>
<div class="empty-message" x-show="!loading && error" x-text="error"></div>
</section>
<section class="reward-stage" x-show="activeTab === 'reward'">
@ -602,16 +652,10 @@
function weekStarPage() {
return {
activeTab: "last",
activeTab: "current",
data: normalizePageData({}),
error: "",
activePeriodText() {
if (this.activeTab === "last") {
return this.data.lastPeriodText || this.data.currentPeriodText || "--";
}
return this.data.currentPeriodText || this.data.lastPeriodText || "--";
},
loading: true,
avatarOf(item) {
return item && item.userAvatar ? item.userAvatar : DEFAULT_AVATAR;
@ -638,6 +682,9 @@
},
displayRows() {
if (this.loading) {
return this.skeletonRows();
}
if (this.activeTab === "current") {
return this.buildRows(this.data.thisWeekTop10, 5, 10);
}
@ -646,6 +693,7 @@
async fetchPage() {
this.error = "";
this.loading = true;
try {
const url = new URL("/go/public/h5/week-star/page", resolveWeekStarAPIBase());
const sysOrigin = new URLSearchParams(window.location.search).get("sysOrigin");
@ -674,6 +722,8 @@
? error.message
: "Please retry later.";
this.data = normalizePageData({});
} finally {
this.loading = false;
}
},
@ -682,6 +732,12 @@
},
giftSlots() {
if (this.loading) {
return Array.from({ length: 3 }, (_, index) => ({
key: "gift-skeleton-" + index,
skeleton: true,
}));
}
const slots = (this.data.qualifyingGifts || []).slice(0, 3).map((gift, index) => {
return {
...gift,
@ -699,6 +755,9 @@
},
giftTitle(gift) {
if (gift && gift.skeleton) {
return "Loading gift";
}
if (!gift || gift.placeholder) {
return "Gift";
}
@ -706,7 +765,7 @@
},
hasRealRows() {
return this.displayRows().some((item) => !item.placeholder);
return !this.loading && this.displayRows().some((item) => !item.placeholder && !item.skeleton);
},
heroPeriodText() {
@ -761,22 +820,34 @@
rowClass(index, item) {
const stripe = index % 2 === 0 ? "light" : "dark";
if (item && item.skeleton) {
return stripe + " skeleton";
}
return item.placeholder ? stripe + " placeholder" : stripe;
},
rowName(item) {
if (!item || item.placeholder) {
if (!item || item.placeholder || item.skeleton) {
return "";
}
return item.userNickname || ("User " + item.userId);
},
rowScore(item) {
if (!item || item.placeholder) {
if (!item || item.placeholder || item.skeleton) {
return "";
}
return toScoreText(item.scoreGold);
},
skeletonRows() {
const size = this.activeTab === "last" ? 4 : 5;
return Array.from({ length: size }, (_, index) => ({
key: "skeleton-" + this.activeTab + "-" + index,
skeleton: true,
rank: 0,
}));
},
};
}