mirror of
https://github.com/qier222/YesPlayMusic.git
synced 2024-11-22 11:29:37 +08:00
refactor: move all states+actions inside player.vue to Player.js
This commit is contained in:
parent
5355caa4e4
commit
fab0227ed3
12
src/App.vue
12
src/App.vue
|
@ -46,7 +46,7 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["showLyrics"]),
|
||||
...mapState(["showLyrics", "showLibraryDefault", "player"]),
|
||||
isAccountLoggedIn() {
|
||||
return isAccountLoggedIn();
|
||||
},
|
||||
|
@ -62,18 +62,14 @@ export default {
|
|||
);
|
||||
},
|
||||
enablePlayer() {
|
||||
return (
|
||||
this.$store.state.player.enabled &&
|
||||
this.$route.name !== "lastfmCallback"
|
||||
);
|
||||
return this.player.enabled && this.$route.name !== "lastfmCallback";
|
||||
},
|
||||
showNavbar() {
|
||||
return this.$route.name !== "lastfmCallback";
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$store.state.settings.showLibraryDefault &&
|
||||
this.$router.push("/library");
|
||||
this.showLibraryDefault && this.$router.push("/library");
|
||||
if (this.isElectron) {
|
||||
ipcRenderer(this);
|
||||
}
|
||||
|
@ -85,7 +81,7 @@ export default {
|
|||
if (e.target.tagName === "INPUT") return false;
|
||||
if (this.$route.name === "mv") return false;
|
||||
e.preventDefault();
|
||||
this.$refs.player.play();
|
||||
this.player.play();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -9,17 +9,16 @@
|
|||
@click.stop
|
||||
>
|
||||
<vue-slider
|
||||
v-model="progress"
|
||||
v-model="player.progress"
|
||||
:min="0"
|
||||
:max="progressMax"
|
||||
:max="player.currentTrackDuration"
|
||||
:interval="1"
|
||||
:drag-on-click="true"
|
||||
:duration="0"
|
||||
:dotSize="12"
|
||||
:dot-size="12"
|
||||
:height="2"
|
||||
:tooltipFormatter="formatTrackTime"
|
||||
@drag-end="setSeek"
|
||||
ref="progress"
|
||||
:tooltip-formatter="formatTrackTime"
|
||||
@drag-end="player.seek"
|
||||
></vue-slider>
|
||||
</div>
|
||||
<div class="controls">
|
||||
|
@ -46,16 +45,16 @@
|
|||
</div>
|
||||
<div class="like-button">
|
||||
<button-icon
|
||||
@click.native="likeCurrentSong"
|
||||
:title="$t('player.like')"
|
||||
@click.native="likeASong(player.currentTrack.id)"
|
||||
>
|
||||
<svg-icon
|
||||
v-show="!player.isCurrentTrackLiked"
|
||||
icon-class="heart"
|
||||
v-show="!liked.songs.includes(currentTrack.id)"
|
||||
></svg-icon>
|
||||
<svg-icon
|
||||
v-show="player.isCurrentTrackLiked"
|
||||
icon-class="heart-solid"
|
||||
v-show="liked.songs.includes(currentTrack.id)"
|
||||
></svg-icon>
|
||||
</button-icon>
|
||||
</div>
|
||||
|
@ -67,24 +66,26 @@
|
|||
<div class="container" @click.stop>
|
||||
<button-icon
|
||||
v-show="!player.isPersonalFM"
|
||||
@click.native="previous"
|
||||
:title="$t('player.previous')"
|
||||
@click.native="player.playPrevTrack"
|
||||
><svg-icon icon-class="previous"
|
||||
/></button-icon>
|
||||
<button-icon
|
||||
v-show="player.isPersonalFM"
|
||||
@click.native="moveToFMTrash"
|
||||
title="不喜欢"
|
||||
@click.native="player.moveToFMTrash"
|
||||
><svg-icon icon-class="thumbs-down"
|
||||
/></button-icon>
|
||||
<button-icon
|
||||
class="play"
|
||||
@click.native="play"
|
||||
:title="$t(player.playing ? 'player.pause' : 'player.play')"
|
||||
@click.native="player.playOrPause"
|
||||
>
|
||||
<svg-icon :iconClass="player.playing ? 'pause' : 'play'"
|
||||
<svg-icon :icon-class="player.playing ? 'pause' : 'play'"
|
||||
/></button-icon>
|
||||
<button-icon @click.native="next" :title="$t('player.next')"
|
||||
<button-icon
|
||||
:title="$t('player.next')"
|
||||
@click.native="player.playNextTrack"
|
||||
><svg-icon icon-class="next"
|
||||
/></button-icon>
|
||||
</div>
|
||||
|
@ -94,48 +95,48 @@
|
|||
<div class="blank"></div>
|
||||
<div class="container" @click.stop>
|
||||
<button-icon
|
||||
@click.native="goToNextTracksPage"
|
||||
:title="$t('player.nextUp')"
|
||||
:class="{
|
||||
active: this.$route.name === 'next',
|
||||
disabled: player.isPersonalFM,
|
||||
}"
|
||||
@click.native="goToNextTracksPage"
|
||||
><svg-icon icon-class="list"
|
||||
/></button-icon>
|
||||
<button-icon
|
||||
:class="{
|
||||
active: player.repeatMode !== 'off',
|
||||
disabled: player.isPersonalFM,
|
||||
}"
|
||||
:title="
|
||||
player.repeatMode === 'one'
|
||||
? $t('player.repeatTrack')
|
||||
: $t('player.repeat')
|
||||
"
|
||||
@click.native="repeat"
|
||||
:class="{
|
||||
active: player.repeatMode !== 'off',
|
||||
disabled: player.isPersonalFM,
|
||||
}"
|
||||
@click.native="player.switchRepeatMode"
|
||||
>
|
||||
<svg-icon
|
||||
icon-class="repeat"
|
||||
v-show="player.repeatMode !== 'one'"
|
||||
icon-class="repeat"
|
||||
/>
|
||||
<svg-icon
|
||||
icon-class="repeat-1"
|
||||
v-show="player.repeatMode === 'one'"
|
||||
icon-class="repeat-1"
|
||||
/>
|
||||
</button-icon>
|
||||
<button-icon
|
||||
@click.native="shuffle"
|
||||
:class="{ active: player.shuffle, disabled: player.isPersonalFM }"
|
||||
:title="$t('player.shuffle')"
|
||||
@click.native="player.switchShuffle"
|
||||
><svg-icon icon-class="shuffle"
|
||||
/></button-icon>
|
||||
<div class="volume-control">
|
||||
<button-icon :title="$t('player.mute')" @click.native="player.mute">
|
||||
<svg-icon icon-class="volume" v-show="volume > 0.5" />
|
||||
<svg-icon icon-class="volume-mute" v-show="volume === 0" />
|
||||
<svg-icon v-show="volume > 0.5" icon-class="volume" />
|
||||
<svg-icon v-show="volume === 0" icon-class="volume-mute" />
|
||||
<svg-icon
|
||||
icon-class="volume-half"
|
||||
v-show="volume <= 0.5 && volume !== 0"
|
||||
icon-class="volume-half"
|
||||
/>
|
||||
</button-icon>
|
||||
<div class="volume-bar">
|
||||
|
@ -147,7 +148,7 @@
|
|||
:drag-on-click="true"
|
||||
:duration="0"
|
||||
:tooltip="`none`"
|
||||
:dotSize="12"
|
||||
:dot-size="12"
|
||||
></vue-slider>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -167,9 +168,6 @@
|
|||
|
||||
<script>
|
||||
import { mapState, mapMutations, mapActions } from "vuex";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import { userLikedSongsIDs } from "@/api/user";
|
||||
import { likeATrack } from "@/api/track";
|
||||
import "@/assets/css/slider.css";
|
||||
|
||||
import ButtonIcon from "@/components/ButtonIcon.vue";
|
||||
|
@ -181,24 +179,8 @@ export default {
|
|||
ButtonIcon,
|
||||
VueSlider,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: null,
|
||||
progress: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
setInterval(() => {
|
||||
this.progress = this.player.seek();
|
||||
}, 1000);
|
||||
if (isAccountLoggedIn()) {
|
||||
userLikedSongsIDs(this.data.user.userId).then((data) => {
|
||||
this.updateLikedSongs(data.ids);
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["player", "settings", "liked", "data"]),
|
||||
...mapState(["player", "settings", "data"]),
|
||||
currentTrack() {
|
||||
return this.player.currentTrack;
|
||||
},
|
||||
|
@ -213,13 +195,6 @@ export default {
|
|||
playing() {
|
||||
return this.player.playing;
|
||||
},
|
||||
progressMax() {
|
||||
let max = ~~(this.player.currentTrack.dt / 1000);
|
||||
return max > 1 ? max - 1 : max;
|
||||
},
|
||||
isCurrentTrackLiked() {
|
||||
return this.liked.songs.includes(this.currentTrack.id);
|
||||
},
|
||||
audioSource() {
|
||||
return this.player._howler?._src.includes("kuwo.cn")
|
||||
? "音源来自酷我音乐"
|
||||
|
@ -227,38 +202,8 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["updateLikedSongs", "toggleLyrics"]),
|
||||
...mapActions(["showToast"]),
|
||||
play() {
|
||||
this.player.playing ? this.player.pause() : this.player.play();
|
||||
},
|
||||
next() {
|
||||
if (this.player.playNextTrack()) this.progress = 0;
|
||||
},
|
||||
previous() {
|
||||
if (this.player.playPrevTrack()) this.progress = 0;
|
||||
},
|
||||
shuffle() {
|
||||
if (this.player.isPersonalFM) return;
|
||||
this.player.shuffle = !this.player.shuffle;
|
||||
},
|
||||
repeat() {
|
||||
if (this.player.isPersonalFM) return;
|
||||
if (this.player.repeatMode === "on") {
|
||||
this.player.repeatMode = "one";
|
||||
} else if (this.player.repeatMode === "one") {
|
||||
this.player.repeatMode = "off";
|
||||
} else {
|
||||
this.player.repeatMode = "on";
|
||||
}
|
||||
},
|
||||
setSeek() {
|
||||
this.progress = this.$refs.progress.getValue();
|
||||
this.player.seek(this.$refs.progress.getValue());
|
||||
},
|
||||
setProgress(value) {
|
||||
this.progress = value;
|
||||
},
|
||||
...mapMutations(["toggleLyrics"]),
|
||||
...mapActions(["showToast", "likeASong"]),
|
||||
goToNextTracksPage() {
|
||||
if (this.player.isPersonalFM) return;
|
||||
this.$route.name === "next"
|
||||
|
@ -271,27 +216,6 @@ export default {
|
|||
let sec = (~~(value % 60)).toString().padStart(2, "0");
|
||||
return `${min}:${sec}`;
|
||||
},
|
||||
likeCurrentSong() {
|
||||
if (!isAccountLoggedIn()) {
|
||||
this.showToast("此操作需要登录网易云账号");
|
||||
return;
|
||||
}
|
||||
let id = this.currentTrack.id;
|
||||
let like = true;
|
||||
if (this.liked.songs.includes(id)) like = false;
|
||||
likeATrack({ id, like }).then(() => {
|
||||
if (like === false) {
|
||||
this.updateLikedSongs(this.liked.songs.filter((d) => d !== id));
|
||||
} else {
|
||||
let newLikeSongs = this.liked.songs;
|
||||
newLikeSongs.push(id);
|
||||
this.updateLikedSongs(newLikeSongs);
|
||||
}
|
||||
});
|
||||
},
|
||||
moveToFMTrash() {
|
||||
this.player.moveToFMTrash();
|
||||
},
|
||||
goToList() {
|
||||
if (this.player.playlistSource.id === this.data.likedSongPlaylistID) {
|
||||
this.$router.push({ path: "/library/liked-songs" });
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import store from "@/store";
|
||||
|
||||
const player = store.state.player;
|
||||
|
||||
export function ipcRenderer(vueInstance) {
|
||||
const self = vueInstance;
|
||||
// 添加专有的类名
|
||||
|
@ -25,46 +29,44 @@ export function ipcRenderer(vueInstance) {
|
|||
});
|
||||
|
||||
ipcRenderer.on("play", () => {
|
||||
self.$refs.player.play();
|
||||
player.playOrPause();
|
||||
});
|
||||
|
||||
ipcRenderer.on("next", () => {
|
||||
console.log("touchBar:next");
|
||||
self.$refs.player.next();
|
||||
player.playNextTrack();
|
||||
});
|
||||
|
||||
ipcRenderer.on("previous", () => {
|
||||
self.$refs.player.previous();
|
||||
player.playPrevTrack();
|
||||
});
|
||||
|
||||
ipcRenderer.on("increaseVolume", () => {
|
||||
if (self.$refs.player.volume + 0.1 >= 1) {
|
||||
return (self.$refs.player.volume = 1);
|
||||
if (player.volume + 0.1 >= 1) {
|
||||
return (player.volume = 1);
|
||||
}
|
||||
self.$refs.player.volume += 0.1;
|
||||
player.volume += 0.1;
|
||||
});
|
||||
|
||||
ipcRenderer.on("decreaseVolume", () => {
|
||||
if (self.$refs.player.volume - 0.1 <= 0) {
|
||||
return (self.$refs.player.volume = 0);
|
||||
if (player.volume - 0.1 <= 0) {
|
||||
return (player.volume = 0);
|
||||
}
|
||||
self.$refs.player.volume -= 0.1;
|
||||
player.volume -= 0.1;
|
||||
});
|
||||
|
||||
ipcRenderer.on("like", () => {
|
||||
self.$refs.player.likeCurrentSong();
|
||||
store.dispatch("likeASong", player.currentTrack.id);
|
||||
});
|
||||
|
||||
ipcRenderer.on("repeat", () => {
|
||||
self.$refs.player.repeat();
|
||||
player.switchRepeatMode();
|
||||
});
|
||||
|
||||
ipcRenderer.on("shuffle", () => {
|
||||
self.$refs.player.shuffle();
|
||||
player.switchShuffle();
|
||||
});
|
||||
|
||||
ipcRenderer.on("routerGo", (event, where) => {
|
||||
console.log(where);
|
||||
self.$refs.navbar.go(where);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// import store, { state, dispatch, commit } from "@/store";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import { likeATrack } from "@/api/track";
|
||||
|
||||
export default {
|
||||
showToast({ state, commit }, text) {
|
||||
if (state.toast.timer !== null) {
|
||||
|
@ -16,4 +20,24 @@ export default {
|
|||
}, 3200),
|
||||
});
|
||||
},
|
||||
likeASong({ state, commit, dispatch }, id) {
|
||||
if (!isAccountLoggedIn()) {
|
||||
dispatch("showToast", "此操作需要登录网易云账号");
|
||||
return;
|
||||
}
|
||||
let like = true;
|
||||
if (state.liked.songs.includes(id)) like = false;
|
||||
likeATrack({ id, like }).then(() => {
|
||||
if (like === false) {
|
||||
commit(
|
||||
"updateLikedSongs",
|
||||
state.liked.songs.filter((d) => d !== id)
|
||||
);
|
||||
} else {
|
||||
let newLikeSongs = state.liked.songs;
|
||||
newLikeSongs.push(id);
|
||||
commit("updateLikedSongs", newLikeSongs);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -17,35 +17,47 @@ const ipcRenderer =
|
|||
|
||||
export default class {
|
||||
constructor() {
|
||||
this._enabled = false;
|
||||
// 播放器状态
|
||||
this._playing = false; // 是否正在播放中
|
||||
this._progress = 0; // 当前播放歌曲的进度
|
||||
this._enabled = false; // 是否启用Player
|
||||
this._repeatMode = "off"; // off | on | one
|
||||
this._shuffle = false; // true | false
|
||||
this._volume = 1; // 0 to 1
|
||||
this._volumeBeforeMuted = 1; // 用于保存静音前的音量
|
||||
this._list = [];
|
||||
this._current = 0; // current track index
|
||||
this._shuffledList = [];
|
||||
this._shuffledCurrent = 0;
|
||||
this._playlistSource = { type: "album", id: 123 };
|
||||
this._currentTrack = { id: 86827685 };
|
||||
this._playNextList = []; // 当这个list不为空时,会优先播放这个list的歌
|
||||
this._playing = false;
|
||||
this._isPersonalFM = false;
|
||||
this._personalFMTrack = { id: 0 };
|
||||
this._personalFMNextTrack = { id: 0 };
|
||||
|
||||
// 播放信息
|
||||
this._list = []; // 播放列表
|
||||
this._current = 0; // 当前播放歌曲在播放列表里的index
|
||||
this._shuffledList = []; // 被随机打乱的播放列表,随机播放模式下会使用此播放列表
|
||||
this._shuffledCurrent = 0; // 当前播放歌曲在随机列表里面的index
|
||||
this._playlistSource = { type: "album", id: 123 }; // 当前播放列表的信息
|
||||
this._currentTrack = { id: 86827685 }; // 当前播放歌曲的详细信息
|
||||
this._playNextList = []; // 当这个list不为空时,会优先播放这个list的歌
|
||||
this._isPersonalFM = false; // 是否是私人FM模式
|
||||
this._personalFMTrack = { id: 0 }; // 私人FM当前歌曲
|
||||
this._personalFMNextTrack = { id: 0 }; // 私人FM下一首歌曲信息(为了快速加载下一首)
|
||||
|
||||
// howler (https://github.com/goldfire/howler.js)
|
||||
this._howler = null;
|
||||
Object.defineProperty(this, "_howler", {
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
// init
|
||||
this._init();
|
||||
|
||||
// for debug
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
window.player = this;
|
||||
}
|
||||
}
|
||||
|
||||
get repeatMode() {
|
||||
return this._repeatMode;
|
||||
}
|
||||
set repeatMode(mode) {
|
||||
if (this._isPersonalFM) return;
|
||||
if (!["off", "on", "one"].includes(mode)) {
|
||||
console.warn("repeatMode: invalid args, must be 'on' | 'off' | 'one'");
|
||||
return;
|
||||
|
@ -56,6 +68,7 @@ export default class {
|
|||
return this._shuffle;
|
||||
}
|
||||
set shuffle(shuffle) {
|
||||
if (this._isPersonalFM) return;
|
||||
if (shuffle !== true && shuffle !== false) {
|
||||
console.warn("shuffle: invalid args, must be Boolean");
|
||||
return;
|
||||
|
@ -109,12 +122,31 @@ export default class {
|
|||
get personalFMTrack() {
|
||||
return this._personalFMTrack;
|
||||
}
|
||||
get currentTrackDuration() {
|
||||
const trackDuration = this._currentTrack.dt || 1000;
|
||||
let duration = ~~(trackDuration / 1000);
|
||||
return duration > 1 ? duration - 1 : duration;
|
||||
}
|
||||
get progress() {
|
||||
return this._progress;
|
||||
}
|
||||
set progress(value) {
|
||||
if (this._howler) {
|
||||
this._howler.seek(value);
|
||||
}
|
||||
}
|
||||
get isCurrentTrackLiked() {
|
||||
return store.state.liked.songs.includes(this.currentTrack.id);
|
||||
}
|
||||
|
||||
_init() {
|
||||
this._loadSelfFromLocalStorage();
|
||||
Howler.autoUnlock = false;
|
||||
Howler.usingWebAudio = true;
|
||||
this._loadSelfFromLocalStorage();
|
||||
Howler.volume(this.volume);
|
||||
|
||||
if (this._enabled) {
|
||||
// 恢复当前播放歌曲
|
||||
this._replaceCurrentTrack(this._currentTrack.id, false).then(() => {
|
||||
this._howler?.seek(localStorage.getItem("playerCurrentTrackTime") ?? 0);
|
||||
setInterval(
|
||||
|
@ -127,10 +159,11 @@ export default class {
|
|||
);
|
||||
}); // update audio source and init howler
|
||||
this._initMediaSession();
|
||||
this._setIntervals();
|
||||
}
|
||||
Howler.volume(this.volume);
|
||||
|
||||
// 初始化私人FM
|
||||
if (this._personalFMTrack.id === 0 || this._personalFMNextTrack.id === 0) {
|
||||
// init fm
|
||||
personalFM().then((result) => {
|
||||
this._personalFMTrack = result.data[0];
|
||||
this._personalFMNextTrack = result.data[1];
|
||||
|
@ -138,23 +171,34 @@ export default class {
|
|||
});
|
||||
}
|
||||
}
|
||||
_setIntervals() {
|
||||
// 同步播放进度
|
||||
// TODO: 如果 _progress 在别的地方被改变了,这个定时器会覆盖之前改变的值,是bug
|
||||
setInterval(() => {
|
||||
this._progress = this._howler === null ? 0 : this._howler.seek();
|
||||
}, 1000);
|
||||
}
|
||||
_getNextTrack() {
|
||||
// 返回 [trackID, index]
|
||||
if (this._playNextList.length > 0) {
|
||||
let trackID = this._playNextList.shift();
|
||||
return [trackID, this.current];
|
||||
}
|
||||
|
||||
// 当歌曲是列表最后一首 && 循环模式开启
|
||||
if (this.list.length === this.current + 1 && this.repeatMode === "on") {
|
||||
// 当歌曲是列表最后一首 && 循环模式开启
|
||||
return [this.list[0], 0];
|
||||
}
|
||||
|
||||
// 返回 [trackID, index]
|
||||
return [this.list[this.current + 1], this.current + 1];
|
||||
}
|
||||
_getPrevTrack() {
|
||||
// 当歌曲是列表第一首 && 循环模式开启
|
||||
if (this.current === 0 && this.repeatMode === "on") {
|
||||
// 当歌曲是列表第一首 && 循环模式开启
|
||||
return [this.list[this.list.length - 1], this.list.length - 1];
|
||||
}
|
||||
|
||||
// 返回 [trackID, index]
|
||||
return [this.list[this.current - 1], this.current - 1];
|
||||
}
|
||||
async _shuffleTheList(firstTrackID = this._currentTrack.id) {
|
||||
|
@ -401,7 +445,7 @@ export default class {
|
|||
this.list.append(trackID);
|
||||
}
|
||||
playNextTrack(isFM = false) {
|
||||
if (this._isPersonalFM || isFM) {
|
||||
if (this._isPersonalFM || isFM === true) {
|
||||
this._isPersonalFM = true;
|
||||
this._personalFMTrack = this._personalFMNextTrack;
|
||||
this._replaceCurrentTrack(this._personalFMTrack.id);
|
||||
|
@ -565,4 +609,17 @@ export default class {
|
|||
likedCurrentTrack: store.state.liked.songs.includes(this.currentTrack.id),
|
||||
});
|
||||
}
|
||||
|
||||
switchRepeatMode() {
|
||||
if (this._repeatMode === "on") {
|
||||
this.repeatMode = "one";
|
||||
} else if (this._repeatMode === "one") {
|
||||
this.repeatMode = "off";
|
||||
} else {
|
||||
this.repeatMode = "on";
|
||||
}
|
||||
}
|
||||
switchShuffle() {
|
||||
this.shuffle = !this.shuffle;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<transition name="slide-up">
|
||||
<div class="lyrics-page" :class="{ 'no-lyric': noLyric }">
|
||||
<div
|
||||
v-if="this.$store.state.settings.showLyricsDynamicBackground"
|
||||
v-if="settings.showLyricsDynamicBackground"
|
||||
class="dynamic-background"
|
||||
>
|
||||
<div v-show="this.$store.state.showLyrics">
|
||||
<div v-show="showLyrics">
|
||||
<div
|
||||
class="top-right"
|
||||
:style="{ backgroundImage: `url(${imageUrl})` }"
|
||||
|
@ -55,11 +55,11 @@
|
|||
<div class="buttons">
|
||||
<button-icon
|
||||
:title="$t('player.like')"
|
||||
@click.native="playerRef.likeCurrentSong"
|
||||
@click.native="likeASong(player.currentTrack.id)"
|
||||
>
|
||||
<svg-icon
|
||||
:icon-class="
|
||||
playerRef.isCurrentTrackLiked ? 'heart-solid' : 'heart'
|
||||
player.isCurrentTrackLiked ? 'heart-solid' : 'heart'
|
||||
"
|
||||
/>
|
||||
</button-icon>
|
||||
|
@ -69,80 +69,77 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<span>{{ formatTrackTime(progress) || "0:00" }}</span>
|
||||
<span>{{ formatTrackTime(player.progress) || "0:00" }}</span>
|
||||
<div class="slider">
|
||||
<vue-slider
|
||||
v-model="progress"
|
||||
v-model="player.progress"
|
||||
:min="0"
|
||||
:max="progressMax"
|
||||
:max="player.currentTrackDuration"
|
||||
:interval="1"
|
||||
:drag-on-click="true"
|
||||
:duration="0"
|
||||
:dotSize="12"
|
||||
:dot-size="12"
|
||||
:height="2"
|
||||
:tooltipFormatter="formatTrackTime"
|
||||
@drag-end="setSeek"
|
||||
ref="progress"
|
||||
:tooltip-formatter="formatTrackTime"
|
||||
@drag-end="player.seek"
|
||||
></vue-slider>
|
||||
</div>
|
||||
<span>{{ formatTrackTime(progressMax) }}</span>
|
||||
<span>{{ formatTrackTime(player.currentTrackDuration) }}</span>
|
||||
</div>
|
||||
<div class="media-controls">
|
||||
<button-icon
|
||||
v-show="!player.isPersonalFM"
|
||||
@click.native="playerRef.repeat"
|
||||
:title="
|
||||
player.repeatMode === 'one'
|
||||
? $t('player.repeatTrack')
|
||||
: $t('player.repeat')
|
||||
"
|
||||
:class="{ active: player.repeatMode !== 'off' }"
|
||||
@click.native="player.switchRepeatMode"
|
||||
>
|
||||
<svg-icon
|
||||
icon-class="repeat"
|
||||
v-show="player.repeatMode !== 'one'"
|
||||
icon-class="repeat"
|
||||
/>
|
||||
<svg-icon
|
||||
icon-class="repeat-1"
|
||||
v-show="player.repeatMode === 'one'"
|
||||
icon-class="repeat-1"
|
||||
/>
|
||||
</button-icon>
|
||||
<div class="middle">
|
||||
<button-icon
|
||||
v-show="!player.isPersonalFM"
|
||||
@click.native="playerRef.previous"
|
||||
:title="$t('player.previous')"
|
||||
@click.native="player.playPrevTrack"
|
||||
>
|
||||
<svg-icon icon-class="previous" />
|
||||
</button-icon>
|
||||
<button-icon
|
||||
v-show="player.isPersonalFM"
|
||||
@click.native="moveToFMTrash"
|
||||
title="不喜欢"
|
||||
@click.native="player.moveToFMTrash"
|
||||
>
|
||||
<svg-icon icon-class="thumbs-down" />
|
||||
</button-icon>
|
||||
<button-icon
|
||||
id="play"
|
||||
@click.native="playerRef.play"
|
||||
:title="$t(player.playing ? 'player.pause' : 'player.play')"
|
||||
@click.native="player.playOrPause"
|
||||
>
|
||||
<svg-icon
|
||||
:icon-class="playerRef.playing ? 'pause' : 'play'"
|
||||
/>
|
||||
<svg-icon :icon-class="player.playing ? 'pause' : 'play'" />
|
||||
</button-icon>
|
||||
<button-icon
|
||||
@click.native="playerRef.next"
|
||||
:title="$t('player.next')"
|
||||
@click.native="player.playNextTrack"
|
||||
>
|
||||
<svg-icon icon-class="next" />
|
||||
</button-icon>
|
||||
</div>
|
||||
<button-icon
|
||||
v-show="!player.isPersonalFM"
|
||||
@click.native="playerRef.shuffle"
|
||||
:title="$t('player.shuffle')"
|
||||
:class="{ active: player.shuffle }"
|
||||
@click.native="player.switchShuffle"
|
||||
>
|
||||
<svg-icon icon-class="shuffle" />
|
||||
</button-icon>
|
||||
|
@ -153,21 +150,22 @@
|
|||
<div class="right-side">
|
||||
<transition name="slide-fade">
|
||||
<div
|
||||
v-show="!noLyric"
|
||||
ref="lyricsContainer"
|
||||
class="lyrics-container"
|
||||
:style="lyricFontSize"
|
||||
ref="lyricsContainer"
|
||||
v-show="!noLyric"
|
||||
>
|
||||
<div class="line" id="line-1"></div>
|
||||
<div id="line-1" class="line"></div>
|
||||
<div
|
||||
v-for="(line, index) in lyricWithTranslation"
|
||||
:id="`line${index}`"
|
||||
:key="index"
|
||||
class="line"
|
||||
:class="{
|
||||
highlight: highlightLyricIndex === index,
|
||||
}"
|
||||
v-for="(line, index) in lyricWithTranslation"
|
||||
:key="index"
|
||||
:id="`line${index}`"
|
||||
@click="clickLyricLine(line.time)"
|
||||
@dblclick="clickLyricLine(line.time, true)"
|
||||
><span v-html="formatLine(line)"></span
|
||||
></div>
|
||||
</div>
|
||||
|
@ -186,7 +184,7 @@
|
|||
// The lyrics page of Apple Music is so gorgeous, so I copy the design.
|
||||
// Some of the codes are from https://github.com/sl1673495/vue-netease-music
|
||||
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { mapState, mapMutations, mapActions } from "vuex";
|
||||
import VueSlider from "vue-slider-component";
|
||||
import { formatTrackTime } from "@/utils/common";
|
||||
import { getLyric } from "@/api/track";
|
||||
|
@ -209,24 +207,13 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["player"]),
|
||||
...mapState(["player", "settings", "showLyrics"]),
|
||||
currentTrack() {
|
||||
return this.player.currentTrack;
|
||||
},
|
||||
imageUrl() {
|
||||
return this.player.currentTrack?.al?.picUrl + "?param=1024y1024";
|
||||
},
|
||||
progress: {
|
||||
get() {
|
||||
return this.playerRef.progress;
|
||||
},
|
||||
set(value) {
|
||||
this.playerRef.setProgress(value);
|
||||
},
|
||||
},
|
||||
progressMax() {
|
||||
return this.playerRef.progressMax;
|
||||
},
|
||||
lyricWithTranslation() {
|
||||
let ret = [];
|
||||
// 空内容的去除
|
||||
|
@ -266,9 +253,6 @@ export default {
|
|||
playerRef() {
|
||||
return this.$parent.$refs.player ? this.$parent.$refs.player : {};
|
||||
},
|
||||
showLyrics() {
|
||||
return this.$store.state.showLyrics;
|
||||
},
|
||||
noLyric() {
|
||||
return this.lyric.length == 0;
|
||||
},
|
||||
|
@ -301,6 +285,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
...mapMutations(["toggleLyrics"]),
|
||||
...mapActions(["likeASong"]),
|
||||
getLyric() {
|
||||
if (!this.currentTrack.id) return;
|
||||
return getLyric(this.currentTrack.id).then((data) => {
|
||||
|
@ -319,18 +304,13 @@ export default {
|
|||
formatTrackTime(value) {
|
||||
return formatTrackTime(value);
|
||||
},
|
||||
setSeek() {
|
||||
let value = this.$refs.progress.getValue();
|
||||
this.playerRef.setProgress(value);
|
||||
this.playerRef.player.seek(value);
|
||||
},
|
||||
seek(value) {
|
||||
this.playerRef.setProgress(value);
|
||||
this.playerRef.player.seek(value);
|
||||
},
|
||||
clickLyricLine(value) {
|
||||
clickLyricLine(value, startPlay = false) {
|
||||
// TODO: 双击选择还会选中文字,考虑搞个右键菜单复制歌词
|
||||
if (window.getSelection().toString().length === 0) {
|
||||
this.seek(value);
|
||||
this.player.seek(value);
|
||||
}
|
||||
if (startPlay === true) {
|
||||
this.player.play();
|
||||
}
|
||||
},
|
||||
setLyricsInterval() {
|
||||
|
@ -476,7 +456,6 @@ export default {
|
|||
}
|
||||
|
||||
.svg-icon {
|
||||
opacity: 0.58;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user