Merge branch 'preview-fixes' of https://github.com/the1812/Bilibili-Evolved into preview-fixes

This commit is contained in:
the1812 2023-11-07 23:15:41 +08:00
commit 50ae796dff
2 changed files with 74 additions and 2 deletions

View File

@ -4,7 +4,7 @@
<VideoCard v-for="c of cards" :key="c.id" :data="c" /> <VideoCard v-for="c of cards" :key="c.id" :data="c" />
</div> </div>
<VEmpty v-if="!loading && cards.length === 0" /> <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" /> <MinimalHomeOperations v-if="cards.length > 0" @refresh="refresh" />
</div> </div>
</template> </template>
@ -43,6 +43,7 @@ export default Vue.extend({
try { try {
this.error = false this.error = false
this.loading = true this.loading = true
this.$refs.scrollTrigger.setLoadState('loading')
this.cards = lodash.uniqBy( this.cards = lodash.uniqBy(
[...this.cards, ...(await getVideoFeeds('video', this.lastID))], [...this.cards, ...(await getVideoFeeds('video', this.lastID))],
it => it.id, it => it.id,
@ -50,12 +51,17 @@ export default Vue.extend({
} catch (error) { } catch (error) {
logError(error) logError(error)
this.error = true this.error = true
this.$refs.scrollTrigger.setLoadState('error')
} finally { } finally {
this.loading = false this.loading = false
if (this.loaded) {
this.$refs.scrollTrigger.setLoadState('loaded')
}
} }
}, },
async refresh() { async refresh() {
this.cards = [] this.cards = []
this.$refs.scrollTrigger.resetIsFirstLoad()
}, },
}, },
}) })

View File

@ -7,23 +7,89 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useScopedConsole } from '@/core/utils/log' import { useScopedConsole } from '@/core/utils/log'
import { delay } from '@/core/utils/'
export default Vue.extend({ export default Vue.extend({
components: { components: {
VLoading: () => import('./VLoading.vue').then(m => m.default), 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() { async mounted() {
const console = useScopedConsole('ScrollTrigger') const console = useScopedConsole('ScrollTrigger')
const element = this.$el as HTMLElement const element = this.$el as HTMLElement
const { visible } = await import('@/core/observer') const { visible } = await import('@/core/observer')
visible(element, records => { visible(element, async records => {
if (records.some(r => r.intersectionRatio > 0)) { if (records.some(r => r.intersectionRatio > 0)) {
console.log('Intersection Observer trigger') console.log('Intersection Observer trigger')
this.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: { 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() { trigger() {
this.$emit('trigger') this.$emit('trigger')
}, },