mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Merge branch 'preview-fixes' of https://github.com/the1812/Bilibili-Evolved into preview-fixes
This commit is contained in:
commit
50ae796dff
@ -4,7 +4,7 @@
|
||||
<VideoCard v-for="c of cards" :key="c.id" :data="c" />
|
||||
</div>
|
||||
<VEmpty v-if="!loading && cards.length === 0" />
|
||||
<ScrollTrigger v-if="!error" @trigger="loadCards" />
|
||||
<ScrollTrigger v-if="!error" ref="scrollTrigger" detect-viewport @trigger="loadCards" />
|
||||
<MinimalHomeOperations v-if="cards.length > 0" @refresh="refresh" />
|
||||
</div>
|
||||
</template>
|
||||
@ -43,6 +43,7 @@ export default Vue.extend({
|
||||
try {
|
||||
this.error = false
|
||||
this.loading = true
|
||||
this.$refs.scrollTrigger.setLoadState('loading')
|
||||
this.cards = lodash.uniqBy(
|
||||
[...this.cards, ...(await getVideoFeeds('video', this.lastID))],
|
||||
it => it.id,
|
||||
@ -50,12 +51,17 @@ export default Vue.extend({
|
||||
} catch (error) {
|
||||
logError(error)
|
||||
this.error = true
|
||||
this.$refs.scrollTrigger.setLoadState('error')
|
||||
} finally {
|
||||
this.loading = false
|
||||
if (this.loaded) {
|
||||
this.$refs.scrollTrigger.setLoadState('loaded')
|
||||
}
|
||||
}
|
||||
},
|
||||
async refresh() {
|
||||
this.cards = []
|
||||
this.$refs.scrollTrigger.resetIsFirstLoad()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@ -7,23 +7,89 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { useScopedConsole } from '@/core/utils/log'
|
||||
import { delay } from '@/core/utils/'
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
VLoading: () => import('./VLoading.vue').then(m => m.default),
|
||||
},
|
||||
props: {
|
||||
detectViewport: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isFirstLoad: true,
|
||||
isViewportTriggerRunning: false,
|
||||
parentLoadState: 'none',
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const console = useScopedConsole('ScrollTrigger')
|
||||
const element = this.$el as HTMLElement
|
||||
const { visible } = await import('@/core/observer')
|
||||
visible(element, records => {
|
||||
visible(element, async records => {
|
||||
if (records.some(r => r.intersectionRatio > 0)) {
|
||||
console.log('Intersection Observer trigger')
|
||||
this.trigger()
|
||||
|
||||
if (!this.detectViewport && !this.isFirstLoad && this.isViewportTriggerRunning) {
|
||||
return
|
||||
}
|
||||
|
||||
this.isViewportTriggerRunning = true
|
||||
while (this.getVisibleStateByViewport(element)) {
|
||||
await delay(500)
|
||||
|
||||
// 需要父组件配合 setLoadState,不然会一直 continue
|
||||
// 确认父组件加载状态,等待加载完成后再继续执行,否则会出现多次加载的情况
|
||||
if (this.parentLoadState !== 'loaded') {
|
||||
continue
|
||||
}
|
||||
|
||||
// 再次确认元素可视情况,避免当元素已不可视时仍然触发加载
|
||||
if (!this.getVisibleStateByViewport(element)) {
|
||||
break
|
||||
}
|
||||
|
||||
console.log('is first load & viewport trigger')
|
||||
this.trigger()
|
||||
}
|
||||
this.isFirstLoad = false
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 当元素顶部位于视口内部时返回为正数,位于视口外部时返回为负数,正好位于视口底部时返回为零。
|
||||
* @param element HTML 元素
|
||||
* @returns 元素顶部到视口底部的距离
|
||||
*/
|
||||
getElementToViewportBottomDistance(element: HTMLElement): number {
|
||||
return document.documentElement.clientHeight - element.getBoundingClientRect().top
|
||||
},
|
||||
|
||||
/**
|
||||
* 当元素顶部在视口内部时返回 true。
|
||||
* 为避免极端情况,当元素顶部到视口底部的距离大于 -20 时同样返回 true。
|
||||
* @param element HTML 元素
|
||||
* @returns 元素可视情况
|
||||
*/
|
||||
getVisibleStateByViewport(element: HTMLElement): boolean {
|
||||
return Boolean(this.getElementToViewportBottomDistance(element) > -20)
|
||||
},
|
||||
|
||||
setLoadState(loadState: 'loading' | 'error' | 'loaded') {
|
||||
this.parentLoadState = loadState
|
||||
},
|
||||
|
||||
resetIsFirstLoad() {
|
||||
this.isFirstLoad = true
|
||||
},
|
||||
|
||||
trigger() {
|
||||
this.$emit('trigger')
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user