mirror of
https://github.com/qier222/YesPlayMusic.git
synced 2024-11-22 14:01:47 +08:00
feat: virtual scrollbar
This commit is contained in:
parent
226a2145c4
commit
7c79afd0d1
29
src/App.vue
29
src/App.vue
|
@ -1,7 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app" :class="{ 'user-select-none': userSelectNone }">
|
||||||
|
<Scrollbar v-show="!showLyrics" ref="scrollbar" />
|
||||||
<Navbar v-show="showNavbar" ref="navbar" />
|
<Navbar v-show="showNavbar" ref="navbar" />
|
||||||
<main>
|
<main ref="main" @scroll="handleScroll">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<router-view v-if="$route.meta.keepAlive"></router-view>
|
<router-view v-if="$route.meta.keepAlive"></router-view>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
<script>
|
<script>
|
||||||
import ModalAddTrackToPlaylist from './components/ModalAddTrackToPlaylist.vue';
|
import ModalAddTrackToPlaylist from './components/ModalAddTrackToPlaylist.vue';
|
||||||
import ModalNewPlaylist from './components/ModalNewPlaylist.vue';
|
import ModalNewPlaylist from './components/ModalNewPlaylist.vue';
|
||||||
|
import Scrollbar from './components/Scrollbar.vue';
|
||||||
import Navbar from './components/Navbar.vue';
|
import Navbar from './components/Navbar.vue';
|
||||||
import Player from './components/Player.vue';
|
import Player from './components/Player.vue';
|
||||||
import Toast from './components/Toast.vue';
|
import Toast from './components/Toast.vue';
|
||||||
|
@ -39,10 +41,12 @@ export default {
|
||||||
ModalAddTrackToPlaylist,
|
ModalAddTrackToPlaylist,
|
||||||
ModalNewPlaylist,
|
ModalNewPlaylist,
|
||||||
Lyrics,
|
Lyrics,
|
||||||
|
Scrollbar,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isElectron: process.env.IS_ELECTRON, // true || undefined
|
isElectron: process.env.IS_ELECTRON, // true || undefined
|
||||||
|
userSelectNone: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -93,6 +97,9 @@ export default {
|
||||||
this.$store.dispatch('fetchLikedMVs');
|
this.$store.dispatch('fetchLikedMVs');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleScroll() {
|
||||||
|
this.$refs.scrollbar.handleScroll();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -104,20 +111,18 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
margin-top: 84px;
|
position: fixed;
|
||||||
margin-bottom: 96px;
|
top: 0;
|
||||||
padding: {
|
bottom: 0;
|
||||||
right: 10vw;
|
right: 0;
|
||||||
left: 10vw;
|
left: 0;
|
||||||
}
|
overflow: auto;
|
||||||
|
padding: 64px 10vw 96px 10vw;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1336px) {
|
@media (max-width: 1336px) {
|
||||||
main {
|
main {
|
||||||
padding: 0 5vw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
padding: 64px 5vw 96px 5vw;
|
padding: 64px 5vw 96px 5vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
173
src/components/Scrollbar.vue
Normal file
173
src/components/Scrollbar.vue
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<transition name="fade">
|
||||||
|
<div
|
||||||
|
v-show="show"
|
||||||
|
id="scrollbar"
|
||||||
|
:class="{ 'on-drag': isOnDrag }"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id="thumbContainer"
|
||||||
|
:class="{ active }"
|
||||||
|
:style="thumbStyle"
|
||||||
|
@mouseenter="handleMouseenter"
|
||||||
|
@mouseleave="handleMouseleave"
|
||||||
|
@mousedown="handleDragStart"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Scrollbar',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
top: 0,
|
||||||
|
thumbHeight: 0,
|
||||||
|
active: false,
|
||||||
|
show: false,
|
||||||
|
hideTimer: null,
|
||||||
|
isOnDrag: false,
|
||||||
|
onDragClientY: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
thumbStyle() {
|
||||||
|
return {
|
||||||
|
transform: `translateY(${this.top}px)`,
|
||||||
|
height: `${this.thumbHeight}px`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
main() {
|
||||||
|
return this.$parent.$refs.main;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.$router.beforeEach((to, from, next) => {
|
||||||
|
this.show = false;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
handleScroll() {
|
||||||
|
const clintHeight = this.main.clientHeight - 128;
|
||||||
|
const scrollHeight = this.main.scrollHeight - 128;
|
||||||
|
const scrollTop = this.main.scrollTop;
|
||||||
|
let top = ~~((scrollTop / scrollHeight) * clintHeight);
|
||||||
|
let thumbHeight = ~~((clintHeight / scrollHeight) * clintHeight);
|
||||||
|
|
||||||
|
if (thumbHeight < 24) thumbHeight = 24;
|
||||||
|
if (top > clintHeight - thumbHeight) {
|
||||||
|
top = clintHeight - thumbHeight;
|
||||||
|
}
|
||||||
|
this.top = top;
|
||||||
|
this.thumbHeight = thumbHeight;
|
||||||
|
|
||||||
|
if (!this.show && clintHeight !== thumbHeight) this.show = true;
|
||||||
|
this.setScrollbarHideTimeout();
|
||||||
|
},
|
||||||
|
handleMouseenter() {
|
||||||
|
this.active = true;
|
||||||
|
},
|
||||||
|
handleMouseleave() {
|
||||||
|
this.active = false;
|
||||||
|
this.setScrollbarHideTimeout();
|
||||||
|
},
|
||||||
|
handleDragStart(e) {
|
||||||
|
this.onDragClientY = e.clientY;
|
||||||
|
this.isOnDrag = true;
|
||||||
|
this.$parent.userSelectNone = true;
|
||||||
|
document.addEventListener('mousemove', this.handleDragMove);
|
||||||
|
document.addEventListener('mouseup', this.handleDragEnd);
|
||||||
|
},
|
||||||
|
handleDragMove(e) {
|
||||||
|
if (!this.isOnDrag) return;
|
||||||
|
const clintHeight = this.main.clientHeight - 128;
|
||||||
|
const scrollHeight = this.main.scrollHeight - 128;
|
||||||
|
const clientY = e.clientY;
|
||||||
|
const scrollTop = this.main.scrollTop;
|
||||||
|
const offset = ~~(
|
||||||
|
((clientY - this.onDragClientY) / clintHeight) *
|
||||||
|
scrollHeight
|
||||||
|
);
|
||||||
|
this.top = ~~((scrollTop / scrollHeight) * clintHeight);
|
||||||
|
this.main.scrollBy(0, offset);
|
||||||
|
this.onDragClientY = clientY;
|
||||||
|
},
|
||||||
|
handleDragEnd() {
|
||||||
|
this.isOnDrag = false;
|
||||||
|
this.$parent.userSelectNone = false;
|
||||||
|
document.removeEventListener('mousemove', this.handleDragMove);
|
||||||
|
document.removeEventListener('mouseup', this.handleDragEnd);
|
||||||
|
},
|
||||||
|
handleClick() {
|
||||||
|
this.main.scrollBy({
|
||||||
|
top: 256,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setScrollbarHideTimeout() {
|
||||||
|
if (this.hideTimer !== null) clearTimeout(this.hideTimer);
|
||||||
|
this.hideTimer = setTimeout(() => {
|
||||||
|
if (!this.active) this.show = false;
|
||||||
|
this.hideTimer = null;
|
||||||
|
}, 4000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#scrollbar {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 16px;
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
#thumbContainer {
|
||||||
|
margin-top: 64px;
|
||||||
|
div {
|
||||||
|
transition: background 0.4s;
|
||||||
|
position: absolute;
|
||||||
|
right: 2px;
|
||||||
|
width: 8px;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(128, 128, 128, 0.38);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#thumbContainer.active div {
|
||||||
|
background: rgba(128, 128, 128, 0.58);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] {
|
||||||
|
#thumbContainer div {
|
||||||
|
background: var(--color-secondary-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#scrollbar.on-drag {
|
||||||
|
left: 0;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
.fade-enter,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-show="show" class="album">
|
<div v-show="show" class="album-page">
|
||||||
<div class="playlist-info">
|
<div class="playlist-info">
|
||||||
<Cover
|
<Cover
|
||||||
:id="album.id"
|
:id="album.id"
|
||||||
|
@ -299,6 +299,9 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.album-page {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
.playlist-info {
|
.playlist-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 78vw;
|
width: 78vw;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-show="show" class="artist">
|
<div v-show="show" class="artist-page">
|
||||||
<div class="artist-info">
|
<div class="artist-info">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<img :src="artist.img1v1Url | resizeImage(1024)" />
|
<img :src="artist.img1v1Url | resizeImage(1024)" />
|
||||||
|
@ -354,6 +354,10 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.artist-page {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
.artist-info {
|
.artist-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="explore">
|
<div class="explore-page">
|
||||||
<h1>{{ $t('explore.explore') }}</h1>
|
<h1>{{ $t('explore.explore') }}</h1>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-show="show" class="home">
|
<div v-show="show" class="home">
|
||||||
<div v-if="settings.showPlaylistsByAppleMusic !== false" class="index-row">
|
<div
|
||||||
|
v-if="settings.showPlaylistsByAppleMusic !== false"
|
||||||
|
class="index-row first-row"
|
||||||
|
>
|
||||||
<div class="title"> by Apple Music </div>
|
<div class="title"> by Apple Music </div>
|
||||||
<CoverRow
|
<CoverRow
|
||||||
:type="'playlist'"
|
:type="'playlist'"
|
||||||
|
@ -147,6 +150,9 @@ export default {
|
||||||
.index-row {
|
.index-row {
|
||||||
margin-top: 54px;
|
margin-top: 54px;
|
||||||
}
|
}
|
||||||
|
.index-row.first-row {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
.playlists {
|
.playlists {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-show="show">
|
<div v-show="show" ref="library">
|
||||||
<h1>
|
<h1>
|
||||||
<img class="avatar" :src="data.user.avatarUrl | resizeImage" />{{
|
<img class="avatar" :src="data.user.avatarUrl | resizeImage" />{{
|
||||||
data.user.nickname
|
data.user.nickname
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ContextMenu ref="playlistTabMenu">
|
<ContextMenu>
|
||||||
<div class="item" @click="changePlaylistFilter('all')">{{
|
<div class="item" @click="changePlaylistFilter('all')">{{
|
||||||
$t('contextMenu.allPlaylists')
|
$t('contextMenu.allPlaylists')
|
||||||
}}</div>
|
}}</div>
|
||||||
|
@ -241,7 +241,7 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.currentTab = tab;
|
this.currentTab = tab;
|
||||||
window.scrollTo({ top: 375, behavior: 'smooth' });
|
this.$parent.$refs.main.scrollTo({ top: 375, behavior: 'smooth' });
|
||||||
},
|
},
|
||||||
goToLikedSongsList() {
|
goToLikedSongsList() {
|
||||||
this.$router.push({ path: '/library/liked-songs' });
|
this.$router.push({ path: '/library/liked-songs' });
|
||||||
|
|
|
@ -1,92 +1,94 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="login">
|
<div class="login">
|
||||||
<div class="section-1">
|
<div class="login-container">
|
||||||
<img src="/img/logos/netease-music.png" />
|
<div class="section-1">
|
||||||
</div>
|
<img src="/img/logos/netease-music.png" />
|
||||||
<div class="title">{{ $t('login.loginText') }}</div>
|
</div>
|
||||||
<div class="section-2">
|
<div class="title">{{ $t('login.loginText') }}</div>
|
||||||
<div v-show="mode === 'phone'" class="input-box">
|
<div class="section-2">
|
||||||
<div class="container" :class="{ active: inputFocus === 'phone' }">
|
<div v-show="mode === 'phone'" class="input-box">
|
||||||
<svg-icon icon-class="mobile" />
|
<div class="container" :class="{ active: inputFocus === 'phone' }">
|
||||||
<div class="inputs">
|
<svg-icon icon-class="mobile" />
|
||||||
<input
|
<div class="inputs">
|
||||||
id="countryCode"
|
<input
|
||||||
v-model="countryCode"
|
id="countryCode"
|
||||||
:placeholder="
|
v-model="countryCode"
|
||||||
inputFocus === 'phone' ? '' : $t('login.countryCode')
|
:placeholder="
|
||||||
"
|
inputFocus === 'phone' ? '' : $t('login.countryCode')
|
||||||
@focus="inputFocus = 'phone'"
|
"
|
||||||
@blur="inputFocus = ''"
|
@focus="inputFocus = 'phone'"
|
||||||
@keyup.enter="login"
|
@blur="inputFocus = ''"
|
||||||
/>
|
@keyup.enter="login"
|
||||||
<input
|
/>
|
||||||
id="phoneNumber"
|
<input
|
||||||
v-model="phoneNumber"
|
id="phoneNumber"
|
||||||
:placeholder="inputFocus === 'phone' ? '' : $t('login.phone')"
|
v-model="phoneNumber"
|
||||||
@focus="inputFocus = 'phone'"
|
:placeholder="inputFocus === 'phone' ? '' : $t('login.phone')"
|
||||||
@blur="inputFocus = ''"
|
@focus="inputFocus = 'phone'"
|
||||||
@keyup.enter="login"
|
@blur="inputFocus = ''"
|
||||||
/>
|
@keyup.enter="login"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-show="mode === 'email'" class="input-box">
|
||||||
|
<div class="container" :class="{ active: inputFocus === 'email' }">
|
||||||
|
<svg-icon icon-class="mail" />
|
||||||
|
<div class="inputs">
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
v-model="email"
|
||||||
|
type="email"
|
||||||
|
:placeholder="inputFocus === 'email' ? '' : $t('login.email')"
|
||||||
|
@focus="inputFocus = 'email'"
|
||||||
|
@blur="inputFocus = ''"
|
||||||
|
@keyup.enter="login"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-box">
|
||||||
|
<div class="container" :class="{ active: inputFocus === 'password' }">
|
||||||
|
<svg-icon icon-class="lock" />
|
||||||
|
<div class="inputs">
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
v-model="password"
|
||||||
|
type="password"
|
||||||
|
:placeholder="
|
||||||
|
inputFocus === 'password' ? '' : $t('login.password')
|
||||||
|
"
|
||||||
|
@focus="inputFocus = 'password'"
|
||||||
|
@blur="inputFocus = ''"
|
||||||
|
@keyup.enter="login"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="mode === 'email'" class="input-box">
|
<div class="confirm">
|
||||||
<div class="container" :class="{ active: inputFocus === 'email' }">
|
<button v-show="!processing" @click="login">
|
||||||
<svg-icon icon-class="mail" />
|
{{ $t('login.login') }}
|
||||||
<div class="inputs">
|
</button>
|
||||||
<input
|
<button v-show="processing" class="loading" disabled>
|
||||||
id="email"
|
<span></span>
|
||||||
v-model="email"
|
<span></span>
|
||||||
type="email"
|
<span></span>
|
||||||
:placeholder="inputFocus === 'email' ? '' : $t('login.email')"
|
</button>
|
||||||
@focus="inputFocus = 'email'"
|
|
||||||
@blur="inputFocus = ''"
|
|
||||||
@keyup.enter="login"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="other-login">
|
||||||
<div class="container" :class="{ active: inputFocus === 'password' }">
|
<a v-show="mode === 'phone'" @click="mode = 'email'">{{
|
||||||
<svg-icon icon-class="lock" />
|
$t('login.loginWithEmail')
|
||||||
<div class="inputs">
|
}}</a>
|
||||||
<input
|
<a v-show="mode === 'email'" @click="mode = 'phone'">{{
|
||||||
id="password"
|
$t('login.loginWithPhone')
|
||||||
v-model="password"
|
}}</a>
|
||||||
type="password"
|
|
||||||
:placeholder="
|
|
||||||
inputFocus === 'password' ? '' : $t('login.password')
|
|
||||||
"
|
|
||||||
@focus="inputFocus = 'password'"
|
|
||||||
@blur="inputFocus = ''"
|
|
||||||
@keyup.enter="login"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="notice"
|
||||||
|
v-html="isElectron ? $t('login.noticeElectron') : $t('login.notice')"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="confirm">
|
|
||||||
<button v-show="!processing" @click="login">
|
|
||||||
{{ $t('login.login') }}
|
|
||||||
</button>
|
|
||||||
<button v-show="processing" class="loading" disabled>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="other-login">
|
|
||||||
<a v-show="mode === 'phone'" @click="mode = 'email'">{{
|
|
||||||
$t('login.loginWithEmail')
|
|
||||||
}}</a>
|
|
||||||
<a v-show="mode === 'email'" @click="mode = 'phone'">{{
|
|
||||||
$t('login.loginWithPhone')
|
|
||||||
}}</a>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="notice"
|
|
||||||
v-html="isElectron ? $t('login.noticeElectron') : $t('login.notice')"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -206,7 +208,14 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: calc(100vh - 192px);
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -223,6 +232,7 @@ export default {
|
||||||
img {
|
img {
|
||||||
height: 64px;
|
height: 64px;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mv">
|
<div class="mv-page">
|
||||||
<div class="current-video">
|
<div class="current-video">
|
||||||
<div class="video">
|
<div class="video">
|
||||||
<video ref="videoPlayer" class="plyr"></video>
|
<video ref="videoPlayer" class="plyr"></video>
|
||||||
|
@ -136,8 +136,9 @@ export default {
|
||||||
--plyr-control-radius: 8px;
|
--plyr-control-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mv {
|
.mv-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin-top: 32px;
|
||||||
}
|
}
|
||||||
.current-video {
|
.current-video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -176,6 +177,7 @@ export default {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
opacity: 0.88;
|
opacity: 0.88;
|
||||||
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-show="show">
|
<div v-show="show" class="playlist">
|
||||||
<div
|
<div
|
||||||
v-if="specialPlaylistInfo === undefined && !isLikeSongsPage"
|
v-if="specialPlaylistInfo === undefined && !isLikeSongsPage"
|
||||||
class="playlist-info"
|
class="playlist-info"
|
||||||
|
@ -174,6 +174,16 @@
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="load-more">
|
||||||
|
<ButtonTwoTone
|
||||||
|
v-show="hasMore"
|
||||||
|
color="grey"
|
||||||
|
:loading="loadingMore"
|
||||||
|
@click.native="loadMore(100)"
|
||||||
|
>{{ $t('explore.loadMore') }}</ButtonTwoTone
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
:show="showFullDescription"
|
:show="showFullDescription"
|
||||||
:close="toggleFullDescription"
|
:close="toggleFullDescription"
|
||||||
|
@ -346,6 +356,7 @@ export default {
|
||||||
showFullDescription: false,
|
showFullDescription: false,
|
||||||
tracks: [],
|
tracks: [],
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
|
hasMore: false,
|
||||||
lastLoadedTrackIndex: 9,
|
lastLoadedTrackIndex: 9,
|
||||||
displaySearchInPlaylist: false, // 是否显示搜索框
|
displaySearchInPlaylist: false, // 是否显示搜索框
|
||||||
searchKeyWords: '', // 搜索使用的关键字
|
searchKeyWords: '', // 搜索使用的关键字
|
||||||
|
@ -397,9 +408,6 @@ export default {
|
||||||
this.loadData(this.$route.params.id);
|
this.loadData(this.$route.params.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroyed() {
|
|
||||||
window.removeEventListener('scroll', this.handleScroll, true);
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['appendTrackToPlayerList']),
|
...mapMutations(['appendTrackToPlayerList']),
|
||||||
...mapActions(['playFirstTrackOnList', 'playTrackOnListByID', 'showToast']),
|
...mapActions(['playFirstTrackOnList', 'playTrackOnListByID', 'showToast']),
|
||||||
|
@ -443,9 +451,6 @@ export default {
|
||||||
if (next !== undefined) next();
|
if (next !== undefined) next();
|
||||||
this.show = true;
|
this.show = true;
|
||||||
this.lastLoadedTrackIndex = data.playlist.tracks.length - 1;
|
this.lastLoadedTrackIndex = data.playlist.tracks.length - 1;
|
||||||
if (this.playlist.trackCount > this.tracks.length) {
|
|
||||||
window.addEventListener('scroll', this.handleScroll, true);
|
|
||||||
}
|
|
||||||
return data;
|
return data;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -455,37 +460,27 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadMore(loadNum = 50) {
|
loadMore(loadNum = 100) {
|
||||||
let trackIDs = this.playlist.trackIds.filter((t, index) => {
|
let trackIDs = this.playlist.trackIds.filter((t, index) => {
|
||||||
if (
|
if (
|
||||||
index > this.lastLoadedTrackIndex &&
|
index > this.lastLoadedTrackIndex &&
|
||||||
index <= this.lastLoadedTrackIndex + loadNum
|
index <= this.lastLoadedTrackIndex + loadNum
|
||||||
)
|
) {
|
||||||
return t;
|
return t;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
trackIDs = trackIDs.map(t => t.id);
|
trackIDs = trackIDs.map(t => t.id);
|
||||||
getTrackDetail(trackIDs.join(',')).then(data => {
|
getTrackDetail(trackIDs.join(',')).then(data => {
|
||||||
this.tracks.push(...data.songs);
|
this.tracks.push(...data.songs);
|
||||||
this.lastLoadedTrackIndex += trackIDs.length;
|
this.lastLoadedTrackIndex += trackIDs.length;
|
||||||
this.loadingMore = false;
|
this.loadingMore = false;
|
||||||
|
if (this.lastLoadedTrackIndex + 1 === this.playlist.trackIds.length) {
|
||||||
|
this.hasMore = false;
|
||||||
|
} else {
|
||||||
|
this.hasMore = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleScroll(e) {
|
|
||||||
let dom = document.querySelector('html');
|
|
||||||
let scrollHeight = Math.max(dom.scrollHeight, dom.scrollHeight);
|
|
||||||
let scrollTop = e.target.scrollingElement.scrollTop;
|
|
||||||
let clientHeight =
|
|
||||||
dom.innerHeight || Math.min(dom.clientHeight, dom.clientHeight);
|
|
||||||
if (clientHeight + scrollTop + 200 >= scrollHeight) {
|
|
||||||
if (
|
|
||||||
this.lastLoadedTrackIndex + 1 === this.playlist.trackIds.length ||
|
|
||||||
this.loadingMore
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
this.loadingMore = true;
|
|
||||||
this.loadMore();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openMenu(e) {
|
openMenu(e) {
|
||||||
this.$refs.playlistMenu.openMenu(e);
|
this.$refs.playlistMenu.openMenu(e);
|
||||||
},
|
},
|
||||||
|
@ -546,6 +541,9 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.playlist {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
.playlist-info {
|
.playlist-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 72px;
|
margin-bottom: 72px;
|
||||||
|
@ -936,4 +934,10 @@ export default {
|
||||||
right: 8vw;
|
right: 8vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.load-more {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-show="show" class="search">
|
<div v-show="show" class="search-page">
|
||||||
<div v-show="artists.length > 0 || albums.length > 0" class="row">
|
<div v-show="artists.length > 0 || albums.length > 0" class="row">
|
||||||
<div v-show="artists.length > 0" class="artists">
|
<div v-show="artists.length > 0" class="artists">
|
||||||
<div v-show="artists.length > 0" class="section-title"
|
<div v-show="artists.length > 0" class="section-title"
|
||||||
|
@ -237,7 +237,7 @@ export default {
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
margin-top: 98px;
|
margin-top: 32px;
|
||||||
|
|
||||||
.artists {
|
.artists {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
@ -139,7 +139,7 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
h1 {
|
h1 {
|
||||||
margin-top: -10px;
|
margin-top: 32px;
|
||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
span {
|
span {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="settings">
|
<div class="settings-page">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div v-if="showUserInfo" class="user">
|
<div v-if="showUserInfo" class="user">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
|
@ -765,9 +765,10 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.settings {
|
.settings-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
margin-top: 32px;
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user