Skip to content

Commit

Permalink
renderer: improve tab animation
Browse files Browse the repository at this point in the history
  • Loading branch information
rocka committed Jul 13, 2023
1 parent 01a4a15 commit 226893a
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 53 deletions.
48 changes: 29 additions & 19 deletions src/renderer/components/ArtistDetail/ArtistDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,23 @@
:value="tab"
@change="handelTabChange">
<mu-tab v-for="tab of DetailTabs"
:key="tab[0]"
:value="tab[0]">{{tab[1]}}</mu-tab>
:key="tab.key"
:value="tab.key">{{ tab.title }}</mu-tab>
</mu-tabs>
<transition mode="out-in"
:name="transitionName">
<keep-alive>
<component :is="detailCompo"
:artist="artist"
@scroll="handleScroll"></component>
</keep-alive>
</transition>
<div class="slide-anim-container">
<transition :name="transitionName">
<keep-alive>
<component :is="detailCompo"
:artist="artist"
@scroll="handleScroll"></component>
</keep-alive>
</transition>
</div>
</div>
</template>

<script>
import { mapActions, mapState } from 'vuex';
import { mapActions } from 'vuex';
import Api from '@/api/ipc';
import { bkgImg, sizeImg } from '@/util/image';
Expand All @@ -64,10 +65,10 @@ import RelatedMVs from './RelatedMVs.vue';
import Introduction from './Introduction.vue';
const DetailTabs = [
['hotSongs', '热门单曲'],
['albums', '所有专辑'],
['mvs', '相关 MV'],
['intro', '艺人介绍']
{ key: 'hotSongs', title: '热门单曲' },
{ key: 'albums', title: '所有专辑' },
{ key: 'mvs', title: '相关 MV' },
{ key: 'intro', title: '艺人介绍' }
];
const DetailCompo = {
Expand All @@ -77,8 +78,11 @@ const DetailCompo = {
intro: 'Introduction'
};
/** @typedef {{ detail: Models.Artist, hotSongs: Models.Track[] }} ArtistDetails */
export default {
props: {
/** @type {Vue.PropOptions<ArtistDetails>} */
artist: {
required: true
}
Expand All @@ -91,14 +95,20 @@ export default {
};
},
computed: {
...mapState(['user']),
/** @returns {{ key: string, title: string }[]} */
DetailTabs() { return DetailTabs; },
/** @returns {import('@/store/modules/user').State} */
user() { return this.$store.state.user; },
/** @returns {string} */
bkgImgStyle() {
return bkgImg(sizeImg(this.artist.detail.picUrl, 640, 300));
},
/** @returns {import('vue-router').Route} */
accountRoute() {
const id = this.artist.detail.accountId;
return id ? { name: 'user', params: { id } } : null;
},
/** @returns {string} */
detailCompo() {
return DetailCompo[this.tab];
}
Expand Down Expand Up @@ -135,8 +145,8 @@ export default {
handelTabChange(val) {
let oldIndex, newIndex;
DetailTabs.forEach((tab, index) => {
if (tab[0] === this.tab) oldIndex = index;
if (tab[0] === val) newIndex = index;
if (tab.key === this.tab) oldIndex = index;
if (tab.key === val) newIndex = index;
});
if (newIndex < oldIndex) {
this.transitionName = 'slide-right';
Expand All @@ -153,7 +163,6 @@ export default {
}
},
created() {
this.DetailTabs = DetailTabs;
this.updateDynamicDetail();
},
components: {
Expand All @@ -167,6 +176,7 @@ export default {

<style lang="less">
.artist-detail {
height: 100%;
overflow-x: hidden;
.header {
width: 100%;
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/UserDetail/Event.vue
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export default {

<style lang="less">
.user-event {
margin: 8px 24px;
padding: 8px 24px;
.event-item:not(:first-child) {
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
Expand Down
8 changes: 4 additions & 4 deletions src/renderer/components/UserDetail/Record.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<span class="statistics">累计听歌 {{ user.listenSongs }} 首</span>
</mu-sub-header>
<CenteredLoading v-if="loading"></CenteredLoading>
<VirtualTrackList v-else
:tracks="tracks"></VirtualTrackList>
<TrackList v-else
:tracks="tracks"></TrackList>
</template>
<CenteredTip v-else
icon="cloud_off"
Expand All @@ -24,7 +24,7 @@
import Api from '@/api/ipc';
import { Track } from '@/util/models';
import VirtualTrackList from '@/components/TrackList/VirtualTrackList.vue';
import TrackList from '@/components/TrackList/TrackList.vue';
import CenteredLoading from '@/components/CenteredLoading.vue';
import CenteredTip from '@/components/CenteredTip.vue';
Expand Down Expand Up @@ -86,7 +86,7 @@ export default {
this.getPlayRecord();
},
components: {
VirtualTrackList,
TrackList,
CenteredLoading,
CenteredTip
}
Expand Down
57 changes: 31 additions & 26 deletions src/renderer/components/UserDetail/UserDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,22 @@
:value="tab"
@change="handleTabChange">
<mu-tab v-for="tab of Tabs"
:key="tab[0]"
:value="tab[0]">{{tab[1]}}</mu-tab>
:key="tab.key"
:value="tab.key">{{ tab.title }}</mu-tab>
</mu-tabs>
<transition mode="out-in"
:name="transitionName">
<keep-alive>
<component :is="detailCompo"
:user="user"
:isSelf="isSelf"></component>
</keep-alive>
</transition>
<div class="slide-anim-container">
<transition :name="transitionName">
<keep-alive>
<component :is="detailCompo"
:user="user"
:isSelf="isSelf"></component>
</keep-alive>
</transition>
</div>
</div>
</template>

<script>
import { mapState } from 'vuex';
import Api from '@/api/ipc';
import { sizeImg, HiDpiPx } from '@/util/image';
Expand All @@ -80,10 +79,10 @@ import Event from './Event.vue';
import About from './About.vue';
const Tabs = [
['music', '音乐'],
['record', '排行'],
['event', '动态'],
['about', '关于']
{ key: 'music', title: '音乐' },
{ key: 'record', title: '排行' },
{ key: 'event', title: '动态' },
{ key: 'about', title: '关于' }
];
const DetailCompo = {
Expand All @@ -95,6 +94,7 @@ const DetailCompo = {
export default {
props: {
/** @type {Vue.PropOptions<Types.UserInfoRes>} */
user: {
required: true
}
Expand All @@ -110,37 +110,45 @@ export default {
};
},
computed: {
...mapState({
userState: 'user'
}),
/** @returns {{ key: string, title: string }[]} */
Tabs() { return Tabs; },
/** @returns {import('@/store/modules/user').State} */
userState() { return this.$store.state.user; },
/** @returns {boolean} */
isSelf() {
if (!this.userState.loginValid) return false;
return this.userState.info.id === this.user.profile.userId;
},
/** @returns {string} */
bkgImgStyle() {
return `background-image:url(${sizeImg(this.user.profile.backgroundUrl, HiDpiPx(640), HiDpiPx(300))})`;
},
/** @returns {string} */
avatarUrl() {
return sizeImg(this.user.profile.avatarUrl, HiDpiPx(75));
},
/** @returns {string} */
description() {
const i = this.user.identify;
if (i && i.imageDesc) return i.imageDesc;
const p = this.user.profile;
if (!p.userType) return null;
return p.description || (UserType[p.userType] || DefaultUserType).name;
},
/** @returns {string} */
follows() {
const p = this.user.profile;
const follows = typeof p.follows === 'number' ? p.follows : '...';
const followeds = typeof this.dynamicFollows.followeds === 'number' ? this.dynamicFollows.followeds : '...';
return `关注 ${follows} | 粉丝 ${followeds}`;
},
/** @returns {{ icon: string, text: string }} */
followBtn() {
return this.dynamicFollows.followed
? { icon: 'done', text: '已关注' }
: { icon: 'add', text: '关注' };
},
/** @returns {string} */
detailCompo() {
return DetailCompo[this.tab];
}
Expand Down Expand Up @@ -181,8 +189,8 @@ export default {
handleTabChange(val) {
let oldIndex, newIndex;
Tabs.forEach((tab, index) => {
if (tab[0] === this.tab) oldIndex = index;
if (tab[0] === val) newIndex = index;
if (tab.key === this.tab) oldIndex = index;
if (tab.key === val) newIndex = index;
});
if (newIndex < oldIndex) {
this.transitionName = 'slide-right';
Expand All @@ -198,7 +206,6 @@ export default {
}
},
created() {
this.Tabs = Tabs;
this.updateDynamicFollows();
},
components: {
Expand All @@ -214,10 +221,8 @@ export default {

<style lang="less">
.user-detail {
// a hack to make RecyclerScroller work
// https://github.com/Akryum/vue-virtual-scroller/blob/v1.0.0-rc.2/src/components/RecycleScroller.vue#L462
// https://github.com/olahol/scrollparent.js/blob/2.0.1/scrollparent.js#L18-L28
overflow: hidden overlay;
height: 100%;
overflow-x: hidden;
.user-bkg {
position: relative;
height: 300px;
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/page/Favorite/Favorite.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
v-text="tab.title"></span>
</mu-tab>
</mu-tabs>
<div class="fav-content">
<transition mode="out-in"
<div class="fav-content slide-anim-container">
<transition
:name="transitionName"
@after-enter="handleTabPageEnter">
<component ref="tabPage"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/page/Favorite/SearchFavrites.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export default {
]).then(() => this.loading = false);
},
tabActivated() {
if (!this.user.loginValid) return;
this.fetchData();
this.focusSearchInput();
},
Expand Down
8 changes: 7 additions & 1 deletion src/renderer/transition.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
/* tab slide */

.slide-anim-container {
position: relative;
}

.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition-duration: 0.15s;
position: absolute;
width: 100%;
transition-duration: 0.3s;
transition-property: transform, opacity;
transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1);
}
Expand Down

0 comments on commit 226893a

Please sign in to comment.