mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-16 05:08:04 +00:00
feat: add reversed mode & improvements to lyrics animation (#1226)
* feat(reversed-mode): add reversed mode & improvements to lyrics animation * feat(lyrics-animation): improve `:active` style * fix: extra white space after artist in lyrics view * refactor: remove unused function * feat: slightly add duration of transition to improve replication from Apple Music style
This commit is contained in:
parent
3ea5446fcc
commit
42f3da9b37
9 changed files with 134 additions and 27 deletions
2
src/assets/icons/sort-up.svg
Normal file
2
src/assets/icons/sort-up.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="sort-amount-up" class="svg-inline--fa fa-sort-amount-up fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M304 416h-64a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h48v304a16 16 0 0 0 16 16h32a16 16 0 0 0 16-16V160h48c14.21 0 21.38-17.24 11.31-27.31l-80-96a16 16 0 0 0-22.62 0l-80 96C-5.35 142.74 1.77 160 16 160zm416 0H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h192a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-64 128H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM496 32H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h256a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 784 B |
|
|
@ -132,6 +132,13 @@
|
||||||
@click.native="player.switchShuffle"
|
@click.native="player.switchShuffle"
|
||||||
><svg-icon icon-class="shuffle"
|
><svg-icon icon-class="shuffle"
|
||||||
/></button-icon>
|
/></button-icon>
|
||||||
|
<button-icon
|
||||||
|
v-if="settings.enableReversedMode"
|
||||||
|
:class="{ active: player.reversed, disabled: player.isPersonalFM }"
|
||||||
|
:title="$t('player.reversed')"
|
||||||
|
@click.native="player.switchReversed"
|
||||||
|
><svg-icon icon-class="sort-up"
|
||||||
|
/></button-icon>
|
||||||
<div class="volume-control">
|
<div class="volume-control">
|
||||||
<button-icon :title="$t('player.mute')" @click.native="player.mute">
|
<button-icon :title="$t('player.mute')" @click.native="player.mute">
|
||||||
<svg-icon v-show="volume > 0.5" icon-class="volume" />
|
<svg-icon v-show="volume > 0.5" icon-class="volume" />
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ export default {
|
||||||
repeat: 'Repeat',
|
repeat: 'Repeat',
|
||||||
repeatTrack: 'Repeat Track',
|
repeatTrack: 'Repeat Track',
|
||||||
shuffle: 'Shuffle',
|
shuffle: 'Shuffle',
|
||||||
|
reversed: 'Reversed',
|
||||||
play: 'Play',
|
play: 'Play',
|
||||||
pause: 'Pause',
|
pause: 'Pause',
|
||||||
mute: 'Mute',
|
mute: 'Mute',
|
||||||
|
|
@ -154,8 +155,9 @@ export default {
|
||||||
showPlaylistsByAppleMusic: 'Show playlists by Apple Music',
|
showPlaylistsByAppleMusic: 'Show playlists by Apple Music',
|
||||||
enableDiscordRichPresence: 'Enable Discord Rich Presence',
|
enableDiscordRichPresence: 'Enable Discord Rich Presence',
|
||||||
enableGlobalShortcut: 'Enable Global Shortcut',
|
enableGlobalShortcut: 'Enable Global Shortcut',
|
||||||
showLibraryDefault: 'Show library default',
|
showLibraryDefault: 'Show Library after App Launched',
|
||||||
subTitleDefault: 'Sub title alia default',
|
subTitleDefault: 'Show Alias for Subtitle by default',
|
||||||
|
enableReversedMode: 'Enable Reversed Mode (Experimental)',
|
||||||
lyricsBackground: {
|
lyricsBackground: {
|
||||||
text: 'Show Lyrics Background',
|
text: 'Show Lyrics Background',
|
||||||
off: 'Off',
|
off: 'Off',
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ export default {
|
||||||
repeat: '循环播放',
|
repeat: '循环播放',
|
||||||
repeatTrack: '单曲循环',
|
repeatTrack: '单曲循环',
|
||||||
shuffle: '随机播放',
|
shuffle: '随机播放',
|
||||||
|
reversed: '倒序播放',
|
||||||
play: '播放',
|
play: '播放',
|
||||||
pause: '暂停',
|
pause: '暂停',
|
||||||
mute: '静音',
|
mute: '静音',
|
||||||
|
|
@ -157,6 +158,7 @@ export default {
|
||||||
enableGlobalShortcut: '启用全局快捷键',
|
enableGlobalShortcut: '启用全局快捷键',
|
||||||
showLibraryDefault: '启动后显示音乐库',
|
showLibraryDefault: '启动后显示音乐库',
|
||||||
subTitleDefault: '副标题使用别名',
|
subTitleDefault: '副标题使用别名',
|
||||||
|
enableReversedMode: '启用倒序播放功能 (实验性功能)',
|
||||||
lyricsBackground: {
|
lyricsBackground: {
|
||||||
text: '显示歌词背景',
|
text: '显示歌词背景',
|
||||||
off: '关闭',
|
off: '关闭',
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ export default {
|
||||||
repeat: '循環播放',
|
repeat: '循環播放',
|
||||||
repeatTrack: '單曲循環',
|
repeatTrack: '單曲循環',
|
||||||
shuffle: '隨機播放',
|
shuffle: '隨機播放',
|
||||||
|
reversed: '倒序播放',
|
||||||
play: '播放',
|
play: '播放',
|
||||||
pause: '暫停',
|
pause: '暫停',
|
||||||
mute: '靜音',
|
mute: '靜音',
|
||||||
|
|
@ -154,6 +155,7 @@ export default {
|
||||||
enableGlobalShortcut: '啟用全域快捷鍵',
|
enableGlobalShortcut: '啟用全域快捷鍵',
|
||||||
showLibraryDefault: '啟動後顯示音樂庫',
|
showLibraryDefault: '啟動後顯示音樂庫',
|
||||||
subTitleDefault: '副標題使用別名',
|
subTitleDefault: '副標題使用別名',
|
||||||
|
enableReversedMode: '啟用倒序播放功能 (實驗性功能)',
|
||||||
lyricsBackground: {
|
lyricsBackground: {
|
||||||
text: '顯示歌詞背景',
|
text: '顯示歌詞背景',
|
||||||
off: '關閉',
|
off: '關閉',
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ let localStorage = {
|
||||||
enableUnblockNeteaseMusic: true,
|
enableUnblockNeteaseMusic: true,
|
||||||
automaticallyCacheSongs: true,
|
automaticallyCacheSongs: true,
|
||||||
cacheLimit: 8192,
|
cacheLimit: 8192,
|
||||||
|
enableReversedMode: false,
|
||||||
nyancatStyle: false,
|
nyancatStyle: false,
|
||||||
showLyricsTranslation: true,
|
showLyricsTranslation: true,
|
||||||
lyricsBackground: true,
|
lyricsBackground: true,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export default class {
|
||||||
this._enabled = false; // 是否启用Player
|
this._enabled = false; // 是否启用Player
|
||||||
this._repeatMode = 'off'; // off | on | one
|
this._repeatMode = 'off'; // off | on | one
|
||||||
this._shuffle = false; // true | false
|
this._shuffle = false; // true | false
|
||||||
|
this._reversed = false;
|
||||||
this._volume = 1; // 0 to 1
|
this._volume = 1; // 0 to 1
|
||||||
this._volumeBeforeMuted = 1; // 用于保存静音前的音量
|
this._volumeBeforeMuted = 1; // 用于保存静音前的音量
|
||||||
this._personalFMLoading = false; // 是否正在私人FM中加载新的track
|
this._personalFMLoading = false; // 是否正在私人FM中加载新的track
|
||||||
|
|
@ -91,6 +92,18 @@ export default class {
|
||||||
this._shuffleTheList();
|
this._shuffleTheList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
get reversed() {
|
||||||
|
return this._reversed;
|
||||||
|
}
|
||||||
|
set reversed(reversed) {
|
||||||
|
if (this._isPersonalFM) return;
|
||||||
|
if (reversed !== true && reversed !== false) {
|
||||||
|
console.warn('reversed: invalid args, must be Boolean');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('changing reversed to:', reversed);
|
||||||
|
this._reversed = reversed;
|
||||||
|
}
|
||||||
get volume() {
|
get volume() {
|
||||||
return this._volume;
|
return this._volume;
|
||||||
}
|
}
|
||||||
|
|
@ -191,27 +204,43 @@ export default class {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
_getNextTrack() {
|
_getNextTrack() {
|
||||||
|
const next = this._reversed ? this.current - 1 : this.current + 1;
|
||||||
|
|
||||||
if (this._playNextList.length > 0) {
|
if (this._playNextList.length > 0) {
|
||||||
let trackID = this._playNextList.shift();
|
let trackID = this._playNextList.shift();
|
||||||
return [trackID, this.current];
|
return [trackID, this.current];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当歌曲是列表最后一首 && 循环模式开启
|
// 循环模式开启,则重新播放当前模式下的相对的下一首
|
||||||
if (this.list.length === this.current + 1 && this.repeatMode === 'on') {
|
if (this.repeatMode === 'on') {
|
||||||
return [this.list[0], 0];
|
if (this._reversed && this.current === 0) {
|
||||||
|
// 倒序模式,当前歌曲是第一首,则重新播放列表最后一首
|
||||||
|
return [this.list[this.list.length - 1], this.list.length - 1];
|
||||||
|
} else if (this.list.length === this.current + 1) {
|
||||||
|
// 正序模式,当前歌曲是最后一首,则重新播放第一首
|
||||||
|
return [this.list[0], 0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回 [trackID, index]
|
// 返回 [trackID, index]
|
||||||
return [this.list[this.current + 1], this.current + 1];
|
return [this.list[next], next];
|
||||||
}
|
}
|
||||||
_getPrevTrack() {
|
_getPrevTrack() {
|
||||||
// 当歌曲是列表第一首 && 循环模式开启
|
const next = this._reversed ? this.current + 1 : this.current - 1;
|
||||||
if (this.current === 0 && this.repeatMode === 'on') {
|
|
||||||
return [this.list[this.list.length - 1], this.list.length - 1];
|
// 循环模式开启,则重新播放当前模式下的相对的下一首
|
||||||
|
if (this.repeatMode === 'on') {
|
||||||
|
if (this._reversed && this.current === 0) {
|
||||||
|
// 倒序模式,当前歌曲是最后一首,则重新播放列表第一首
|
||||||
|
return [this.list[0], 0];
|
||||||
|
} else if (this.list.length === this.current + 1) {
|
||||||
|
// 正序模式,当前歌曲是第一首,则重新播放列表最后一首
|
||||||
|
return [this.list[this.list.length - 1], this.list.length - 1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回 [trackID, index]
|
// 返回 [trackID, index]
|
||||||
return [this.list[this.current - 1], this.current - 1];
|
return [this.list[next], next];
|
||||||
}
|
}
|
||||||
async _shuffleTheList(firstTrackID = this._currentTrack.id) {
|
async _shuffleTheList(firstTrackID = this._currentTrack.id) {
|
||||||
let list = this._list.filter(tid => tid !== firstTrackID);
|
let list = this._list.filter(tid => tid !== firstTrackID);
|
||||||
|
|
@ -726,6 +755,9 @@ export default class {
|
||||||
switchShuffle() {
|
switchShuffle() {
|
||||||
this.shuffle = !this.shuffle;
|
this.shuffle = !this.shuffle;
|
||||||
}
|
}
|
||||||
|
switchReversed() {
|
||||||
|
this.reversed = !this.reversed;
|
||||||
|
}
|
||||||
|
|
||||||
clearPlayNextList() {
|
clearPlayNextList() {
|
||||||
this._playNextList = [];
|
this._playNextList = [];
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@
|
||||||
<router-link
|
<router-link
|
||||||
:to="`/artist/${artist.id}`"
|
:to="`/artist/${artist.id}`"
|
||||||
@click.native="toggleLyrics"
|
@click.native="toggleLyrics"
|
||||||
>{{ artist.name }}
|
>{{ artist.name }}</router-link
|
||||||
</router-link>
|
>
|
||||||
<span v-if="album.id !== 0">
|
<span v-if="album.id !== 0">
|
||||||
-
|
-
|
||||||
<router-link
|
<router-link
|
||||||
|
|
@ -187,8 +187,18 @@
|
||||||
}"
|
}"
|
||||||
@click="clickLyricLine(line.time)"
|
@click="clickLyricLine(line.time)"
|
||||||
@dblclick="clickLyricLine(line.time, true)"
|
@dblclick="clickLyricLine(line.time, true)"
|
||||||
><span v-html="formatLine(line)"></span
|
>
|
||||||
></div>
|
<span v-if="line.contents[0]">{{ line.contents[0] }}</span>
|
||||||
|
<br />
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
line.contents[1] &&
|
||||||
|
$store.state.settings.showLyricsTranslation
|
||||||
|
"
|
||||||
|
class="translation"
|
||||||
|
>{{ line.contents[1] }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -400,16 +410,6 @@ export default {
|
||||||
}
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
},
|
},
|
||||||
formatLine(line) {
|
|
||||||
const showLyricsTranslation = this.$store.state.settings
|
|
||||||
.showLyricsTranslation;
|
|
||||||
if (showLyricsTranslation && line.contents[1]) {
|
|
||||||
return `<span>${line.contents[0]}<br/>${line.contents[1]}</span>`;
|
|
||||||
} else if (line.contents[0] !== undefined) {
|
|
||||||
return `<span>${line.contents[0]}</span>`;
|
|
||||||
}
|
|
||||||
return 'unknown';
|
|
||||||
},
|
|
||||||
moveToFMTrash() {
|
moveToFMTrash() {
|
||||||
this.player.moveToFMTrash();
|
this.player.moveToFMTrash();
|
||||||
},
|
},
|
||||||
|
|
@ -673,17 +673,28 @@ export default {
|
||||||
scrollbar-width: none; // firefox
|
scrollbar-width: none; // firefox
|
||||||
|
|
||||||
.line {
|
.line {
|
||||||
padding: 18px;
|
margin: 2px 0;
|
||||||
transition: 0.2s;
|
padding: 12px 18px;
|
||||||
|
transition: 0.5s;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--color-secondary-bg-for-transparent);
|
background: var(--color-secondary-bg-for-transparent);
|
||||||
}
|
}
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
opacity: 0.28;
|
opacity: 0.28;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
font-size: 1em;
|
||||||
|
transition: all 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
}
|
||||||
|
|
||||||
|
span.translation {
|
||||||
|
opacity: 0.2;
|
||||||
|
font-size: 0.95em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -691,9 +702,19 @@ export default {
|
||||||
background: unset;
|
background: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.translation {
|
||||||
|
margin-top: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
.highlight span {
|
.highlight span {
|
||||||
opacity: 0.98;
|
opacity: 0.98;
|
||||||
transition: 0.5s;
|
display: inline-block;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight span.translation {
|
||||||
|
opacity: 0.65;
|
||||||
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -755,6 +776,12 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1200px) {
|
||||||
|
.right-side .lyrics-container {
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.slide-up-enter-active,
|
.slide-up-enter-active,
|
||||||
.slide-up-leave-active {
|
.slide-up-leave-active {
|
||||||
transition: all 0.4s;
|
transition: all 0.4s;
|
||||||
|
|
|
||||||
|
|
@ -384,6 +384,23 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="left">
|
||||||
|
<div class="title">{{ $t('settings.enableReversedMode') }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<div class="toggle">
|
||||||
|
<input
|
||||||
|
id="enable-reversed-mode"
|
||||||
|
v-model="enableReversedMode"
|
||||||
|
type="checkbox"
|
||||||
|
name="enable-reversed-mode"
|
||||||
|
/>
|
||||||
|
<label for="enable-reversed-mode"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="title" style="transform: scaleX(-1)">🐈️ 🏳️🌈</div>
|
<div class="title" style="transform: scaleX(-1)">🐈️ 🏳️🌈</div>
|
||||||
|
|
@ -805,6 +822,21 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
enableReversedMode: {
|
||||||
|
get() {
|
||||||
|
if (this.settings.enableReversedMode === undefined) return false;
|
||||||
|
return this.settings.enableReversedMode;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('updateSettings', {
|
||||||
|
key: 'enableReversedMode',
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
if (value === false) {
|
||||||
|
this.$store.state.player.reversed = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
enableGlobalShortcut: {
|
enableGlobalShortcut: {
|
||||||
get() {
|
get() {
|
||||||
return this.settings.enableGlobalShortcut;
|
return this.settings.enableGlobalShortcut;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue