Skip to content

Commit 2f46f15

Browse files
committed
feat: 新增视频卡片图片加载限制的相关设置,默认并发4,可以关闭
1 parent 358896b commit 2f46f15

File tree

9 files changed

+87
-29
lines changed

9 files changed

+87
-29
lines changed

src/_locales/cmn-CN.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,10 @@ settings:
278278
grid_columns: 显示
279279
grid_columns_unit:
280280
grid_add_breakpoint: 添加断点
281+
enable_image_load_concurrency_limit: 启用图片加载并发限制
282+
enable_image_load_concurrency_limit_desc: 限制同时加载的图片数量,减少页面卡顿
283+
image_load_concurrency_count: 图片加载并发数
284+
image_load_concurrency_count_desc: 同时加载图片的最大数量,可根据设备性能调整
281285

282286
home_adaptive_title_auto_size: 标题字号自适应
283287
home_adaptive_title_auto_size_desc: 根据网格宽度自动调整标题字号

src/_locales/cmn-TW.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ settings:
190190
grid_columns: 顯示
191191
grid_columns_unit:
192192
grid_add_breakpoint: 新增斷點
193+
enable_image_load_concurrency_limit: 啟用圖片載入並行限制
194+
enable_image_load_concurrency_limit_desc: 限制同時載入的圖片數量,減少頁面卡頓
195+
image_load_concurrency_count: 圖片載入並行數
196+
image_load_concurrency_count_desc: 同時載入圖片的最大數量,可依裝置效能調整
193197

194198
home_adaptive_title_auto_size: 標題字級自適應
195199
home_adaptive_title_auto_size_desc: 依據網格寬度自動調整標題字級

src/_locales/en.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,10 @@ settings:
247247
grid_columns: Show
248248
grid_columns_unit: cols
249249
grid_add_breakpoint: Add Breakpoint
250+
enable_image_load_concurrency_limit: Enable image load concurrency limit
251+
enable_image_load_concurrency_limit_desc: Limit the number of images loading simultaneously to reduce page lag
252+
image_load_concurrency_count: Image load concurrency
253+
image_load_concurrency_count_desc: Maximum number of images to load at once, adjust based on device performance
250254

251255
home_adaptive_title_auto_size: Auto adjust title font size
252256
home_adaptive_title_auto_size_desc: Adjusts title font size based on the grid width

src/_locales/jyut.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ settings:
190190
grid_columns: 顯示
191191
grid_columns_unit:
192192
grid_add_breakpoint: 加斷點
193+
enable_image_load_concurrency_limit: 啟用圖片載入並行限制
194+
enable_image_load_concurrency_limit_desc: 限制同時載入嘅圖片數量,減少頁面窒
195+
image_load_concurrency_count: 圖片載入並行數
196+
image_load_concurrency_count_desc: 同時載入圖片嘅最大數量,可以根據裝置效能調整
193197

194198
home_adaptive_title_auto_size: 標題字體大小自動
195199
home_adaptive_title_auto_size_desc: 根據網格闊度自動調整標題字體大小

src/components/LazyPicture.vue

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
* 优化的懒加载图片组件
44
* 使用 Intersection Observer API 实现精确的懒加载控制
55
* 只在图片即将进入视口时才开始加载,减少不必要的网络请求
6-
* 通过全局队列限制并发加载数量
6+
* 通过全局队列限制并发加载数量(可在设置中开关)
77
*/
88
99
import { useGlobalScrollState } from '~/composables/useGlobalScrollState'
1010
import { enqueueImageLoad } from '~/composables/useImageLoadQueue'
11+
import { settings } from '~/logic'
1112
1213
interface Props {
1314
src: string
@@ -50,6 +51,12 @@ function isInViewport(): boolean {
5051
return rect.top < window.innerHeight && rect.bottom > 0
5152
}
5253
54+
// 直接加载图片(不经过队列)
55+
function loadDirectly() {
56+
isVisible.value = true
57+
actualSrc.value = props.src
58+
}
59+
5360
// 开始加载图片(由队列调度)
5461
function startLoad() {
5562
// 再次检查是否在视口附近,不在则跳过
@@ -83,12 +90,17 @@ if (props.loading === 'eager') {
8390
// 使用全局共享的滚动状态
8491
const { isScrolling } = useGlobalScrollState()
8592
86-
// 监听滚动停止,将待加载的图片加入队列
93+
// 监听滚动停止,将待加载的图片加入队列或直接加载
8794
watch(isScrolling, (scrolling) => {
8895
if (!scrolling && pendingLoad.value && !isVisible.value && !queueHandle) {
8996
pendingLoad.value = false
90-
// 视口内的图片优先加载
91-
queueHandle = enqueueImageLoad(startLoad, isInViewport())
97+
if (settings.value.enableImageLoadConcurrencyLimit) {
98+
// 视口内的图片优先加载
99+
queueHandle = enqueueImageLoad(startLoad, isInViewport())
100+
}
101+
else {
102+
loadDirectly()
103+
}
92104
}
93105
})
94106
@@ -103,10 +115,13 @@ onMounted(() => {
103115
if (isScrolling.value) {
104116
pendingLoad.value = true
105117
}
106-
else {
118+
else if (settings.value.enableImageLoadConcurrencyLimit) {
107119
// 视口内的图片优先加载
108120
queueHandle = enqueueImageLoad(startLoad, isInViewport())
109121
}
122+
else {
123+
loadDirectly()
124+
}
110125
observer?.disconnect()
111126
}
112127
})

src/components/Settings/PluginComponentsAndPages/VideoCard/VideoCard.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,24 @@ function resetColumns() {
122122
</div>
123123
</template>
124124
</SettingsItem>
125+
126+
<SettingsItem :title="$t('settings.enable_image_load_concurrency_limit')" :desc="$t('settings.enable_image_load_concurrency_limit_desc')">
127+
<Radio v-model="settings.enableImageLoadConcurrencyLimit" />
128+
</SettingsItem>
129+
130+
<SettingsItem
131+
v-if="settings.enableImageLoadConcurrencyLimit"
132+
:title="$t('settings.image_load_concurrency_count')"
133+
:desc="$t('settings.image_load_concurrency_count_desc')"
134+
>
135+
<Input
136+
v-model="settings.imageLoadConcurrencyCount"
137+
type="number"
138+
:min="1"
139+
:max="6"
140+
w-20
141+
/>
142+
</SettingsItem>
125143
</SettingsItemGroup>
126144

127145
<SettingsItemGroup :title="$t('settings.group_video_card')">

src/components/VideoCardGrid.vue

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,8 @@ const gridContainerStyle = computed(() => ({
401401
}))
402402
403403
// 是否显示初始骨架屏(数据量不足阈值且还有更多内容时)
404-
const showInitialSkeleton = computed(() => {
404+
// 改为计算是否需要填充骨架屏,而不是完全切换
405+
const needSkeletonPadding = computed(() => {
405406
if (props.needToLoginFirst)
406407
return false
407408
if (props.noMoreContent)
@@ -411,14 +412,18 @@ const showInitialSkeleton = computed(() => {
411412
return true
412413
})
413414
414-
// 生成初始骨架屏数据(仅用于首次加载
415-
const initialSkeletonItems = computed(() => {
416-
if (!showInitialSkeleton.value)
415+
// 生成填充骨架屏数据(填补到最小渲染数量
416+
const paddingSkeletonItems = computed(() => {
417+
if (!needSkeletonPadding.value)
417418
return []
418419
419-
return Array.from({ length: dynamicSkeletonCount.value }, (_, i) => ({
420+
const currentCount = props.items.length
421+
const targetCount = dynamicSkeletonCount.value
422+
const paddingCount = Math.max(0, targetCount - currentCount)
423+
424+
return Array.from({ length: paddingCount }, (_, i) => ({
420425
_isSkeleton: true,
421-
_skeletonId: `skeleton-init-${i}`,
426+
_skeletonId: `skeleton-padding-${i}`,
422427
})) as T[]
423428
})
424429
@@ -443,6 +448,11 @@ const loadingMoreSkeletonItems = computed(() => {
443448
444449
// 合并实际数据和骨架屏
445450
const displayItems = computed(() => {
451+
// 数据不足时:数据 + 填充骨架屏
452+
if (needSkeletonPadding.value) {
453+
return [...props.items, ...paddingSkeletonItems.value]
454+
}
455+
446456
// 加载更多时:数据 + 骨架屏
447457
if (isLoadingMore.value) {
448458
return [...props.items, ...loadingMoreSkeletonItems.value]
@@ -616,22 +626,8 @@ function getUniqueKey(item: T, index: number): string | number {
616626
m="b-0 t-0" relative w-full
617627
:style="gridContainerStyle"
618628
>
619-
<!-- 初始加载骨架屏 -->
620-
<div v-if="showInitialSkeleton" :class="gridClass">
621-
<VideoCard
622-
v-for="item in initialSkeletonItems"
623-
:key="(item as any)._skeletonId"
624-
skeleton
625-
:horizontal="isHorizontal"
626-
>
627-
<template v-for="(_, name) in $slots" #[name]>
628-
<slot :name="name" :item="item" />
629-
</template>
630-
</VideoCard>
631-
</div>
632-
633-
<!-- Grid 内容 -->
634-
<div v-else :class="gridClass">
629+
<!-- Grid 内容(统一渲染,避免 v-if/v-else 切换导致的滚动跳动) -->
630+
<div :class="gridClass">
635631
<VideoCard
636632
v-for="(item, index) in displayItems"
637633
:key="getUniqueKey(item, index)"

src/composables/useImageLoadQueue.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
* 全局图片加载队列(单例模式)
33
* 限制同时加载的图片数量,避免并发过高
44
* 支持优先级:视口内的图片优先加载
5+
* 并发数可通过设置调整
56
*/
67

8+
import { settings } from '~/logic'
9+
710
type LoadCallback = () => void
811

912
interface QueueItem {
@@ -14,10 +17,15 @@ interface QueueItem {
1417
// 全局状态(单例)
1518
const queue: QueueItem[] = []
1619
const loading = new Set<symbol>()
17-
const MAX_CONCURRENT = 4
20+
21+
function getMaxConcurrent(): number {
22+
const count = settings.value.imageLoadConcurrencyCount
23+
return Math.max(1, Math.min(6, count || 4))
24+
}
1825

1926
function processQueue() {
20-
while (loading.size < MAX_CONCURRENT && queue.length > 0) {
27+
const maxConcurrent = getMaxConcurrent()
28+
while (loading.size < maxConcurrent && queue.length > 0) {
2129
const item = queue.shift()
2230
if (item) {
2331
loading.add(item.id)

src/logic/storage.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ export interface Settings {
267267
// Video card shadow customization (modern layout only)
268268
videoCardShadowCurve: ShadowCurvePoint[]
269269
videoCardShadowHeight: number // 1.0-3.0
270+
// Image load concurrency limit
271+
enableImageLoadConcurrencyLimit: boolean
272+
imageLoadConcurrencyCount: number // 1-6
270273
useSearchPageModeOnHomePage: boolean
271274
searchPageModeWallpaperFixed: boolean
272275
preserveForYouState: boolean
@@ -470,6 +473,8 @@ export const originalSettings: Settings = {
470473
{ position: 100, opacity: 0 },
471474
],
472475
videoCardShadowHeight: 1.0,
476+
enableImageLoadConcurrencyLimit: true,
477+
imageLoadConcurrencyCount: 4,
473478
useSearchPageModeOnHomePage: false,
474479
searchPageModeWallpaperFixed: false,
475480
preserveForYouState: false,

0 commit comments

Comments
 (0)