mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
133 lines
3.3 KiB
Vue
133 lines
3.3 KiB
Vue
<template>
|
|
<div
|
|
class="fresh-home-video-list scroll-top scroll-bottom"
|
|
:class="{ 'not-empty': videos.length > 0 }"
|
|
>
|
|
<div ref="content" class="fresh-home-video-list-content">
|
|
<div v-if="videos.length === 0" class="fresh-home-video-list-empty">
|
|
<VLoading v-if="loading" />
|
|
<VEmpty v-else />
|
|
</div>
|
|
<VideoCardWrapper v-for="video of videos" v-else ref="cards" :key="video.id" :data="video" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script lang="ts">
|
|
import type { Ref, PropType } from 'vue'
|
|
import { defineComponent, ref } from 'vue'
|
|
|
|
import type { VideoCard } from '@/components/feeds/video-card'
|
|
import { enableHorizontalScroll } from '@/core/horizontal-scroll'
|
|
import { addComponentListener } from '@/core/settings'
|
|
import { VEmpty, VLoading } from '@/ui'
|
|
|
|
import { cleanUpScrollMask, setupScrollMask } from './scroll-mask'
|
|
import VideoCardWrapper from './VideoCardWrapper.vue'
|
|
|
|
export default defineComponent({
|
|
components: {
|
|
VEmpty,
|
|
VLoading,
|
|
VideoCardWrapper,
|
|
},
|
|
props: {
|
|
videos: {
|
|
type: Array as PropType<VideoCard[]>,
|
|
default: () => [],
|
|
},
|
|
loading: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
},
|
|
setup: () => ({
|
|
content: ref(null) as Ref<HTMLDivElement | null>,
|
|
cards: ref(null) as Ref<InstanceType<typeof VideoCardWrapper>[] | null>,
|
|
}),
|
|
watch: {
|
|
videos: {
|
|
handler() {
|
|
this.setupIntersection()
|
|
},
|
|
deep: true,
|
|
},
|
|
loaded(value) {
|
|
if (value) {
|
|
this.setupIntersection()
|
|
}
|
|
},
|
|
},
|
|
beforeUnmount() {
|
|
cleanUpScrollMask(this.$el)
|
|
},
|
|
mounted() {
|
|
const container = this.content as HTMLElement
|
|
let cancel: () => void
|
|
addComponentListener(
|
|
'freshHome.horizontalWheelScroll',
|
|
(scroll: boolean) => {
|
|
if (scroll) {
|
|
cancel = enableHorizontalScroll(container)
|
|
} else {
|
|
cancel?.()
|
|
}
|
|
},
|
|
true,
|
|
)
|
|
},
|
|
methods: {
|
|
async setupIntersection() {
|
|
await this.$nextTick()
|
|
setupScrollMask({
|
|
container: this.$el,
|
|
items: this.cards.map(c => c.$el),
|
|
})
|
|
},
|
|
offsetPage(offset: number) {
|
|
const container = this.content as HTMLElement
|
|
const style = getComputedStyle(container)
|
|
const containerWidth = container.clientWidth
|
|
const wrapperWidth =
|
|
parseFloat(style.getPropertyValue('--card-width')) +
|
|
parseFloat(style.getPropertyValue('--card-padding'))
|
|
const pageWidth = Math.trunc(containerWidth / wrapperWidth) * wrapperWidth
|
|
container.scrollBy(offset * pageWidth, 0)
|
|
},
|
|
},
|
|
})
|
|
</script>
|
|
<style lang="scss">
|
|
@import 'common';
|
|
@import 'effects';
|
|
|
|
.fresh-home-video-list {
|
|
--card-height: var(--home-content-height);
|
|
--card-width: 200px;
|
|
--card-padding: 12px;
|
|
position: relative;
|
|
display: flex;
|
|
flex: 1 0 0;
|
|
width: 0;
|
|
@include scroll-mask-x(36px, var(--home-base-color));
|
|
|
|
&-content {
|
|
@include h-center();
|
|
@include no-scrollbar();
|
|
overscroll-behavior: initial;
|
|
flex: 1;
|
|
min-height: calc(var(--home-content-height) + var(--card-padding) * 2);
|
|
}
|
|
&-empty {
|
|
margin: var(--card-padding);
|
|
border: 2px dashed #8884;
|
|
border-radius: var(--home-card-radius);
|
|
flex-grow: 1;
|
|
align-self: stretch;
|
|
@include h-center();
|
|
}
|
|
&.not-empty &-content {
|
|
scroll-snap-type: x mandatory;
|
|
}
|
|
}
|
|
</style>
|