mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Add bangumi timeline
This commit is contained in:
parent
7d9bf609b3
commit
b8460d2681
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
<BangumiTimeline
|
||||
:view-last-week="viewLastWeek"
|
||||
:api="bangumiDataMap[route].api"
|
||||
:api="timelineApi"
|
||||
/>
|
||||
</div>
|
||||
<div class="fresh-home-categories-bangumi-rank-list">
|
||||
@ -55,16 +55,16 @@ import {
|
||||
import SubHeader from '../../../SubHeader.vue'
|
||||
import RankList from './RankList.vue'
|
||||
import BangumiTimeline from './BangumiTimeline.vue'
|
||||
import { RankListCard } from './rank-list-card'
|
||||
import { RankListCard } from './rank-list'
|
||||
|
||||
const bangumiDataMap = {
|
||||
anime: {
|
||||
api: 'https://bangumi.bilibili.com/web_api/timeline_global',
|
||||
timeline: 'global',
|
||||
seasonType: 1,
|
||||
rankingName: 'bangumi',
|
||||
},
|
||||
guochuang: {
|
||||
api: 'https://bangumi.bilibili.com/web_api/timeline_cn',
|
||||
timeline: 'cn',
|
||||
seasonType: 4,
|
||||
// 你永远不知道"国创"在 b 站代码里有多少种叫法...
|
||||
rankingName: 'guochan',
|
||||
@ -86,12 +86,14 @@ export default Vue.extend({
|
||||
},
|
||||
data() {
|
||||
const { route } = this.region.category
|
||||
const { rankingName, seasonType, timeline } = bangumiDataMap[route]
|
||||
return {
|
||||
viewLastWeek: false,
|
||||
bangumiDataMap,
|
||||
route,
|
||||
rankingsApi: `https://api.bilibili.com/pgc/season/rank/web/list?day=3&season_type=${bangumiDataMap[route].seasonType}`,
|
||||
rankingsLink: `https://www.bilibili.com/v/popular/rank/${bangumiDataMap[route].rankingName}`,
|
||||
timelineApi: `https://bangumi.bilibili.com/web_api/timeline_${timeline}`,
|
||||
rankingsApi: `https://api.bilibili.com/pgc/season/rank/web/list?day=3&season_type=${seasonType}`,
|
||||
rankingsLink: `https://www.bilibili.com/v/popular/rank/${rankingName}`,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@ -1,14 +1,73 @@
|
||||
<template>
|
||||
<div class="fresh-home-categories-bangumi-timeline-content">
|
||||
timeline
|
||||
<div
|
||||
class="fresh-home-categories-bangumi-timeline-content"
|
||||
:class="{ loading, loaded, 'last-week': viewLastWeek }"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) of items"
|
||||
:key="item.date_ts"
|
||||
class="fresh-home-categories-bangumi-timeline-item"
|
||||
:class="{ today: index === todayIndex }"
|
||||
>
|
||||
<div class="fresh-home-categories-bangumi-timeline-date">
|
||||
<div
|
||||
class="fresh-home-categories-bangumi-timeline-date-icon"
|
||||
:class="{
|
||||
today: index === todayIndex,
|
||||
['day-of-week-' + item.day_of_week]: true
|
||||
}"
|
||||
/>
|
||||
<div class="fresh-home-categories-bangumi-timeline-date-number">
|
||||
{{ item.date }}
|
||||
</div>
|
||||
<div class="fresh-home-categories-bangumi-timeline-date-text">
|
||||
{{ dayOfWeekText(item) }}
|
||||
</div>
|
||||
<div
|
||||
v-if="index === todayIndex"
|
||||
class="fresh-home-categories-bangumi-timeline-date-today"
|
||||
>
|
||||
TODAY
|
||||
</div>
|
||||
</div>
|
||||
<div class="fresh-home-categories-bangumi-timeline-seasons">
|
||||
<div
|
||||
v-for="season of item.seasons"
|
||||
:key="season.season_id"
|
||||
class="fresh-home-categories-bangumi-timeline-season"
|
||||
:class="{ today: index === todayIndex }"
|
||||
>
|
||||
<div class="fresh-home-categories-bangumi-timeline-season-cover">
|
||||
<DpiImage
|
||||
:src="season.square_cover"
|
||||
:size="80"
|
||||
/>
|
||||
</div>
|
||||
<div class="fresh-home-categories-bangumi-timeline-season-title">
|
||||
{{ season.title }}
|
||||
</div>
|
||||
<div class="fresh-home-categories-bangumi-timeline-season-episode">
|
||||
{{ getEpisode(season) }}
|
||||
</div>
|
||||
<div class="fresh-home-categories-bangumi-timeline-season-time">
|
||||
{{ season.pub_time }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { requestMixin } from './mixin'
|
||||
import { DpiImage } from '@/ui'
|
||||
import { cssVariableMixin, requestMixin } from './mixin'
|
||||
import { rankListCssVars } from './rank-list'
|
||||
|
||||
interface TimelineSeason {
|
||||
cover: string
|
||||
delay: number
|
||||
delay_id: number
|
||||
delay_index: string
|
||||
delay_reason: string
|
||||
ep_id: number
|
||||
favorites: number
|
||||
follow: number
|
||||
@ -30,18 +89,63 @@ interface TimelineDay {
|
||||
seasons: TimelineSeason[]
|
||||
}
|
||||
|
||||
const rankListHeight = rankListCssVars.panelHeight - 2 * rankListCssVars.padding
|
||||
export default Vue.extend({
|
||||
mixins: [requestMixin],
|
||||
components: { DpiImage },
|
||||
mixins: [requestMixin, cssVariableMixin({
|
||||
rankListHeight,
|
||||
})],
|
||||
props: {
|
||||
viewLastWeek: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
todayIndex() {
|
||||
return (this.items as TimelineDay[]).findIndex(it => it.is_today === 1)
|
||||
},
|
||||
pastWeekItems() {
|
||||
return this.items.slice(0, this.todayIndex + 1)
|
||||
},
|
||||
currentWeekItems() {
|
||||
return this.items.slice(this.todayIndex)
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
viewLastWeek() {
|
||||
this.updateScrollPosition()
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.updateScrollPosition()
|
||||
},
|
||||
methods: {
|
||||
parseJson(json: any) {
|
||||
return json.result ?? []
|
||||
},
|
||||
updateScrollPosition() {
|
||||
const element = this.$el as HTMLElement
|
||||
element.scrollTo(0, this.viewLastWeek ? 0 : rankListHeight)
|
||||
},
|
||||
getEpisode(season: TimelineSeason) {
|
||||
if (season.delay) {
|
||||
return `${season.delay_reason}: ${season.delay_index}`
|
||||
}
|
||||
return season.pub_index
|
||||
},
|
||||
dayOfWeekText(item: TimelineDay) {
|
||||
return `周${[
|
||||
'日',
|
||||
'一',
|
||||
'二',
|
||||
'三',
|
||||
'四',
|
||||
'五',
|
||||
'六',
|
||||
'日',
|
||||
][item.day_of_week]}`
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@ -49,8 +153,166 @@ export default Vue.extend({
|
||||
@import "common";
|
||||
|
||||
.fresh-home-categories-bangumi {
|
||||
&-timeline-content {
|
||||
&-timeline {
|
||||
&-content {
|
||||
--season-item-width: 220px;
|
||||
--season-today-width: 250px;
|
||||
--timeline-item-height: 66px;
|
||||
--timeline-today-height: 96px;
|
||||
--timeline-viewport-items-height: calc(
|
||||
6 * var(--timeline-item-height) + var(--timeline-today-height)
|
||||
);
|
||||
--timeline-item-gap: calc(
|
||||
(var(--rank-list-height) - var(--timeline-viewport-items-height)) / 6
|
||||
);
|
||||
--timeline-viewport-height: calc(
|
||||
6 * var(--timeline-item-gap) + var(--timeline-viewport-items-height)
|
||||
);
|
||||
scroll-behavior: smooth;
|
||||
height: var(--timeline-viewport-height);
|
||||
max-height: var(--timeline-viewport-height);
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
@include v-stretch(var(--timeline-item-gap));
|
||||
}
|
||||
&-item {
|
||||
@include h-center(24px);
|
||||
flex-shrink: 0;
|
||||
height: var(--timeline-item-height);
|
||||
&.today {
|
||||
height: var(--timeline-today-height);
|
||||
}
|
||||
}
|
||||
&-date {
|
||||
display: grid;
|
||||
flex-shrink: 0;
|
||||
grid-template: "icon number" 18px "icon text" 22px / 50px auto;
|
||||
gap: 8px;
|
||||
&-icon {
|
||||
grid-area: icon;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
background-image: url('//s1.hdslb.com/bfs/static/bangumi-timeline/asserts/icons.png');
|
||||
filter: brightness(0.5);
|
||||
$icon-width: 50;
|
||||
$icon-height: 48;
|
||||
--scale-factor-x: calc(#{$icon-width} / 38);
|
||||
--scale-factor-y: calc(#{$icon-height} / 36);
|
||||
background-size: calc(247px * var(--scale-factor-x)) calc(663px * var(--scale-factor-y));
|
||||
width: #{$icon-width}px;
|
||||
height: #{$icon-height}px;
|
||||
background-position-x: calc(-146px * var(--scale-factor-x));
|
||||
@for $day from 1 through 7 {
|
||||
&.day-of-week-#{$day} {
|
||||
background-position-y: calc(#{-36 - 72 * ($day - 1)}px * var(--scale-factor-y));
|
||||
}
|
||||
}
|
||||
body.dark & {
|
||||
filter: none;
|
||||
}
|
||||
&.today {
|
||||
--scale-factor-x: 1;
|
||||
--scale-factor-y: 1;
|
||||
background-position-x: -56px;
|
||||
body.dark & {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
&-number {
|
||||
grid-area: number;
|
||||
font-size: 12px;
|
||||
align-self: end;
|
||||
margin-bottom: -6px;
|
||||
}
|
||||
&-text {
|
||||
grid-area: text;
|
||||
@include semi-bold();
|
||||
font-size: 16px;
|
||||
align-self: start;
|
||||
}
|
||||
&-today {
|
||||
grid-column: 1 / 3;
|
||||
font-size: 12px;
|
||||
@include semi-bold();
|
||||
align-self: stretch;
|
||||
justify-self: stretch;
|
||||
@include h-center();
|
||||
justify-content: center;
|
||||
padding: 2px 0;
|
||||
color: #fff;
|
||||
background-color: var(--theme-color);
|
||||
letter-spacing: 1px;
|
||||
line-height: 1.25;
|
||||
border-radius: calc(1.25em + 4px);
|
||||
}
|
||||
}
|
||||
&-seasons {
|
||||
@include h-stretch(var(--timeline-item-gap));
|
||||
@include no-scrollbar();
|
||||
width: 0;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
&-season {
|
||||
@include card(var(--home-card-radius));
|
||||
--cover-size: 50px;
|
||||
flex-shrink: 0;
|
||||
padding: 7px;
|
||||
display: grid;
|
||||
grid-template:
|
||||
"cover title title" 1.2fr
|
||||
"cover time episode" 1fr / var(--cover-size) auto 1fr;
|
||||
row-gap: 4px;
|
||||
column-gap: 12px;
|
||||
justify-content: start;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
width: var(--season-item-width);
|
||||
height: var(--timeline-item-height);
|
||||
|
||||
&-cover {
|
||||
grid-area: cover;
|
||||
width: var(--cover-size);
|
||||
height: var(--cover-size);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
img {
|
||||
width: var(--cover-size);
|
||||
height: var(--cover-size);
|
||||
}
|
||||
}
|
||||
&-title {
|
||||
grid-area: title;
|
||||
@include semi-bold();
|
||||
@include single-line();
|
||||
}
|
||||
&-episode {
|
||||
grid-area: episode;
|
||||
font-size: 12px;
|
||||
@include single-line();
|
||||
}
|
||||
&-time {
|
||||
grid-area: time;
|
||||
@include card(6px);
|
||||
@include semi-bold();
|
||||
font-size: 12px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
&.today {
|
||||
width: var(--season-today-width);
|
||||
height: var(--timeline-today-height);
|
||||
--cover-size: 80px;
|
||||
grid-template:
|
||||
"cover title title" 1.5fr
|
||||
"cover episode episode" 1fr
|
||||
"cover time ." 1fr / var(--cover-size) auto 1fr;
|
||||
row-gap: 8px;
|
||||
.fresh-home-categories-bangumi-timeline-season-cover {
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { RankListCard } from './rank-list-card'
|
||||
import { RankListCard } from './rank-list'
|
||||
import RankList from './RankList.vue'
|
||||
import VideoSlides from './VideoSlides.vue'
|
||||
import SubHeader from '../../../SubHeader.vue'
|
||||
|
||||
@ -144,6 +144,7 @@ import {
|
||||
VButton,
|
||||
} from '@/ui'
|
||||
import { requestMixin, cssVariableMixin } from './mixin'
|
||||
import { rankListCssVars } from './rank-list'
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
@ -159,19 +160,7 @@ export default Vue.extend({
|
||||
},
|
||||
mixins: [
|
||||
requestMixin,
|
||||
cssVariableMixin({
|
||||
panelHeight: 608,
|
||||
padding: 12,
|
||||
rankItemHeight: 110,
|
||||
rankItemMargin: 24,
|
||||
rankItemTitleHeight: 20,
|
||||
firstCoverHeight: 225,
|
||||
firstCoverWidth: 350,
|
||||
secondCoverHeight: 110,
|
||||
secondCoverWidth: 168,
|
||||
thirdCoverHeight: 90,
|
||||
thirdCoverWidth: 139,
|
||||
}),
|
||||
cssVariableMixin(rankListCssVars),
|
||||
],
|
||||
props: {
|
||||
parseJson: {
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
export interface RankListCard {
|
||||
id: string
|
||||
title: string
|
||||
videoHref: string
|
||||
coverUrl: string
|
||||
upFaceUrl?: string
|
||||
upHref: string
|
||||
upName: string
|
||||
points: number
|
||||
playCount: number
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
export interface RankListCard {
|
||||
id: string
|
||||
title: string
|
||||
videoHref: string
|
||||
coverUrl: string
|
||||
upFaceUrl?: string
|
||||
upHref: string
|
||||
upName: string
|
||||
points: number
|
||||
playCount: number
|
||||
}
|
||||
export const rankListCssVars = {
|
||||
panelHeight: 608,
|
||||
padding: 12,
|
||||
rankItemHeight: 110,
|
||||
rankItemMargin: 24,
|
||||
rankItemTitleHeight: 20,
|
||||
firstCoverHeight: 225,
|
||||
firstCoverWidth: 350,
|
||||
secondCoverHeight: 110,
|
||||
secondCoverWidth: 168,
|
||||
thirdCoverHeight: 90,
|
||||
thirdCoverWidth: 139,
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user