Skip to content

Commit 84875b9

Browse files
committed
✨ feat: 优化虚拟列表
1 parent da96327 commit 84875b9

10 files changed

Lines changed: 461 additions & 71 deletions

File tree

components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,5 +163,6 @@ declare module 'vue' {
163163
UpdatePlaylist: typeof import('./src/components/Modal/UpdatePlaylist.vue')['default']
164164
User: typeof import('./src/components/Layout/User.vue')['default']
165165
UserAgreement: typeof import('./src/components/Modal/UserAgreement.vue')['default']
166+
VirtualScroll: typeof import('./src/components/UI/VirtualScroll.vue')['default']
166167
}
167168
}

src/components/Card/SongCard.vue

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
@update:show="localCover"
2929
/>
3030
<!-- 信息 -->
31-
<div class="info">
31+
<n-flex size="small" class="info" vertical >
3232
<!-- 名称 -->
3333
<div class="name">
3434
<n-ellipsis
@@ -40,7 +40,10 @@
4040
class="name-text"
4141
>
4242
{{ song?.name || "未知曲目" }}
43+
<n-text v-if="song.alia?.length" class="alia" depth="3"> ({{ song.alia }}) </n-text>
4344
</n-ellipsis>
45+
</div>
46+
<n-flex :size="4" :wrap="false" class="desc" align="center">
4447
<!-- 音质 -->
4548
<n-tag
4649
v-if="song?.quality && settingStore.showSongQuality"
@@ -86,27 +89,25 @@
8689
>
8790
MV
8891
</n-tag>
89-
</div>
90-
<!-- 歌手 -->
91-
<div v-if="Array.isArray(song.artists)" class="artists text-hidden">
92-
<n-text
93-
v-for="ar in song.artists"
94-
:key="ar.id"
95-
class="ar"
96-
@click="openJumpArtist(song.artists)"
97-
>
98-
{{ ar.name }}
99-
</n-text>
100-
</div>
101-
<div v-else-if="song.type === 'radio'" class="artists">
102-
<n-text class="ar"> 电台节目 </n-text>
103-
</div>
104-
<div v-else class="artists text-hidden" @click="openJumpArtist(song.artists)">
105-
<n-text class="ar"> {{ song.artists || "未知艺术家" }} </n-text>
106-
</div>
107-
<!-- 别名 -->
108-
<n-text v-if="song.alia" class="alia text-hidden" depth="3">{{ song.alia }}</n-text>
109-
</div>
92+
<!-- 歌手 -->
93+
<div v-if="Array.isArray(song.artists)" class="artists text-hidden">
94+
<n-text
95+
v-for="ar in song.artists"
96+
:key="ar.id"
97+
class="ar"
98+
@click="openJumpArtist(song.artists)"
99+
>
100+
{{ ar.name }}
101+
</n-text>
102+
</div>
103+
<div v-else-if="song.type === 'radio'" class="artists">
104+
<n-text class="ar"> 电台节目 </n-text>
105+
</div>
106+
<div v-else class="artists text-hidden" @click="openJumpArtist(song.artists)">
107+
<n-text class="ar"> {{ song.artists || "未知艺术家" }} </n-text>
108+
</div>
109+
</n-flex>
110+
</n-flex>
110111
</div>
111112
<!-- 专辑 -->
112113
<div v-if="song.type !== 'radio' && !hiddenAlbum" class="album text-hidden">
@@ -230,7 +231,7 @@ const localCover = async (show: boolean) => {
230231

231232
<style lang="scss" scoped>
232233
.song-card {
233-
height: 100%;
234+
height: 90px;
234235
cursor: pointer;
235236
.song-content {
236237
display: flex;
@@ -242,10 +243,10 @@ const localCover = async (show: boolean) => {
242243
border-radius: 12px;
243244
border: 2px solid rgba(var(--primary), 0.12);
244245
background-color: var(--surface-container-hex);
245-
// transition:
246-
// transform 0.1s,
247-
// background-color 0.3s var(--n-bezier),
248-
// border-color 0.3s var(--n-bezier);
246+
transition:
247+
transform 0.1s,
248+
background-color 0.3s var(--n-bezier),
249+
border-color 0.3s var(--n-bezier);
249250
&.play {
250251
border-color: rgba(var(--primary), 0.58);
251252
background-color: rgba(var(--primary), 0.28);
@@ -325,21 +326,20 @@ const localCover = async (show: boolean) => {
325326
overflow: hidden;
326327
}
327328
.info {
328-
display: flex;
329-
flex-direction: column;
330329
.name {
331330
display: flex;
332331
flex-direction: row;
333332
align-items: center;
333+
line-height: normal;
334334
font-size: 16px;
335-
:deep(.name-text) {
336-
margin-right: 6px;
337-
}
335+
}
336+
.desc {
337+
margin-top: 2px;
338+
font-size: 13px;
338339
.n-tag {
339-
--n-height: 20px;
340-
font-size: 12px;
340+
--n-height: 18px;
341+
font-size: 10px;
341342
cursor: pointer;
342-
margin-right: 6px;
343343
pointer-events: none;
344344
&:last-child {
345345
margin-right: 0;
@@ -366,8 +366,6 @@ const localCover = async (show: boolean) => {
366366
}
367367
}
368368
.artists {
369-
margin-top: 2px;
370-
font-size: 13px;
371369
.ar {
372370
display: inline-flex;
373371
transition: opacity 0.3s;
@@ -387,11 +385,6 @@ const localCover = async (show: boolean) => {
387385
}
388386
}
389387
}
390-
.alia {
391-
margin-top: 2px;
392-
font-size: 12px;
393-
opacity: 0.8;
394-
}
395388
}
396389
.sort {
397390
margin-left: 6px;

src/components/List/ListDetail.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ const handleTabChange = (value: "songs" | "comments") => {
303303
display: flex;
304304
height: 240px;
305305
width: 100%;
306-
padding: 12px 0 30px 0;
306+
padding: 12px 0 24px 0;
307307
will-change: height, opacity;
308308
z-index: 1;
309309
transition:

src/components/List/SongList.vue

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<div v-if="!isEmpty(listData)" ref="songListRef" class="song-list">
55
<Transition name="fade" mode="out-in">
66
<div
7-
:key="listKey"
7+
:key="listKey + '_' + statusStore.listSort"
88
:style="{ height: height === 'auto' ? 'auto' : `${height || songListHeight}px` }"
99
class="virtual-list-wrapper"
1010
>
@@ -34,13 +34,13 @@
3434
<n-text v-if="data?.[0].size && !hiddenSize" class="meta size">大小</n-text>
3535
</div>
3636
<!-- 虚拟列表 -->
37-
<n-virtual-list
37+
<VirtualScroll
3838
ref="listRef"
39-
:item-size="94"
39+
:item-height="90"
40+
:item-fixed="true"
4041
:items="virtualListItems"
41-
:style="{ height: `calc(100% - 40px)` }"
42+
:height="`calc(100% - 40px)`"
4243
:padding-bottom="80"
43-
item-resizable
4444
@scroll="onScroll"
4545
>
4646
<template #default="{ item, index }">
@@ -77,7 +77,7 @@
7777
<n-divider v-else dashed> 没有更多啦 ~ </n-divider>
7878
</div>
7979
</template>
80-
</n-virtual-list>
80+
</VirtualScroll>
8181
</div>
8282
</Transition>
8383
<!-- 右键菜单 -->
@@ -108,14 +108,15 @@
108108
</template>
109109

110110
<script setup lang="ts">
111-
import type { DropdownOption, VirtualListInst } from "naive-ui";
111+
import type { DropdownOption } from "naive-ui";
112112
import { SongType, SortType } from "@/types/main";
113113
import { useMusicStore, useStatusStore } from "@/stores";
114114
import { entries, isEmpty } from "lodash-es";
115115
import { sortOptions } from "@/utils/meta";
116116
import { renderIcon } from "@/utils/helper";
117117
import { usePlayerController } from "@/core/player/PlayerController";
118118
import SongListMenu from "@/components/Menu/SongListMenu.vue";
119+
import VirtualScroll from "@/components/UI/VirtualScroll.vue";
119120
120121
const props = withDefaults(
121122
defineProps<{
@@ -177,7 +178,7 @@ const scrollTop = ref<number>(0);
177178
const scrollIndex = ref<number>(0);
178179
179180
// 列表元素
180-
const listRef = ref<VirtualListInst | null>(null);
181+
const listRef = ref<InstanceType<typeof VirtualScroll> | null>(null);
181182
const songListRef = ref<HTMLElement | null>(null);
182183
183184
// 悬浮工具
@@ -286,7 +287,7 @@ const onScroll = (e: Event) => {
286287
const target = e.target as HTMLElement;
287288
const top = target.scrollTop;
288289
scrollTop.value = top;
289-
scrollIndex.value = Math.floor(top / 94);
290+
scrollIndex.value = Math.floor(top / 90);
290291
291292
// 触底检测
292293
const scrollHeight = target.scrollHeight;
@@ -298,13 +299,13 @@ const onScroll = (e: Event) => {
298299
299300
// 滚动到顶部
300301
const scrollToTop = () => {
301-
listRef.value?.scrollTo({ index: 0 });
302+
listRef.value?.scrollToIndex(0);
302303
};
303304
304305
// 滚动到当前播放歌曲
305306
const scrollToCurrentSong = () => {
306307
if (hasPlaySong.value >= 0) {
307-
listRef.value?.scrollTo({ index: hasPlaySong.value });
308+
listRef.value?.scrollToIndex(hasPlaySong.value);
308309
}
309310
};
310311
@@ -321,9 +322,9 @@ const sortSelect = (key: SortType) => {
321322
// 滚动到当前播放歌曲或顶部
322323
nextTick(() => {
323324
if (hasPlaySong.value >= 0) {
324-
listRef.value?.scrollTo({ index: hasPlaySong.value });
325+
listRef.value?.scrollToIndex(hasPlaySong.value);
325326
} else {
326-
listRef.value?.scrollTo({ index: 0 });
327+
listRef.value?.scrollToIndex(0);
327328
}
328329
});
329330
};
@@ -341,7 +342,7 @@ onActivated(() => {
341342
if (props.height === "auto") stopCalcHeight();
342343
if (scrollIndex.value > 0) {
343344
nextTick(() => {
344-
listRef.value?.scrollTo({ index: scrollIndex.value, behavior: "auto" });
345+
listRef.value?.scrollToIndex(scrollIndex.value);
345346
});
346347
}
347348
});
@@ -446,6 +447,10 @@ onBeforeUnmount(() => {
446447
.virtual-list-wrapper {
447448
height: 100%;
448449
position: relative;
450+
transition:
451+
height 0.3s,
452+
transform 0.3s,
453+
opacity 0.3s;
449454
.sticky-header {
450455
position: sticky;
451456
top: 0;

src/components/Player/MainAMLyric.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ import { type LyricLine } from "@applemusic-like-lyrics/core";
4646
import { useMusicStore, useSettingStore, useStatusStore } from "@/stores";
4747
import { getLyricLanguage } from "@/utils/format";
4848
import { usePlayerController } from "@/core/player/PlayerController";
49-
import "@applemusic-like-lyrics/core/style.css";
5049
import { cloneDeep } from "lodash-es";
50+
import "@applemusic-like-lyrics/core/style.css";
5151
5252
const musicStore = useMusicStore();
5353
const statusStore = useStatusStore();
@@ -150,7 +150,7 @@ onBeforeUnmount(() => {
150150
top: 0;
151151
padding-left: 10px;
152152
padding-right: 80px;
153-
--amll-lyric-view-color: rgb(239, 239, 239);
153+
--amll-lp-color: rgb(239, 239, 239);
154154
// margin-left: -2rem;
155155
}
156156

src/components/Player/MainPlayList.vue

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@
1616
</template>
1717
<Transition name="fade" mode="out-in">
1818
<!-- 播放列表 -->
19-
<n-virtual-list
19+
<VirtualScroll
2020
v-if="dataStore.playList.length"
2121
ref="playListRef"
22-
:item-size="80"
22+
:item-height="80"
23+
:item-fixed="true"
2324
:items="playListData"
2425
:default-scroll-index="statusStore.playIndex"
2526
class="playlist-list"
2627
style="max-height: calc(100vh - 142px)"
28+
:height="`calc(100vh - 142px)`"
2729
>
2830
<template #default="{ item: songData, index }">
2931
<div
@@ -68,7 +70,7 @@
6870
</div>
6971
</div>
7072
</template>
71-
</n-virtual-list>
73+
</VirtualScroll>
7274
<n-empty
7375
v-else
7476
description="播放列表暂无歌曲,快去添加吧"
@@ -109,14 +111,14 @@
109111

110112
<script setup lang="ts">
111113
import { useStatusStore, useDataStore } from "@/stores";
112-
import type { VirtualListInst } from "naive-ui";
114+
import VirtualScroll from "@/components/UI/VirtualScroll.vue";
113115
import { usePlayerController } from "@/core/player/PlayerController";
114116
115117
const dataStore = useDataStore();
116118
const statusStore = useStatusStore();
117119
const player = usePlayerController();
118120
119-
const playListRef = ref<VirtualListInst | null>(null);
121+
const playListRef = ref<InstanceType<typeof VirtualScroll> | null>(null);
120122
121123
// 播放列表数据
122124
const playListData = computed(() => {
@@ -130,7 +132,7 @@ const playListData = computed(() => {
130132
131133
// 滚动至指定元素
132134
const scrollToItem = (index: number) => {
133-
playListRef.value?.scrollTo({ index });
135+
playListRef.value?.scrollToIndex(index);
134136
};
135137
136138
// 清空播放列表
@@ -261,6 +263,11 @@ const cleanPlayList = () => {
261263
padding: 0;
262264
height: 100%;
263265
}
266+
.custom-virtual-list {
267+
.n-scrollbar-content {
268+
height: auto;
269+
}
270+
}
264271
.n-drawer-footer {
265272
height: 72px;
266273
padding: 16px;

0 commit comments

Comments
 (0)