♻️ 列表渲染改成原生,降低性能消耗

This commit is contained in:
BTMuli
2023-11-21 15:23:51 +08:00
parent 618d3a1632
commit fd3822fe70

View File

@@ -19,41 +19,29 @@
<div class="wrap"> <div class="wrap">
<!-- 左侧菜单 --> <!-- 左侧菜单 -->
<div class="left-wrap"> <div class="left-wrap">
<v-list <div
v-for="series in allSeriesData" v-for="series in allSeriesData"
:key="series.id" :key="series.id"
class="card-left" class="card-series"
@click="selectSeries(series.id)" @click="selectSeries(series.id)"
> >
<div class="version-icon-series">v{{ series.version }}</div> <div class="series-version">v{{ series.version }}</div>
<v-list-item> <img alt="icon" class="series-icon" :src="getIcon(series.id)" />
<template #prepend> <div class="series-content">
<v-img class="series-icon" :src="getIcon(series.id)" /> <span :title="series.name">
</template>
<v-list-item-title :title="series.name">
{{ series.name }} {{ series.name }}
</v-list-item-title> </span>
<v-list-item-subtitle> <span> {{ series.finCount }} / {{ series.totalCount }} </span>
{{ series.finCount }} / {{ series.totalCount }} </div>
</v-list-item-subtitle> </div>
</v-list-item>
</v-list>
</div> </div>
<!-- 右侧内容--> <!-- 右侧内容-->
<div class="right-wrap" @scroll="handleScroll"> <div class="right-wrap">
<v-list <v-list
v-if="selectedSeries !== 0 && selectedSeries !== 17 && selectedSeries !== -1 && !loading" v-if="selectedSeries !== 0 && selectedSeries !== 17 && selectedSeries !== -1 && !loading"
class="achi-series"
:style="{ :style="{
backgroundImage: 'url(' + getCardImg.bg || null + ')', backgroundImage: 'url(' + getCardImg.bg + ')',
backgroundPosition: 'right',
backgroundSize: 'auto 100%',
backgroundRepeat: 'no-repeat',
margin: '10px',
borderRadius: '10px 50px 50px 10px',
border: '1px solid var(--common-shadow-2)',
fontFamily: 'var(--font-title)',
cursor: 'pointer',
position: 'relative',
}" }"
@click="openImg()" @click="openImg()"
> >
@@ -63,54 +51,51 @@
</template> </template>
</v-list-item> </v-list-item>
</v-list> </v-list>
<div class="list-empty" :style="{ height: `${emptyHeight}px` }"> <div
<v-list v-for="achievement in renderSelect"
v-for="achievement in renderAchievement" :key="achievement.id"
:key="achievement.id" class="card-achi"
class="card-right" :title="allSeriesData.find((item) => item.id === achievement.series)?.name ?? ''"
:style="{ transform: `translateY(${translateY})` }" >
:title="allSeriesData.find((item) => item.id === achievement.series)?.name ?? ''" <div v-if="achievement.progress !== 0" class="achi-progress">
> {{ achievement.progress }}
<div v-if="achievement.progress !== 0" class="achievement-progress"> </div>
{{ achievement.progress }} <div class="achi-pre">
<div class="achi-pre-icon">
<v-icon
v-if="!achievement.isCompleted"
color="var(--tgc-blue-3)"
@click="setAchi(achievement, true)"
style="cursor: pointer"
>
mdi-circle
</v-icon>
<v-icon
v-else
class="achievement-finish"
style="cursor: pointer"
@click="setAchi(achievement, false)"
>
<img alt="finish" src="/source/UI/finish.webp" />
</v-icon>
</div> </div>
<v-list-item> <div class="achi-pre-info">
<template #prepend> <span
<v-icon >{{ achievement.name }}
v-if="!achievement.isCompleted" <span>v{{ achievement.version }}</span>
color="var(--tgc-blue-3)" </span>
@click="setAchi(achievement, true)" <span>{{ achievement.description }}</span>
style="cursor: pointer" </div>
> </div>
mdi-circle <div class="achi-append">
</v-icon> <span v-show="achievement.isCompleted">
<v-icon {{ achievement.completedTime }}
v-else </span>
class="achievement-finish" <div class="achi-append-icon">
style="cursor: pointer" <img alt="icon" src="/icon/material/201.webp" />
@click="setAchi(achievement, false)" <span>{{ achievement.reward }}</span>
> </div>
<img alt="finish" src="/source/UI/finish.webp" /> </div>
</v-icon>
</template>
<v-list-item-title>
{{ achievement.name }}
<span class="version-icon-single">v{{ achievement.version }}</span>
</v-list-item-title>
<v-list-item-subtitle>{{ achievement.description }}</v-list-item-subtitle>
<template #append>
<span v-show="achievement.isCompleted" class="right-time">{{
achievement.completedTime
}}</span>
<v-card class="reward-card">
<v-img src="/icon/material/201.webp" sizes="32" />
<div class="reward-num">
<span>{{ achievement.reward }}</span>
</div>
</v-card>
</template>
</v-list-item>
</v-list>
</div> </div>
</div> </div>
</div> </div>
@@ -158,18 +143,6 @@ const renderSelect = computed(() => {
} }
return selectedAchievement.value; return selectedAchievement.value;
}); });
const renderAchievement = computed(() => {
return renderSelect.value.slice(start.value, start.value + itemCount.value + 1);
});
// virtual list
const start = ref<number>(0);
const itemCount = computed(() => {
return Math.ceil((window.innerHeight - 100) / 76);
});
const emptyHeight = computed(() => {
return renderSelect.value.length * 76;
});
const translateY = ref<string>("0px");
// render // render
const search = ref<string>(""); const search = ref<string>("");
const hideFin = ref<boolean>(false); const hideFin = ref<boolean>(false);
@@ -229,43 +202,6 @@ onMounted(async () => {
} }
}); });
function handleScroll(e: Event): void {
const target: HTMLElement = <HTMLElement>e.target;
// 如果 scrollTop 到底部了
if (target.scrollTop + target.offsetHeight >= target.scrollHeight) {
// 如果 selectedAchievement 的长度小于 itemCount不进行偏移
if (renderSelect.value.length <= itemCount.value) {
window.requestAnimationFrame(() => {
start.value = 0;
translateY.value = "0px";
});
return;
}
window.requestAnimationFrame(() => {
start.value = renderSelect.value.length - itemCount.value;
translateY.value = `${(renderSelect.value.length - itemCount.value) * 76}px`;
});
return;
}
const scrollTop = target.scrollTop;
if (selectedSeries.value !== 0 && selectedSeries.value !== 17 && selectedSeries.value !== -1) {
window.requestAnimationFrame(() => {
if (scrollTop < 86.8) {
start.value = 0;
translateY.value = "0px";
} else {
start.value = Math.floor((scrollTop - 86.8) / 76);
translateY.value = `${scrollTop - 86.8}px`;
}
});
} else {
window.requestAnimationFrame(() => {
start.value = Math.floor(scrollTop / 76);
translateY.value = `${scrollTop}px`;
});
}
}
// 渲染选中的成就系列 // 渲染选中的成就系列
async function selectSeries(index: number): Promise<void> { async function selectSeries(index: number): Promise<void> {
// 如果选中的是已经选中的系列,则不进行操作 // 如果选中的是已经选中的系列,则不进行操作
@@ -471,7 +407,7 @@ async function setAchi(
newAchievement.isCompleted = 0; newAchievement.isCompleted = 0;
newAchievement.completedTime = ""; newAchievement.completedTime = "";
} }
renderAchievement.value[renderAchievement.value.findIndex((item) => item.id === achievement.id)] = renderSelect.value[renderSelect.value.findIndex((item) => item.id === achievement.id)] =
newAchievement; newAchievement;
await setAchiDB(newAchievement); await setAchiDB(newAchievement);
await flushOverview(); await flushOverview();
@@ -503,7 +439,8 @@ async function getSeriesData(series?: number): Promise<TGApp.Sqlite.Achievement.
if (series) { if (series) {
sql = `SELECT * sql = `SELECT *
FROM AchievementSeries FROM AchievementSeries
WHERE id = ${series};`; WHERE id = ${series}
ORDER BY \`order\`;`;
} }
return await db.select(sql); return await db.select(sql);
} }
@@ -557,7 +494,7 @@ async function setAchiDB(achievement: TGApp.Sqlite.Achievement.SingleTable): Pro
await db.execute(sql); await db.execute(sql);
} }
</script> </script>
<!-- 顶部栏跟 wrap 大概布局 -->
<style lang="css" scoped> <style lang="css" scoped>
.top-bar { .top-bar {
display: flex; display: flex;
@@ -599,25 +536,43 @@ async function setAchiDB(achievement: TGApp.Sqlite.Achievement.SingleTable): Pro
/* 左侧系列 */ /* 左侧系列 */
.left-wrap { .left-wrap {
display: flex;
width: 400px; width: 400px;
height: 100%; height: 100%;
flex-direction: column;
padding-right: 10px;
overflow-y: auto; overflow-y: auto;
row-gap: 10px;
} }
/* 右侧成就 */ /* 右侧成就 */
.right-wrap { .right-wrap {
display: flex;
width: 100%; width: 100%;
height: 100%; height: 100%;
flex-direction: column;
padding-right: 10px;
overflow-y: auto; overflow-y: auto;
row-gap: 10px;
} }
</style>
.list-empty { <!-- 左侧成就系列 wrap -->
<style lang="css" scoped>
.card-series {
position: relative; position: relative;
width: 100%; display: flex;
align-items: center;
justify-content: center;
padding: 10px;
border-radius: 10px;
background: var(--box-bg-1);
color: var(--box-text-1);
column-gap: 10px;
cursor: pointer;
} }
/* 版本信息 */ /* 版本信息 */
.version-icon-series { .series-version {
position: absolute; position: absolute;
right: 0; right: 0;
bottom: 0; bottom: 0;
@@ -637,43 +592,63 @@ async function setAchiDB(achievement: TGApp.Sqlite.Achievement.SingleTable): Pro
height: 40px; height: 40px;
padding: 5px; padding: 5px;
border-radius: 5px; border-radius: 5px;
margin-right: 10px;
background: background:
linear-gradient(to bottom, rgb(255 255 255 / 15%) 0%, rgb(0 0 0 / 15%) 100%), linear-gradient(to bottom, rgb(255 255 255 / 15%) 0%, rgb(0 0 0 / 15%) 100%),
radial-gradient(at top center, rgb(255 255 255 / 40%) 0%, rgb(0 0 0 / 40%) 120%) #989898; radial-gradient(at top center, rgb(255 255 255 / 40%) 0%, rgb(0 0 0 / 40%) 120%) #989898;
background-blend-mode: multiply, multiply; background-blend-mode: multiply, multiply;
} }
.version-icon-single { .series-content {
color: var(--tgc-pink-1); display: flex;
font-family: var(--font-title); width: 100%;
font-size: 10px; flex-flow: column wrap;
text-align: center; align-items: flex-start;
justify-content: center;
text-align: left;
} }
.card-left { .series-content :nth-child(1) {
border-radius: 10px; font-size: 14px;
margin-right: 10px; font-weight: bold;
margin-bottom: 10px;
background: var(--box-bg-1);
color: var(--box-text-1);
cursor: pointer;
} }
.series-content :nth-child(2) {
font-size: 12px;
opacity: 0.8;
}
</style>
<!-- 右侧成就 -->
<style lang="css" scoped>
/* 成就卡片 */ /* 成就卡片 */
.card-right { .achi-series {
position: relative;
width: 100%;
height: 80px;
border: 1px solid var(--common-shadow-2);
border-radius: 10px 50px 50px 10px;
background-position: right;
background-repeat: no-repeat;
cursor: pointer;
font-family: var(--font-title);
}
.card-achi {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px;
border-radius: 10px; border-radius: 10px;
margin: 10px;
background: var(--box-bg-1); background: var(--box-bg-1);
color: var(--box-text-7); color: var(--box-text-7);
} }
/* 成就进度 */ /* 成就进度 */
.achievement-progress { .achi-progress {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 65px; width: 50px;
border-right: 1px solid var(--common-shadow-1); border-right: 1px solid var(--common-shadow-1);
border-bottom: 1px solid var(--common-shadow-1); border-bottom: 1px solid var(--common-shadow-1);
background: var(--box-bg-2); background: var(--box-bg-2);
@@ -684,30 +659,42 @@ async function setAchiDB(achievement: TGApp.Sqlite.Achievement.SingleTable): Pro
text-align: center; text-align: center;
} }
.achi-pre {
display: flex;
align-items: center;
justify-content: flex-start;
column-gap: 10px;
}
.achi-pre-icon {
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
}
.achievement-finish img { .achievement-finish img {
width: 30px; width: 30px;
height: 30px; height: 30px;
filter: invert(51%) sepia(100%) saturate(353%) hue-rotate(42deg) brightness(107%) contrast(91%); filter: invert(51%) sepia(100%) saturate(353%) hue-rotate(42deg) brightness(107%) contrast(91%);
} }
/* 成就完成时间 */ .achi-pre-info {
.right-time { display: flex;
margin-right: 10px; width: 100%;
color: var(--box-text-4); flex-flow: column wrap;
font-size: small; align-items: flex-start;
justify-content: center;
text-align: left;
} }
/* 成就奖励 */ .achi-pre-info :nth-child(1) {
.reward-card { font-family: var(--font-title);
position: relative; font-size: 14px;
width: 40px;
height: 40px;
border-radius: 5px;
background-image: url("/icon/bg/5-Star.webp");
background-size: cover;
} }
.reward-num { .achi-append-icon span {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
@@ -720,4 +707,41 @@ async function setAchiDB(achievement: TGApp.Sqlite.Achievement.SingleTable): Pro
color: var(--tgc-white-1); color: var(--tgc-white-1);
font-size: 8px; font-size: 8px;
} }
.achi-pre-info :nth-child(1) span {
color: var(--tgc-pink-1);
font-size: 12px;
}
.achi-pre-info :nth-child(2) {
font-size: 12px;
opacity: 0.8;
}
.achi-append {
display: flex;
align-items: center;
justify-content: flex-end;
column-gap: 10px;
}
.achi-append :nth-last-child(2) {
margin-right: 10px;
color: var(--box-text-4);
font-size: small;
}
.achi-append-icon {
position: relative;
width: 40px;
height: 40px;
border-radius: 5px;
background-image: url("/icon/bg/5-Star.webp");
background-size: cover;
}
.achi-append-icon img {
width: 40px;
height: 40px;
}
</style> </style>