From 346fa2c0045e276158c75c90524a376b6a013e62 Mon Sep 17 00:00:00 2001 From: lyswhut <lyswhut@qq.com> Date: Tue, 28 Apr 2020 13:33:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=92=AD=E6=94=BE=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- postcss.config.js | 5 + publish/changeLog.md | 2 + src/main/utils/flacMeta.js | 1 - src/renderer/App.vue | 16 +- src/renderer/assets/styles/variables.less | 39 + src/renderer/components/core/Aside.vue | 5 + src/renderer/components/core/Icons.vue | 3 + src/renderer/components/core/Player.vue | 200 +++-- src/renderer/components/core/PlayerDetail.vue | 706 ++++++++++++++++++ src/renderer/components/core/Toolbar.vue | 9 +- src/renderer/lang/cns/core/player.json | 7 +- src/renderer/lang/cnt/core/player.json | 7 +- src/renderer/lang/en/core/player.json | 7 +- src/renderer/store/modules/player.js | 5 + src/renderer/utils/index.js | 5 + 15 files changed, 935 insertions(+), 82 deletions(-) create mode 100644 src/renderer/components/core/PlayerDetail.vue diff --git a/postcss.config.js b/postcss.config.js index 456a2009..260098a9 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -11,9 +11,14 @@ module.exports = { 'line-height', 'letter-spacing', 'padding', 'margin', + 'padding-left', 'padding-right', + 'padding-top', 'padding-bottom', + 'margin-left', 'margin-right', + 'margin-top', 'margin-bottom', 'height', 'width', 'max-width', 'max-height', 'min-width', 'min-height', + 'flex', '::-webkit-scrollbar', 'top', 'left', 'bottom', 'right', 'border-radius', ], diff --git a/publish/changeLog.md b/publish/changeLog.md index 08531797..b61ee280 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -8,6 +8,7 @@ - 新增虾米音源 - 新增新皮肤“粉妆玉琢”、“青出于黑”,可去体验下~ - 新增“超大”、“巨大”窗口尺寸 +- 新增播放详情页(退出详情页可点击右上角退出按钮或者在播放详情页任意地方**鼠标快速右击两次**) ### 优化 @@ -20,6 +21,7 @@ - 下载列表的歌曲下载、播放将随设置中的保存路径改变而改变,不再固定指向其初始位置 - 移除列表多选框,现在多选需要键盘配合,想要多选前需按下`Shift`或`Ctrl`键然后再鼠标点击想要选中的内容即可触发多选机制,其中`Shift`键用于连续选择,`Ctrl`键用于不连续选择,`Ctrl+a`用于快速全选。例子一:想要选中1-5项,则先按下`Shift`键后,鼠标点击第一项,再点击第五项即可完成选择;例子二:想要选中1项与第3项,则先按下`Ctrl`键后,鼠标点击第一项,再点击第三项即可完成选择;例子三:想要选中当前列表的全部内容,键盘先按下`Ctrl`键不放,然后按`a`键,即可完成选择。用`Shift`或`Ctrl`选择时,鼠标点击未选中的内容会将其选中,点击已选择的内容会将其取消选择,若想全部取消选择,在不按`Shift`或`Alt`键的情况下,随意点击列表里的一项内容即可全部取消选择。(P.S:`Ctrl`键对应Mac OS上的`Command`键) +- 现在进度条的封面图左击改为打开播放详情页,在列表定位歌曲改为右击 ### 修复 diff --git a/src/main/utils/flacMeta.js b/src/main/utils/flacMeta.js index c9beaf26..9be44505 100644 --- a/src/main/utils/flacMeta.js +++ b/src/main/utils/flacMeta.js @@ -19,7 +19,6 @@ const writeMeta = async(filePath, meta, picPath) => { } if (picPath) { const apicData = await fsPromises.readFile(picPath) - console.log(apicData) let imgSize = getImgSize(apicData) let mime_type let bitsPerPixel diff --git a/src/renderer/App.vue b/src/renderer/App.vue index f4e1107d..19f74dcd 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -1,24 +1,24 @@ <template lang="pug"> -#container(v-if="isProd && !isNt" :class="theme" @mouseenter="enableIgnoreMouseEvents" @mouseleave="dieableIgnoreMouseEvents") +#container(v-if="isProd && !isNt" :class="[theme, nd ? 'nd' : '']" @mouseenter="enableIgnoreMouseEvents" @mouseleave="dieableIgnoreMouseEvents") core-aside#left #right core-toolbar#toolbar core-view#view core-player#player core-icons - material-pact-modal(v-show="!setting.isAgreePact || globalObj.isShowPact") - material-version-modal(v-show="version.showModal") material-xm-verify-modal(v-show="globalObj.xm.isShowVerify" :show="globalObj.xm.isShowVerify" :bg-close="false" @close="handleXMVerifyModalClose") -#container(v-else :class="theme") + material-version-modal(v-show="version.showModal") + material-pact-modal(v-show="!setting.isAgreePact || globalObj.isShowPact") +#container(v-else :class="[theme, nd ? 'nd' : '']") core-aside#left #right core-toolbar#toolbar core-view#view core-player#player core-icons - material-pact-modal(v-show="!setting.isAgreePact || globalObj.isShowPact") - material-version-modal(v-show="version.showModal") material-xm-verify-modal(v-show="globalObj.xm.isShowVerify" :show="globalObj.xm.isShowVerify" :bg-close="false" @close="handleXMVerifyModalClose") + material-version-modal(v-show="version.showModal") + material-pact-modal(v-show="!setting.isAgreePact || globalObj.isShowPact") </template> <script> @@ -55,6 +55,7 @@ export default { } }, computed: { + ...mapGetters('player', ['isShowPlayerDetail']), ...mapGetters(['setting', 'theme', 'version', 'windowSizeActive']), ...mapGetters('list', ['defaultList', 'loveList']), ...mapGetters('download', { @@ -64,6 +65,9 @@ export default { ...mapGetters('search', { searchHistoryList: 'historyList', }), + nd() { + return this.isShowPlayerDetail + }, }, created() { this.saveSetting = throttle(n => { diff --git a/src/renderer/assets/styles/variables.less b/src/renderer/assets/styles/variables.less index 4f0a869f..1bc71284 100644 --- a/src/renderer/assets/styles/variables.less +++ b/src/renderer/assets/styles/variables.less @@ -46,14 +46,17 @@ @color-player-progress-bar1: darken(@color-theme_2, 12%); @color-player-progress-bar2: lighten(@color-theme, 12%); @color-player-status-text: lighten(@color-theme_2-font, 10%); +@color-player-detail-play-btn: lighten(@color-theme, 7%); @color-tab-btn-background: fadeout(lighten(@color-theme, 10%), 80%); @color-tab-btn-background-hover: @color-theme_2-hover; @color-tab-border-top: fadeout(lighten(@color-theme, 5%), 50%); @color-tab-border-bottom: lighten(@color-theme, 5%); +@color-hideBtn: #3bc2b2; @color-minBtn: #85c43b; @color-maxBtn: #e7aa36; @color-closeBtn: #ea6e4d; +@color-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-minBtn-hover: fadeout(@color-minBtn, 10%); @color-maxBtn-hover: fadeout(@color-maxBtn, 10%); @color-closeBtn-hover: fadeout(@color-closeBtn, 10%); @@ -101,13 +104,16 @@ @color-green-player-progress-bar1: darken(@color-green-theme_2, 12%); @color-green-player-progress-bar2: lighten(@color-green-theme, 12%); @color-green-player-status-text: lighten(@color-green-theme_2-font, 10%); +@color-green-player-detail-play-btn: lighten(@color-green-theme, 7%); @color-green-tab-btn-background: fadeout(lighten(@color-green-theme, 10%), 80%); @color-green-tab-btn-background-hover: @color-green-theme_2-hover; @color-green-tab-border-top: fadeout(lighten(@color-green-theme, 5%), 50%); @color-green-tab-border-bottom: lighten(@color-green-theme, 5%); +@color-green-hideBtn: #3bc2b2; @color-green-minBtn: #85c43b; @color-green-maxBtn: #e7aa36; @color-green-closeBtn: #ea6e4d; +@color-green-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-green-minBtn-hover: fadeout(@color-green-minBtn, 10%); @color-green-maxBtn-hover: fadeout(@color-green-maxBtn, 10%); @color-green-closeBtn-hover: fadeout(@color-green-closeBtn, 10%); @@ -151,13 +157,16 @@ @color-yellow-player-progress-bar1: darken(@color-yellow-theme_2, 12%); @color-yellow-player-progress-bar2: lighten(@color-yellow-theme, 2%); @color-yellow-player-status-text: lighten(@color-yellow-theme_2-font, 10%); +@color-yellow-player-detail-play-btn: lighten(@color-yellow-theme, 7%); @color-yellow-tab-btn-background: fadeout(lighten(@color-yellow-theme, 10%), 70%); @color-yellow-tab-btn-background-hover: @color-yellow-theme_2-hover; @color-yellow-tab-border-top: fadeout(lighten(@color-yellow-theme, 5%), 40%); @color-yellow-tab-border-bottom: @color-yellow-theme; +@color-yellow-hideBtn: #3bc2b2; @color-yellow-minBtn: #85c43b; @color-yellow-maxBtn: #e7aa36; @color-yellow-closeBtn: #ea6e4d; +@color-yellow-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-yellow-minBtn-hover: fadeout(@color-yellow-minBtn, 10%); @color-yellow-maxBtn-hover: fadeout(@color-yellow-maxBtn, 10%); @color-yellow-closeBtn-hover: fadeout(@color-yellow-closeBtn, 10%); @@ -200,13 +209,16 @@ @color-orange-player-progress-bar1: darken(@color-orange-theme_2, 12%); @color-orange-player-progress-bar2: lighten(@color-orange-theme, 12%); @color-orange-player-status-text: lighten(@color-orange-theme_2-font, 10%); +@color-orange-player-detail-play-btn: lighten(@color-orange-theme, 7%); @color-orange-tab-btn-background: fadeout(lighten(@color-orange-theme, 10%), 80%); @color-orange-tab-btn-background-hover: @color-orange-theme_2-hover; @color-orange-tab-border-top: fadeout(lighten(@color-orange-theme, 5%), 50%); @color-orange-tab-border-bottom: lighten(@color-orange-theme, 5%); +@color-orange-hideBtn: #3bc2b2; @color-orange-minBtn: #85c43b; @color-orange-maxBtn: #e7aa36; @color-orange-closeBtn: #ea6e4d; +@color-orange-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-orange-minBtn-hover: fadeout(@color-orange-minBtn, 10%); @color-orange-maxBtn-hover: fadeout(@color-orange-maxBtn, 10%); @color-orange-closeBtn-hover: fadeout(@color-orange-closeBtn, 10%); @@ -249,13 +261,16 @@ @color-blue-player-progress-bar1: darken(@color-blue-theme_2, 12%); @color-blue-player-progress-bar2: lighten(@color-blue-theme, 12%); @color-blue-player-status-text: lighten(@color-blue-theme_2-font, 10%); +@color-blue-player-detail-play-btn: lighten(@color-blue-theme, 7%); @color-blue-tab-btn-background: fadeout(lighten(@color-blue-theme, 10%), 80%); @color-blue-tab-btn-background-hover: @color-blue-theme_2-hover; @color-blue-tab-border-top: fadeout(lighten(@color-blue-theme, 5%), 50%); @color-blue-tab-border-bottom: lighten(@color-blue-theme, 5%); +@color-blue-hideBtn: #3bc2b2; @color-blue-minBtn: #85c43b; @color-blue-maxBtn: #e7aa36; @color-blue-closeBtn: #ea6e4d; +@color-blue-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-blue-minBtn-hover: fadeout(@color-blue-minBtn, 10%); @color-blue-maxBtn-hover: fadeout(@color-blue-maxBtn, 10%); @color-blue-closeBtn-hover: fadeout(@color-blue-closeBtn, 10%); @@ -298,15 +313,18 @@ @color-red-player-progress-bar1: darken(@color-red-theme_2, 12%); @color-red-player-progress-bar2: lighten(@color-red-theme, 12%); @color-red-player-status-text: lighten(@color-red-theme_2-font, 10%); +@color-red-player-detail-play-btn: lighten(@color-red-theme, 7%); @color-red-tab-border-top: fadeout(lighten(@color-red-theme, 25%), 70%); @color-red-tab-border-bottom: lighten(@color-red-theme, 35%); @color-red-tab-btn-background: fadeout(lighten(@color-red-theme, 10%), 80%); @color-red-tab-btn-background-hover: @color-red-theme_2-hover; @color-red-tab-border-top: fadeout(lighten(@color-red-theme, 5%), 50%); @color-red-tab-border-bottom: lighten(@color-red-theme, 5%); +@color-red-hideBtn: #3bc2b2; @color-red-minBtn: #85c43b; @color-red-maxBtn: #e7aa36; @color-red-closeBtn: #ea6e4d; +@color-red-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-red-minBtn-hover: fadeout(@color-red-minBtn, 10%); @color-red-maxBtn-hover: fadeout(@color-red-maxBtn, 10%); @color-red-closeBtn-hover: fadeout(@color-red-closeBtn, 10%); @@ -349,13 +367,16 @@ @color-pink-player-progress-bar1: darken(@color-pink-theme_2, 12%); @color-pink-player-progress-bar2: lighten(@color-pink-theme, 2%); @color-pink-player-status-text: lighten(@color-pink-theme_2-font, 10%); +@color-pink-player-detail-play-btn: lighten(@color-pink-theme, 7%); @color-pink-tab-btn-background: fadeout(lighten(@color-pink-theme, 10%), 70%); @color-pink-tab-btn-background-hover: @color-pink-theme_2-hover; @color-pink-tab-border-top: fadeout(lighten(@color-pink-theme, 5%), 40%); @color-pink-tab-border-bottom: @color-pink-theme; +@color-pink-hideBtn: #3bc2b2; @color-pink-minBtn: #85c43b; @color-pink-maxBtn: #e7aa36; @color-pink-closeBtn: #ea6e4d; +@color-pink-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-pink-minBtn-hover: fadeout(@color-pink-minBtn, 10%); @color-pink-maxBtn-hover: fadeout(@color-pink-maxBtn, 10%); @color-pink-closeBtn-hover: fadeout(@color-pink-closeBtn, 10%); @@ -398,13 +419,16 @@ @color-purple-player-progress-bar1: darken(@color-purple-theme_2, 12%); @color-purple-player-progress-bar2: lighten(@color-purple-theme, 12%); @color-purple-player-status-text: lighten(@color-purple-theme_2-font, 10%); +@color-purple-player-detail-play-btn: lighten(@color-purple-theme, 7%); @color-purple-tab-btn-background: fadeout(lighten(@color-purple-theme, 10%), 80%); @color-purple-tab-btn-background-hover: @color-purple-theme_2-hover; @color-purple-tab-border-top: fadeout(lighten(@color-purple-theme, 5%), 50%); @color-purple-tab-border-bottom: lighten(@color-purple-theme, 5%); +@color-purple-hideBtn: #3bc2b2; @color-purple-minBtn: #85c43b; @color-purple-maxBtn: #e7aa36; @color-purple-closeBtn: #ea6e4d; +@color-purple-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-purple-minBtn-hover: fadeout(@color-purple-minBtn, 10%); @color-purple-maxBtn-hover: fadeout(@color-purple-maxBtn, 10%); @color-purple-closeBtn-hover: fadeout(@color-purple-closeBtn, 10%); @@ -447,13 +471,16 @@ @color-grey-player-progress-bar1: darken(@color-grey-theme_2, 12%); @color-grey-player-progress-bar2: lighten(@color-grey-theme, 12%); @color-grey-player-status-text: lighten(@color-grey-theme_2-font, 10%); +@color-grey-player-detail-play-btn: lighten(@color-grey-theme, 7%); @color-grey-tab-btn-background: fadeout(lighten(@color-grey-theme, 10%), 80%); @color-grey-tab-btn-background-hover: @color-grey-theme_2-hover; @color-grey-tab-border-top: fadeout(lighten(@color-grey-theme, 5%), 50%); @color-grey-tab-border-bottom: lighten(@color-grey-theme, 5%); +@color-grey-hideBtn: #3bc2b2; @color-grey-minBtn: #85c43b; @color-grey-maxBtn: #e7aa36; @color-grey-closeBtn: #ea6e4d; +@color-grey-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-grey-minBtn-hover: fadeout(@color-grey-minBtn, 10%); @color-grey-maxBtn-hover: fadeout(@color-grey-maxBtn, 10%); @color-grey-closeBtn-hover: fadeout(@color-grey-closeBtn, 10%); @@ -497,15 +524,18 @@ @color-ming-player-progress-bar1: darken(@color-ming-theme_2, 12%); @color-ming-player-progress-bar2: lighten(@color-ming-theme, 12%); @color-ming-player-status-text: lighten(@color-ming-theme_2-font, 10%); +@color-ming-player-detail-play-btn: lighten(@color-ming-theme, 10%); @color-ming-tab-border-top: fadeout(lighten(@color-ming-theme, 25%), 70%); @color-ming-tab-border-bottom: lighten(@color-ming-theme, 35%); @color-ming-tab-btn-background: fadeout(lighten(@color-ming-theme, 10%), 80%); @color-ming-tab-btn-background-hover: @color-ming-theme_2-hover; @color-ming-tab-border-top: fadeout(lighten(@color-ming-theme, 5%), 50%); @color-ming-tab-border-bottom: lighten(@color-ming-theme, 5%); +@color-ming-hideBtn: #3bc2b2; @color-ming-minBtn: #85c43b; @color-ming-maxBtn: #e7aa36; @color-ming-closeBtn: #ea6e4d; +@color-ming-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-ming-minBtn-hover: fadeout(@color-ming-minBtn, 10%); @color-ming-maxBtn-hover: fadeout(@color-ming-maxBtn, 10%); @color-ming-closeBtn-hover: fadeout(@color-ming-closeBtn, 10%); @@ -548,13 +578,16 @@ @color-mid_autumn-player-progress-bar1: darken(@color-mid_autumn-theme_2, 12%); @color-mid_autumn-player-progress-bar2: lighten(@color-mid_autumn-theme, 12%); @color-mid_autumn-player-status-text: lighten(@color-mid_autumn-theme_2-font, 10%); +@color-mid_autumn-player-detail-play-btn: lighten(@color-mid_autumn-theme, 7%); @color-mid_autumn-tab-btn-background: fadeout(lighten(@color-mid_autumn-theme, 10%), 80%); @color-mid_autumn-tab-btn-background-hover: @color-mid_autumn-theme_2-hover; @color-mid_autumn-tab-border-top: fadeout(lighten(@color-mid_autumn-theme, 5%), 50%); @color-mid_autumn-tab-border-bottom: lighten(@color-mid_autumn-theme, 5%); +@color-mid_autumn-hideBtn: #3bc2b2; @color-mid_autumn-minBtn: #85c43b; @color-mid_autumn-maxBtn: #e7aa36; @color-mid_autumn-closeBtn: #ea6e4d; +@color-mid_autumn-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-mid_autumn-minBtn-hover: fadeout(@color-mid_autumn-minBtn, 10%); @color-mid_autumn-maxBtn-hover: fadeout(@color-mid_autumn-maxBtn, 10%); @color-mid_autumn-closeBtn-hover: fadeout(@color-mid_autumn-closeBtn, 10%); @@ -597,13 +630,16 @@ @color-naruto-player-progress-bar1: darken(@color-naruto-theme_2, 12%); @color-naruto-player-progress-bar2: lighten(@color-naruto-theme, 12%); @color-naruto-player-status-text: lighten(@color-naruto-theme_2-font, 10%); +@color-naruto-player-detail-play-btn: lighten(@color-naruto-theme, 7%); @color-naruto-tab-btn-background: fadeout(lighten(@color-naruto-theme, 10%), 80%); @color-naruto-tab-btn-background-hover: @color-naruto-theme_2-hover; @color-naruto-tab-border-top: fadeout(lighten(@color-naruto-theme, 5%), 50%); @color-naruto-tab-border-bottom: lighten(@color-naruto-theme, 5%); +@color-naruto-hideBtn: #3bc2b2; @color-naruto-minBtn: #85c43b; @color-naruto-maxBtn: #e7aa36; @color-naruto-closeBtn: #ea6e4d; +@color-naruto-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-naruto-minBtn-hover: fadeout(@color-naruto-minBtn, 10%); @color-naruto-maxBtn-hover: fadeout(@color-naruto-maxBtn, 10%); @color-naruto-closeBtn-hover: fadeout(@color-naruto-closeBtn, 10%); @@ -646,13 +682,16 @@ @color-happy_new_year-player-progress-bar1: darken(@color-happy_new_year-theme_2, 12%); @color-happy_new_year-player-progress-bar2: lighten(@color-happy_new_year-theme, 9%); @color-happy_new_year-player-status-text: lighten(@color-happy_new_year-theme_2-font, 10%); +@color-happy_new_year-player-detail-play-btn: lighten(@color-happy_new_year-theme, 7%); @color-happy_new_year-tab-btn-background: fadeout(lighten(@color-happy_new_year-theme, 10%), 80%); @color-happy_new_year-tab-btn-background-hover: @color-happy_new_year-theme_2-hover; @color-happy_new_year-tab-border-top: fadeout(lighten(@color-happy_new_year-theme, 5%), 50%); @color-happy_new_year-tab-border-bottom: lighten(@color-happy_new_year-theme, 5%); +@color-happy_new_year-hideBtn: #64a130; @color-happy_new_year-minBtn: #c4943b; @color-happy_new_year-maxBtn: #e7aa36; @color-happy_new_year-closeBtn: #ea6e4d; +@color-happy_new_year-hideBtn-hover: fadeout(@color-hideBtn, 10%); @color-happy_new_year-minBtn-hover: fadeout(@color-happy_new_year-minBtn, 10%); @color-happy_new_year-maxBtn-hover: fadeout(@color-happy_new_year-maxBtn, 10%); @color-happy_new_year-closeBtn-hover: fadeout(@color-happy_new_year-closeBtn, 10%); diff --git a/src/renderer/components/core/Aside.vue b/src/renderer/components/core/Aside.vue index 07146070..849ce95c 100644 --- a/src/renderer/components/core/Aside.vue +++ b/src/renderer/components/core/Aside.vue @@ -66,6 +66,11 @@ export default { display: flex; flex-flow: column nowrap; } +:global(.nd) { + .aside { + -webkit-app-region: no-drag; + } +} .logo { box-sizing: border-box; padding: 12% 13%; diff --git a/src/renderer/components/core/Icons.vue b/src/renderer/components/core/Icons.vue index 6a8492ef..10ce2f23 100644 --- a/src/renderer/components/core/Icons.vue +++ b/src/renderer/components/core/Icons.vue @@ -70,6 +70,9 @@ svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/19 g#icon-down // 0 0 451.847 451.847 path(fill='currentColor' d='M225.923,354.706c-8.098,0-16.195-3.092-22.369-9.263L9.27,151.157c-12.359-12.359-12.359-32.397,0-44.751c12.354-12.354,32.388-12.354,44.748,0l171.905,171.915l171.906-171.909c12.359-12.354,32.391-12.354,44.744,0c12.365,12.354,12.365,32.392,0,44.751L248.292,345.449C242.115,351.621,234.018,354.706,225.923,354.706z') + g#icon-back + // 0 0 512 512 + path(fill='currentColor' d='M511.563,434.259c-1.728-142.329-124.42-258.242-277.087-263.419V95.999c0-17.645-14.342-31.999-31.974-31.999 c-7.931,0-15.591,3.042-21.524,8.562c0,0-134.828,124.829-173.609,163.755C2.623,241.109,0,248.088,0,255.994 c0,7.906,2.623,14.885,7.369,19.687c38.781,38.915,173.609,163.745,173.609,163.745c5.933,5.521,13.593,8.562,21.524,8.562 c17.631,0,31.974-14.354,31.974-31.999v-74.591c153.479,2.156,255.792,50.603,255.792,95.924c0,5.896,4.767,10.666,10.658,10.666 c0.167,0.021,0.333,0.01,0.416,0c5.891,0,10.658-4.771,10.658-10.666C512,436.259,511.854,435.228,511.563,434.259z') g#icon-refresh // 0 0 512 512 path(fill='currentColor' d='M440.65 12.57l4 82.77A247.16 247.16 0 0 0 255.83 8C134.73 8 33.91 94.92 12.29 209.82A12 12 0 0 0 24.09 224h49.05a12 12 0 0 0 11.67-9.26 175.91 175.91 0 0 1 317-56.94l-101.46-4.86a12 12 0 0 0-12.57 12v47.41a12 12 0 0 0 12 12H500a12 12 0 0 0 12-12V12a12 12 0 0 0-12-12h-47.37a12 12 0 0 0-11.98 12.57zM255.83 432a175.61 175.61 0 0 1-146-77.8l101.8 4.87a12 12 0 0 0 12.57-12v-47.4a12 12 0 0 0-12-12H12a12 12 0 0 0-12 12V500a12 12 0 0 0 12 12h47.35a12 12 0 0 0 12-12.6l-4.15-82.57A247.17 247.17 0 0 0 255.83 504c121.11 0 221.93-86.92 243.55-201.82a12 12 0 0 0-11.8-14.18h-49.05a12 12 0 0 0-11.67 9.26A175.86 175.86 0 0 1 255.83 432z') diff --git a/src/renderer/components/core/Player.vue b/src/renderer/components/core/Player.vue index 50fa8ad7..73af8f76 100644 --- a/src/renderer/components/core/Player.vue +++ b/src/renderer/components/core/Player.vue @@ -1,10 +1,10 @@ <template lang="pug"> div(:class="$style.player") - div(:class="$style.left" @click="handleToMusicLocation") + div(:class="$style.left" @contextmenu="handleToMusicLocation" @click="showPlayerDetail") img(v-if="musicInfo.img" :src="musicInfo.img" @error="imgError") svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' height='100%' viewBox='0 0 60 60' space='preserve') use(:xlink:href='`#${$style.iconPic}`') - div(:class="$style.right") + div(:class="$style.right" v-if="!isShowPlayerDetail") div(:class="$style.column1") div(:class="$style.container") div(:class="$style.title" @click="handleCopy(title)" :title="title + $t('core.player.copy_title')") {{title}} @@ -25,14 +25,21 @@ div(:class="$style.player") svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve') use(xlink:href='#icon-play') div(:class="$style.column2") - div(:class="$style.progress") + div(:class="$style.progress" v-if="!isShowPlayerDetail") //- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ transform: `scaleX(${progress || 0})` }") div(:class="[$style.progressBar, $style.progressBar2, isActiveTransition ? $style.barTransition : '']" @transitionend="handleTransitionEnd" :style="{ transform: `scaleX(${progress || 0})` }") - div(:class="$style.progressMask" @click='setProgess' ref="dom_progress") + div(:class="$style.progressMask" @click='handleSetProgress' ref="dom_progress") div(:class="$style.column3") span {{nowPlayTimeStr}} - span(:class="$style.statusText") {{status}} + span(:class="$style.statusText") {{statusText}} span {{maxPlayTimeStr}} + //- transition(enter-active-class="animated lightSpeedIn" + transition(enter-active-class="animated lightSpeedIn" + leave-active-class="animated slideOutDown") + core-player-detail(v-if="isShowPlayerDetail" :musicInfo="musicInfo" + :lyric="lyric" :list="list" :listId="listId" + :playInfo="{ nowPlayTimeStr, maxPlayTimeStr, progress, nowPlayTime, status }" + :isPlay="isPlay" @action="handlePlayDetailAction") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' style="display: none;") defs g(:id="$style.iconPic") @@ -64,19 +71,21 @@ export default { nowPlayTime: 0, maxPlayTime: 0, isPlay: false, - status: '^-^', + status: '', + statusText: '^-^', musicInfo: { songmid: null, img: null, lrc: null, url: null, - name: '^', - singer: '^', + name: '', + singer: '', + album: '', }, targetSong: null, pregessWidth: 0, lyric: { - lrc: null, + lines: [], text: '', line: 0, }, @@ -98,14 +107,14 @@ export default { }, computed: { ...mapGetters(['setting']), - ...mapGetters('player', ['list', 'playIndex', 'changePlay', 'listId']), + ...mapGetters('player', ['list', 'playIndex', 'changePlay', 'listId', 'isShowPlayerDetail']), // pic() { // return this.musicInfo.img ? this.musicInfo.img : '' // }, title() { return this.musicInfo.name ? `${this.musicInfo.name} - ${this.musicInfo.singer}` - : '' + : '^-^' }, nowPlayTimeStr() { return this.nowPlayTime ? formatPlayTime2(this.nowPlayTime) : '00:00' @@ -123,7 +132,7 @@ export default { mounted() { this.init() this.$nextTick(() => { - this.setProgessWidth() + this.setProgressWidth() }) this.handleSaveVolume = debounce(volume => { this.setVolume(volume) @@ -190,6 +199,7 @@ export default { 'setPlayIndex', 'fixPlayIndex', 'resetChangePlay', + 'visiblePlayerDetail', ]), ...mapMutations(['setVolume']), ...mapMutations('list', ['updateMusicInfo']), @@ -205,19 +215,20 @@ export default { this.audio.addEventListener('playing', () => { console.log('开始播放') - this.status = this.$t('core.player.playing') + this.statusText = this.$t('core.player.playing') + this.status = '' this.startPlay() }) this.audio.addEventListener('pause', () => { console.log('暂停播放') - this.lyric.lrc.pause() + window.lrc.pause() this.stopPlay() - // this.status = this.$t('core.player.stop') + // this.status = this.statusText = this.$t('core.player.stop') }) this.audio.addEventListener('ended', () => { console.log('播放完毕') this.stopPlay() - this.status = this.$t('core.player.end') + this.status = this.statusText = this.$t('core.player.end') this.handleNext() }) this.audio.addEventListener('error', () => { @@ -230,12 +241,12 @@ export default { if (!this.audioErrorTime) this.audioErrorTime = this.audio.currentTime // 记录出错的播放时间 this.retryNum++ this.setUrl(this.list[this.playIndex], true) - this.status = this.$t('core.player.refresh_url') + this.status = this.statusText = this.$t('core.player.refresh_url') return } this.sendProgressEvent(this.progress, 'error') - this.status = this.$t('core.player.error') + this.status = this.statusText = this.$t('core.player.error') this.addDelayNextTimeout() }) this.audio.addEventListener('loadeddata', () => { @@ -245,10 +256,10 @@ export default { this.audioErrorTime = 0 } if (!this.targetSong.interval && this.listId != 'download') this.updateMusicInfo({ id: 'default', index: this.playIndex, data: { interval: formatPlayTime2(this.maxPlayTime) } }) - this.status = this.$t('core.player.loading') + this.status = this.statusText = this.$t('core.player.loading') }) this.audio.addEventListener('loadstart', () => { - this.status = this.$t('core.player.loading') + this.status = this.statusText = this.$t('core.player.loading') }) this.audio.addEventListener('canplay', () => { console.log('加载完成开始播放') @@ -260,20 +271,20 @@ export default { if (this.mediaBuffer.timeout) { this.clearBufferTimeout() } - // if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000) - this.status = this.$t('core.player.loading') + // if (this.musicInfo.lrc) window.lrc.play(this.audio.currentTime * 1000) + this.status = this.statusText = this.$t('core.player.loading') }) // this.audio.addEventListener('canplaythrough', () => { // console.log('音乐加载完毕') // // if (this.musicInfo.lyric.orgLrc) this.musicInfo.lyric.lrc.play(this.audio.currentTime * 1000) - // this.status = '播放中...' + // this.status = this.statusText = '播放中...' // }) this.audio.addEventListener('emptied', () => { this.mediaBuffer.playTime = 0 this.clearBufferTimeout() // console.log('媒介资源元素突然为空,网络错误 or 切换歌曲?') - // this.status = '媒介资源元素突然为空,网络错误?' + // this.status = this.statusText = '媒介资源元素突然为空,网络错误?' }) this.audio.addEventListener('timeupdate', () => { @@ -285,17 +296,22 @@ export default { // console.log('缓冲中...') this.stopPlay() this.startBuffering() - this.status = this.$t('core.player.buffering') + this.status = this.statusText = this.$t('core.player.buffering') }) - this.lyric.lrc = new Lyric({ + window.lrc = new Lyric({ onPlay: (line, text) => { this.lyric.text = text this.lyric.line = line - this.status = text + this.statusText = text // console.log(line, text) }, - offset: 150, + onSetLyric: lines => { // listening lyrics seting event + // console.log(lines) // lines is array of all lyric text + this.lyric.lines = lines + this.lyric.line = 0 + }, + offset: 100, }) }, async play() { @@ -314,6 +330,7 @@ export default { this.musicInfo.songmid = targetSong.musicInfo.songmid this.musicInfo.singer = targetSong.musicInfo.singer this.musicInfo.name = targetSong.musicInfo.name + this.musicInfo.album = targetSong.albumName this.audio.src = filePath // console.log(filePath) this.setImg(targetSong.musicInfo) @@ -322,6 +339,7 @@ export default { this.musicInfo.songmid = targetSong.songmid this.musicInfo.singer = targetSong.singer this.musicInfo.name = targetSong.name + this.musicInfo.album = targetSong.albumName this.setUrl(targetSong) this.setImg(targetSong) this.setLrc(targetSong) @@ -341,7 +359,7 @@ export default { this.handleNext() }, 5000) }, - async handleNext() { + async filterList() { // if (this.list.listName === null) return let list if (this.listId == 'download') { @@ -353,21 +371,23 @@ export default { } else if (this.isAPITemp) { list = this.list.filter(s => s.source == 'kw') } else { - list = [...this.list] + list = this.list } + return list + }, + async handlePrev() { + // console.log(playIndex) + let list = await this.filterList() if (!list.length) return this.setPlayIndex(-1) let playIndex = this.list === list ? this.playIndex : list.indexOf(this.list[this.playIndex]) - // console.log(playIndex) let index switch (this.setting.player.togglePlayMethod) { - case 'listLoop': - index = this.hanldeListLoop(list, playIndex) - break case 'random': index = this.hanldeListRandom(list, playIndex) break + case 'listLoop': case 'list': - index = this.hanldeListNext(list, playIndex) + index = playIndex === 0 ? list.length - 1 : playIndex - 1 break default: return @@ -376,44 +396,62 @@ export default { if (this.list !== list) index = this.list.indexOf(list[index]) this.setPlayIndex(index) }, - hanldeListLoop(list, index) { - return index === list.length - 1 ? 0 : index + 1 - }, - hanldeListNext(list, index) { - return index === list.length - 1 ? -1 : index + 1 + async handleNext() { + // if (this.list.listName === null) return + let list = await this.filterList() + if (!list.length) return this.setPlayIndex(-1) + let playIndex = this.list === list ? this.playIndex : list.indexOf(this.list[this.playIndex]) + // console.log(playIndex) + let index + switch (this.setting.player.togglePlayMethod) { + case 'listLoop': + index = playIndex === list.length - 1 ? 0 : playIndex + 1 + break + case 'random': + index = this.hanldeListRandom(list, playIndex) + break + case 'list': + index = playIndex === list.length - 1 ? -1 : playIndex + 1 + break + default: + return + } + if (index < 0) return + if (this.list !== list) index = this.list.indexOf(list[index]) + this.setPlayIndex(index) }, hanldeListRandom(list, index) { return getRandom(0, list.length) }, startPlay() { this.isPlay = true - if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000) + if (this.musicInfo.lrc) window.lrc.play(this.audio.currentTime * 1000) this.setAppTitle() this.sendProgressEvent(this.progress, 'normal') }, stopPlay() { this.isPlay = false - this.lyric.lrc.pause() + window.lrc.pause() this.sendProgressEvent(this.progress, 'paused') this.clearAppTitle() }, - setProgess(e) { - if (!this.audio.src) return - // this.isActiveTransition = true - this.$nextTick(() => { - const time = (e.offsetX / this.pregessWidth) * this.maxPlayTime - if (this.audioErrorTime) this.audioErrorTime = time - if (this.mediaBuffer.playTime) { - this.clearBufferTimeout() - this.mediaBuffer.playTime = time - this.startBuffering() - } - this.audio.currentTime = time - - if (!this.isPlay) this.audio.play() - }) + handleSetProgress(event) { + this.setProgress(event.offsetX / this.pregessWidth) }, - setProgessWidth() { + setProgress(pregress) { + if (!this.audio.src) return + const time = pregress * this.maxPlayTime + if (this.audioErrorTime) this.audioErrorTime = time + if (this.mediaBuffer.playTime) { + this.clearBufferTimeout() + this.mediaBuffer.playTime = time + this.startBuffering() + } + this.audio.currentTime = time + + if (!this.isPlay) this.audio.play() + }, + setProgressWidth() { this.pregessWidth = parseInt( window.getComputedStyle(this.$refs.dom_progress, null).width, ) @@ -445,14 +483,14 @@ export default { setUrl(targetSong, isRefresh, isRetryed = false) { let type = this.getPlayType(this.setting.player.highQuality, targetSong) this.musicInfo.url = targetSong.typeUrl[type] - this.status = this.$t('core.player.geting_url') + this.status = this.statusText = this.$t('core.player.geting_url') return this.getUrl({ musicInfo: targetSong, type, isRefresh }).then(() => { this.audio.src = this.musicInfo.url = targetSong.typeUrl[type] }).catch(err => { if (err.message == requestMsg.cancelRequest) return if (!isRetryed) return this.setUrl(targetSong, isRefresh, true) - this.status = err.message + this.status = this.statusText = err.message this.addDelayNextTimeout() return Promise.reject(err) }) @@ -477,25 +515,28 @@ export default { lrcP .then(() => { - this.lyric.lrc.setLyric(this.musicInfo.lrc) - if (this.isPlay && (this.musicInfo.url || this.listId == 'download')) this.lyric.lrc.play(this.audio.currentTime * 1000) + window.lrc.setLyric(this.musicInfo.lrc) + if (this.isPlay && (this.musicInfo.url || this.listId == 'download')) window.lrc.play(this.audio.currentTime * 1000) }) .catch(() => { - this.status = this.$t('core.player.lyric_error') + this.status = this.statusText = this.$t('core.player.lyric_error') }) }, handleRemoveMusic() { this.stopPlay() this.audio.src = null this.audio.removeAttribute('src') - this.status = '^-^' + this.statusText = '^-^' this.musicInfo.img = null - this.musicInfo.name = this.musicInfo.singer = '^' + this.status = this.musicInfo.name = this.musicInfo.singer = '' this.musicInfo.songmid = null this.musicInfo.lrc = null this.musicInfo.url = null this.nowPlayTime = 0 this.maxPlayTime = 0 + this.lyric.lines = [] + this.lyric.line = 0 + this.lyric.text = 0 }, sendProgressEvent(status, mode) { // console.log(status) @@ -512,7 +553,6 @@ export default { }, handleVolumeMsDown(e) { this.volumeEvent.isMsDown = true - this.volumeEvent.isMsMoved = false this.volumeEvent.msDownX = e.clientX let val = e.offsetX / 70 @@ -538,7 +578,7 @@ export default { clipboardWriteText(text) }, handleResize() { - this.setProgessWidth() + this.setProgressWidth() }, handleToMusicLocation() { if (this.listId == 'download') return @@ -551,6 +591,9 @@ export default { }, }) }, + showPlayerDetail() { + this.visiblePlayerDetail(true) + }, handleTransitionEnd(e) { // console.log(e) this.isActiveTransition = false @@ -593,6 +636,24 @@ export default { this.setMediaDeviceId('default') }) }, + handlePlayDetailAction({ type, data }) { + switch (type) { + case 'prev': + this.handlePrev() + break + case 'togglePlay': + this.togglePlay() + break + case 'next': + this.handleNext() + break + case 'progress': + this.setProgress(data) + break + case 'volume': + break + } + }, }, } </script> @@ -609,13 +670,14 @@ export default { border-top: 2px solid @color-theme; box-sizing: border-box; display: flex; - z-index: 1; + z-index: 2; * { box-sizing: border-box; } } .left { width: @height-player - 2; + height: @height-player - 2; color: @color-theme; transition: @transition-theme; transition-property: color; @@ -863,7 +925,6 @@ each(@themes, { .volume-bar { background-color: ~'@{color-@{value}-theme}'; - box-shadow: 0 0 2px rgba(0, 0, 0, 0.3); } @@ -875,7 +936,6 @@ each(@themes, { } .progress-bar2 { background-color: ~'@{color-@{value}-player-progress-bar2}'; - box-shadow: 0 0 2px rgba(0, 0, 0, 0.2); } .column3 { diff --git a/src/renderer/components/core/PlayerDetail.vue b/src/renderer/components/core/PlayerDetail.vue new file mode 100644 index 00000000..aa6106ab --- /dev/null +++ b/src/renderer/components/core/PlayerDetail.vue @@ -0,0 +1,706 @@ +<template lang="pug"> + div(:class="$style.container" @contextmenu="handleContextMenu") + //- div(:class="$style.bg" :style="bgStyle") + //- div(:class="$style.bg2") + div(:class="$style.header") + div(:class="$style.control") + button(type="button" :class="$style.hide" :title="$t('core.player.hide_detail')" @click="visiblePlayerDetail(false)") + button(type="button" :class="$style.min" :title="$t('core.toolbar.min')" @click="min") + //- button(type="button" :class="$style.max" @click="max") + button(type="button" :class="$style.close" :title="$t('core.toolbar.close')" @click="close") + + div(:class="$style.main") + div(:class="$style.left") + div(:class="$style.info") + div(:class="$style.img") + img(:src="musicInfo.img" v-if="musicInfo.img") + div(:class="$style.description") + p {{$t('core.player.name')}}{{musicInfo.name}} + p {{$t('core.player.singer')}}{{musicInfo.singer}} + p(v-if="musicInfo.album") {{$t('core.player.album')}}{{musicInfo.album}} + //- div(:class="$style.list") + ul + + div(:class="$style.right") + div(:class="[$style.lyric, lyricEvent.isMsDown ? $style.draging : null]" @mousedown="handleLyricMouseDown" ref="dom_lyric") + div(:class="$style.lyricSpace") + p(v-for="(info, index) in lyric.lines" :key="index" :class="lyric.line == index ? $style.lrcActive : null") {{info.text}} + div(:class="$style.lyricSpace") + div(:class="$style.footer") + div(:class="$style.left") + div(:class="$style.progressContainer") + div(:class="$style.progressContent") + div(:class="$style.progress") + //- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ transform: `scaleX(${progress || 0})` }") + div(:class="[$style.progressBar, $style.progressBar2, isActiveTransition ? $style.barTransition : '']" @transitionend="handleTransitionEnd" :style="{ transform: `scaleX(${playInfo.progress || 0})` }") + div(:class="$style.progressMask" @click='setProgress' ref="dom_progress") + div(:class="$style.timeLabel") + span(style="margin-left: 15px") {{playInfo.status}} + div + span {{playInfo.nowPlayTimeStr}} + span(style="margin: 0 5px;") / + span {{playInfo.maxPlayTimeStr}} + div(:class="$style.playControl") + //- div(:class="$style.playBtn") + //- div(:class="$style.playBtn") + div(:class="$style.playBtn" @click="$emit('action', { type: 'prev' })" style="transform: rotate(180deg);" :title="$t('core.player.prev')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') + use(xlink:href='#icon-nextMusic') + div(:class="$style.playBtn" :title="isPlay ? $t('core.player.pause') : $t('core.player.play')" @click="$emit('action', { type: 'togglePlay' })") + svg(v-if="isPlay" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve') + use(xlink:href='#icon-pause') + svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve') + use(xlink:href='#icon-play') + div(:class="$style.playBtn" @click="$emit('action', { type: 'next' })" :title="$t('core.player.next')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') + use(xlink:href='#icon-nextMusic') +</template> + + +<script> +import { mapGetters, mapMutations } from 'vuex' +import { rendererSend } from 'common/ipc' +import { scrollTo } from '../../utils' + +let cancelScrollFn = null + +export default { + props: { + musicInfo: { + type: Object, + default() { + return { + songmid: null, + img: null, + lrc: null, + url: null, + name: '^', + singer: '^', + } + }, + }, + lyric: { + type: Object, + default() { + return { + line: 0, + text: '', + lines: [], + } + }, + }, + playInfo: { + type: Object, + default() { + return { + nowPlayTimeStr: '00:00', + maxPlayTimeStr: '00:00', + progress: 0, + nowPlayTime: 0, + status: 0, + } + }, + }, + list: { + type: Array, + default() { + return [] + }, + }, + listId: { + type: String, + default() { + return '' + }, + }, + isPlay: { + type: Boolean, + default: false, + }, + }, + watch: { + 'musicInfo.img': { + handler(n) { + if (n) { + this.bgStyle.backgroundImage = `url(${n})` + } + }, + immediate: true, + }, + 'lyric.lines': { + handler() { + this.$nextTick(() => { + this.dom_lines = this.$refs.dom_lyric.querySelectorAll('p') + this.handleScrollLrc() + }) + }, + immediate: true, + }, + 'lyric.line': { + handler(n) { + this.handleScrollLrc() + }, + immediate: true, + }, + 'playInfo.nowPlayTime'(n, o) { + if (Math.abs(n - o) > 2) this.isActiveTransition = true + }, + }, + data() { + return { + bgStyle: { + backgroundImage: null, + }, + dom_lines: [], + isActiveTransition: false, + pregessWidth: 0, + clickTime: 0, + volumeEvent: { + isMsDown: false, + msDownX: 0, + msDownValue: 0, + }, + lyricEvent: { + isMsDown: false, + msDownY: 0, + msDownScrollY: 0, + isStopScroll: false, + timeout: null, + }, + } + }, + mounted() { + this.$nextTick(() => { + this.setProgressWidth() + }) + document.addEventListener('mousemove', this.handleMouseMsMove) + document.addEventListener('mouseup', this.handleMouseMsUp) + window.addEventListener('resize', this.handleResize) + }, + beforeDestroy() { + this.clearLyricScrollTimeout() + document.removeEventListener('mousemove', this.handleMouseMsMove) + document.removeEventListener('mouseup', this.handleMouseMsUp) + window.removeEventListener('resize', this.handleResize) + }, + computed: { + ...mapGetters('player', ['isShowPlayerDetail']), + }, + methods: { + ...mapMutations('player', [ + 'visiblePlayerDetail', + ]), + handleResize() { + this.setProgressWidth() + }, + handleScrollLrc() { + if (!this.dom_lines.length) return + if (cancelScrollFn) { + cancelScrollFn() + cancelScrollFn = null + } + if (this.lyricEvent.isStopScroll) return + let dom_p = this.dom_lines[this.lyric.line] + cancelScrollFn = scrollTo(this.$refs.dom_lyric, dom_p ? (dom_p.offsetTop - this.$refs.dom_lyric.clientHeight * 0.38) : 0) + }, + handleTransitionEnd() { + this.isActiveTransition = false + }, + setProgress(event) { + this.$emit('action', { + type: 'progress', + data: event.offsetX / this.pregessWidth, + }) + }, + setProgressWidth() { + this.pregessWidth = parseInt( + window.getComputedStyle(this.$refs.dom_progress, null).width, + ) + }, + handleContextMenu() { + if (window.performance.now() - this.clickTime > 400) { + this.clickTime = window.performance.now() + return + } + this.clickTime = 0 + this.visiblePlayerDetail(false) + }, + handleLyricMouseDown(e) { + // console.log(e) + this.lyricEvent.isMsDown = true + this.lyricEvent.msDownY = e.clientY + this.lyricEvent.msDownScrollY = this.$refs.dom_lyric.scrollTop + }, + handleMouseMsUp(e) { + this.lyricEvent.isMsDown = this.volumeEvent.isMsDown = false + }, + handleMouseMsMove(e) { + if (this.lyricEvent.isMsDown) { + if (!this.lyricEvent.isStopScroll) this.lyricEvent.isStopScroll = true + if (cancelScrollFn) { + cancelScrollFn() + cancelScrollFn = null + } + this.$refs.dom_lyric.scrollTop = this.lyricEvent.msDownScrollY + this.lyricEvent.msDownY - e.clientY + this.startLyricScrollTimeout() + } + + // if (this.volumeEvent.isMsDown) { + // let val = this.volumeEvent.msDownValue + (e.clientX - this.volumeEvent.msDownX) / 70 + // this.volume = val < 0 ? 0 : val > 1 ? 1 : val + // if (this.audio) this.audio.volume = this.volume + // } + + // console.log(val) + }, + startLyricScrollTimeout() { + this.clearLyricScrollTimeout() + this.lyricEvent.timeout = setTimeout(() => { + this.lyricEvent.timeout = null + this.lyricEvent.isStopScroll = false + if (!this.isPlay) return + this.handleScrollLrc() + }, 3000) + }, + clearLyricScrollTimeout() { + if (!this.lyricEvent.timeout) return + clearTimeout(this.lyricEvent.timeout) + this.lyricEvent.timeout = null + }, + min() { + rendererSend('min') + }, + max() { + rendererSend('max') + }, + close() { + rendererSend('close') + }, + }, +} +</script> + + +<style lang="less" module> +@import '../../assets/styles/layout.less'; + +@control-btn-width: @height-toolbar * .5; + +.container { + position: absolute; + display: flex; + flex-flow: column nowrap; + width: 100%; + height: 100%; + top: 0; + left: 0; + background-color: #fff; + z-index: 10; + // -webkit-app-region: drag; + overflow: hidden; + border-radius: 4px; + color: @color-theme_2-font; + border-left: 12px solid @color-theme; +} +.bg { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + background-size: 110% 110%; + filter: blur(60px); + z-index: -1; +} +.bg2 { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: -1; + background-color: rgba(255, 255, 255, .8); +} + +.header { + position: relative; + flex: 0 0 @height-toolbar; + -webkit-app-region: drag; +} +.control { + position: absolute; + right: 0; + top: 0; + display: flex; + align-items: center; + height: @height-toolbar; + -webkit-app-region: no-drag; + padding: 0 @control-btn-width * 0.6; + &:hover { + button:before { + opacity: 1; + } + } + + button { + position: relative; + width: @control-btn-width; + height: @control-btn-width; + background: none; + border: none; + display: flex; + justify-content: center; + align-items: center; + outline: none; + padding: 0; + cursor: pointer; + + button { + margin-left: @control-btn-width * .4; + } + + &:after { + content: ' '; + display: block; + border-radius: 50%; + width: 14px; + height: 14px; + transition: background-color 0.2s ease-in-out; + } + + &:before { + display: block; + position: absolute; + opacity: 0; + transition: opacity @transition-theme; + } + + &.hide:after { + background-color: @color-hideBtn; + } + &.min:after { + background-color: @color-minBtn; + } + &.max:after { + background-color: @color-maxBtn; + } + &.close:after { + background-color: @color-closeBtn; + } + + &.hide:hover:after { + background-color: @color-hideBtn-hover; + } + &.min:hover:after { + background-color: @color-minBtn-hover; + opacity: 1; + } + &.max:hover:after { + background-color: @color-maxBtn-hover; + } + &.close:hover:after { + background-color: @color-closeBtn-hover; + } + } +} + +.hide { + &:before { + content: '∨'; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + font-size: 14px; + line-height: 1; + color: #fff; + } +} + +.min { + &:before { + content: ' '; + width: 8px; + height: 2px; + left: @control-btn-width / 2 - 4; + top: @control-btn-width / 2 - 1; + background-color: #fff; + } +} + +.close { + &:before { + content: '×'; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + font-size: 14px; + line-height: 1; + color: #fff; + } +} + +.main { + flex: auto; + min-height: 0; + overflow: hidden; + display: flex; + padding: 0 30px; +} +.left { + flex: 0 0 40%; + overflow: hidden; +} +.info { + display: flex; + flex-flow: column nowrap; + align-items: center; +} +.img { + width: 300px; + height: 300px; + display: flex; + justify-content: flex-start; + align-items: center; + -webkit-app-region: drag; + + img { + max-width: 100%; + max-height: 100%; + border: 5px solid @color-theme-hover; + // border: 5px solid #fff; + } +} +.description { + width: 300px; + padding: 15px 0; + overflow: hidden; + p { + line-height: 1.5; + font-size: 14px; + overflow-wrap: break-word; + } +} + +.right { + flex: 0 0 60%; + // padding: 0 30px; + position: relative; + &:before { + position: absolute; + top: 0; + left: 0; + content: ' '; + height: 100px; + width: 100%; + background-image: linear-gradient(0deg,rgba(255,255,255,0) 0%,#fff 95%); + pointer-events: none; + } + &:after { + position: absolute; + bottom: 0; + left: 0; + content: ' '; + height: 100px; + width: 100%; + background-image: linear-gradient(-180deg,rgba(255,255,255,0) 0%,#fff 95%); + pointer-events: none; + } +} +.lyric { + text-align: center; + height: 100%; + overflow: hidden; + font-size: 16px; + cursor: grab; + &.draging { + cursor: grabbing; + } + p { + padding: 8px 0; + line-height: 1.2; + overflow-wrap: break-word; + transition: @transition-theme; + transition-property: color, font-size; + } +} +.lyric-space { + height: 70%; +} +.lrc-active { + color: @color-theme; + font-size: 1.2em; +} +.footer { + flex: 0 0 100px; + overflow: hidden; + display: flex; + align-items: center; +} +.left { + flex: auto; + display: flex; + flex-flow: column nowrap; + align-items: center; + padding-top: 13px; +} +.progress-container { + width: 100%; + position: relative; + padding: 3px 0; +} +.progress-content { + position: relative; + height: 15px; + padding: 5px 0; + width: 100%; +} +.progress { + height: 100%; + width: 100%; + border-radius: 0.2rem; + // overflow: hidden; + transition: @transition-theme; + transition-property: background-color; + background-color: @color-player-progress; + // background-color: #f5f5f5; + position: relative; + border-radius: @radius-progress-border; +} +.progress-mask { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + cursor: pointer; +} +.progress-bar { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + transform-origin: 0; + border-top-right-radius: @radius-progress-border; + border-bottom-right-radius: @radius-progress-border; +} +.progress-bar1 { + background-color: @color-player-progress-bar1; +} +.progress-bar2 { + background-color: @color-player-progress-bar2; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.3); + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; +} + +.bar-transition { + transition-property: transform; + transition-timing-function: ease-out; + transition-duration: 0.2s; +} +.time-label { + width: 100%; + display: flex; + justify-content: space-between; + span { + font-size: 13px; + } +} + +.play-control { + flex: none; + height: 100%; + display: flex; + justify-content: flex-end; + align-items: center; + padding: 0 25px; + color: @color-btn; +} +.play-btn { + height: 40%; + padding: 5px; + cursor: pointer; + flex: none; + transition: @transition-theme; + transition-property: color; + color: @color-player-detail-play-btn; + transition: opacity 0.1s ease; + opacity: 1; + cursor: pointer; + + +.play-btn { + margin-left: 10px; + } + svg { + fill: currentColor; + filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.2)); + } + &:hover { + opacity: 0.8; + } + &:active { + opacity: 0.6; + } +} + +each(@themes, { + :global(#container.@{value}) { + .container { + border-left-color: ~'@{color-@{value}-theme}'; + } + .control { + button { + // &.hide:after { + // background-color: ~'@{color-@{value}-hideBtn}'; + // } + &.hide:after { + background-color: ~'@{color-@{value}-hideBtn}'; + } + &.min:after { + background-color: ~'@{color-@{value}-minBtn}'; + } + &.max:after { + background-color: ~'@{color-@{value}-maxBtn}'; + } + &.close:after { + background-color: ~'@{color-@{value}-closeBtn}'; + } + + &.hide:hover:after { + background-color: ~'@{color-@{value}-hideBtn-hover}'; + } + &.min:hover:after { + background-color: ~'@{color-@{value}-minBtn-hover}'; + } + &.max:hover:after { + background-color: ~'@{color-@{value}-maxBtn-hover}'; + } + &.close:hover:after { + background-color: ~'@{color-@{value}-closeBtn-hover}'; + } + } + } + .img { + img { + border-color: ~'@{color-@{value}-theme-hover}'; + } + } + .lrc-active { + color: ~'@{color-@{value}-theme}'; + } + .progress { + background-color: ~'@{color-@{value}-player-progress}'; + } + .progress-bar1 { + background-color: ~'@{color-@{value}-player-progress-bar1}'; + } + .progress-bar2 { + background-color: ~'@{color-@{value}-player-progress-bar2}'; + } + .play-btn { + color: ~'@{color-@{value}-player-detail-play-btn}'; + } + } +}) + +</style> diff --git a/src/renderer/components/core/Toolbar.vue b/src/renderer/components/core/Toolbar.vue index f778c611..a0219eb8 100644 --- a/src/renderer/components/core/Toolbar.vue +++ b/src/renderer/components/core/Toolbar.vue @@ -141,6 +141,11 @@ export default { z-index: 2; position: relative; } +:global(.nd) { + .toolbar { + -webkit-app-region: no-drag; + } +} .input { -webkit-app-region: no-drag; position: absolute; @@ -166,7 +171,7 @@ export default { align-items: center; height: 100%; -webkit-app-region: no-drag; - padding: 0 @control-btn-width / 2; + padding: 0 @control-btn-width * 0.6; &:hover { button:before { opacity: 1; @@ -186,7 +191,7 @@ export default { padding: 0; cursor: pointer; + button { - margin-left: @control-btn-width / 2; + margin-left: @control-btn-width * 0.4; } &:after { diff --git a/src/renderer/lang/cns/core/player.json b/src/renderer/lang/cns/core/player.json index 86ebfc0d..36936ad5 100644 --- a/src/renderer/lang/cns/core/player.json +++ b/src/renderer/lang/cns/core/player.json @@ -3,6 +3,7 @@ "volume": "当前音量:", "pause": "暂停", "play": "播放", + "prev": "上一首", "next": "下一首", "playing": "播放中...", "stop": "暂停播放", @@ -12,5 +13,9 @@ "loading": "音乐加载中...", "buffering": "缓冲中...", "geting_url": "歌曲链接获取中...", - "lyric_error": "歌词获取失败" + "lyric_error": "歌词获取失败", + "hide_detail": "隐藏详情页", + "name": "歌曲名:", + "singer": "艺术家:", + "album": "专辑名:" } diff --git a/src/renderer/lang/cnt/core/player.json b/src/renderer/lang/cnt/core/player.json index 0b68ff96..513ea11f 100644 --- a/src/renderer/lang/cnt/core/player.json +++ b/src/renderer/lang/cnt/core/player.json @@ -3,6 +3,7 @@ "volume": "當前音量:", "pause": "暫停", "play": "播放", + "prev": "上一首", "next": "下一首", "playing": "播放中...", "stop": "暫停播放", @@ -12,5 +13,9 @@ "loading": "音樂加載中...", "buffering": "緩衝中...", "geting_url": "歌曲鏈接獲取中...", - "lyric_error": "歌詞獲取失敗" + "lyric_error": "歌詞獲取失敗", + "hide_detail": "隱藏詳情頁", + "name": "歌曲名:", + "singer": "藝術家:", + "album": "專輯名:" } diff --git a/src/renderer/lang/en/core/player.json b/src/renderer/lang/en/core/player.json index 4a4e883f..dff70093 100644 --- a/src/renderer/lang/en/core/player.json +++ b/src/renderer/lang/en/core/player.json @@ -3,6 +3,7 @@ "volume": "Volume: ", "pause": "Pause", "play": "Play", + "prev": "Prev", "next": "Next", "playing": "Now playing...", "stop": "Paused", @@ -12,5 +13,9 @@ "loading": "Music loading...", "buffering": "Buffering...", "geting_url": "Getting music link...", - "lyric_error": "Failed to get lyrics" + "lyric_error": "Failed to get lyrics", + "hide_detail": "Hide detail page", + "name": "Name: ", + "singer": "Artist: ", + "album": "Album: " } diff --git a/src/renderer/store/modules/player.js b/src/renderer/store/modules/player.js index f3c27621..2af51c1e 100644 --- a/src/renderer/store/modules/player.js +++ b/src/renderer/store/modules/player.js @@ -6,6 +6,7 @@ const state = { listId: null, playIndex: -1, changePlay: false, + isShowPlayerDetail: false, } let urlRequest @@ -18,6 +19,7 @@ const getters = { listId: state => state.listId, changePlay: satte => satte.changePlay, playIndex: state => state.playIndex, + isShowPlayerDetail: state => state.isShowPlayerDetail, } // actions @@ -87,6 +89,9 @@ const mutations = { resetChangePlay(state) { state.changePlay = false }, + visiblePlayerDetail(state, visible) { + state.isShowPlayerDetail = visible + }, } export default { diff --git a/src/renderer/utils/index.js b/src/renderer/utils/index.js index 17bbfa6b..4c6f677d 100644 --- a/src/renderer/utils/index.js +++ b/src/renderer/utils/index.js @@ -65,6 +65,7 @@ const easeInOutQuad = (t, b, c, d) => { export const scrollTo = (element, to, duration = 300, fn = () => {}) => { if (!element) return const start = element.scrollTop || element.scrollY || 0 + let cancel = false if (to > start) { let maxScrollTop = element.scrollHeight - element.clientHeight if (to > maxScrollTop) to = maxScrollTop @@ -87,12 +88,16 @@ export const scrollTo = (element, to, duration = 300, fn = () => {}) => { element.scrollTop = val } if (currentTime < duration) { + if (cancel) return fn() setTimeout(animateScroll, increment) } else { fn() } } animateScroll() + return () => { + cancel = true + } } /**