This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
appcat
/
sonist
Archived
1
0
Fork 0

增加ktv模式

2.x
宇天 2018-12-29 20:00:19 +08:00
parent 0221d8b8fe
commit 6f72645313
7 changed files with 276 additions and 188 deletions

View File

@ -18,20 +18,20 @@
- [x] 设置本地音乐目录 - [x] 设置本地音乐目录
- [x] 缓存歌词 - [x] 缓存歌词
- [x] 自动获取专辑封面和歌词,自动更新ID3信息 - [x] 自动获取专辑封面和歌词,自动更新ID3信息
- [ ] 酷狗音乐排行榜
- [x] 酷狗歌手列表(完成20%)
- [ ] 酷狗音乐MV
- [ ] 试听列表
- [x] 本地音乐 - [x] 本地音乐
- [x] 设置界面 - [x] 设置界面
- [x] KTV模式
- [x] 歌曲ID3信息修改
- [x] 酷狗歌手列表(完成20%)
- [ ] 酷狗音乐排行榜
- [ ] 酷狗音乐MV
- [ ] 试听列表
- [ ] 均衡器 - [ ] 均衡器
- [ ] 桌面歌词 - [ ] 桌面歌词
- [ ] 迷你模式 - [ ] 迷你模式
- [ ] KTV模式
- [ ] 多媒体快捷键 - [ ] 多媒体快捷键
- [ ] 酷狗账号直接登录(犹豫中) - [ ] 酷狗账号直接登录(犹豫中)
- [ ] 铃声制作(犹豫中) - [ ] 铃声制作(犹豫中)
- [x] 歌曲ID3信息修改
- [ ] 用户评论/点赞(取决于登陆功能是否开发) - [ ] 用户评论/点赞(取决于登陆功能是否开发)
- [ ] 试听下载 - [ ] 试听下载
- [ ] 歌曲质量选择 - [ ] 歌曲质量选择

File diff suppressed because one or more lines are too long

View File

@ -52,107 +52,107 @@ table {overflow:auto;display:table;width:100%;line-height:2.5rem;
::-webkit-scrollbar-thumb:hover {background:nth($ct, 1);} ::-webkit-scrollbar-thumb:hover {background:nth($ct, 1);}
.do-mod-app {display:flex;width:100%;height:100%;background:#fff; .do-mod-app {position:relative;display:flex;flex-flow:column wrap;width:100%;height:100%;background:#fff;
//
.title-bar {position:relative;z-index:9;display:flex;flex:0 5rem;
.menubar {position:absolute;left:1.2rem;top:0;z-index:99;width:auto;height:3rem;padding:.9rem 0;
.item {display:inline-block;width:1.2rem;height:1.2rem;margin:0 .2rem;background:url(/images/btn-grey.svg) no-repeat;background-size:cover;}
&.focus {
.quit {background-image:url(/images/btn-close.svg);}
.min {background-image:url(/images/btn-mini.svg);}
.max {background-image:url(/images/btn-maxi.svg);}
}
&:hover {
.quit {background-image:url(/images/btn-close_a.svg);}
.min {background-image:url(/images/btn-mini_a.svg);}
.max {background-image:url(/images/btn-maxi_a.svg);}
}
}
.menubar-win {position:absolute;right:1.2rem;top:0;z-index:99;width:auto;height:4rem;padding:.9rem 0;line-height:1.8rem;
.item,.opt {position:relative;display:inline-block;width:2.2rem;height:2.2rem;margin:0 .2rem;padding:.2rem;font-size:1.6rem;
}
.item:hover {transform:scale(1.1)}
.opt i {font-size:1.8rem;}
.opt-list {position:absolute;z-index:100;right:0;top:2.2rem;width:13rem;height:auto;padding:.8rem 0;background:#fff;box-shadow:0 .5rem 2rem rgba(0, 0, 0, .1);font-size:1.4rem;
span {display:flex;align-items:center;height:3rem;padding:0 2rem;line-height:3rem;
i {padding-right:.8rem;}
&.pipe {height:.1rem;margin:.5rem 0;border-bottom:.1rem solid nth($cp, 1)}
&:hover {background:nth($cp, 1)}
}
}
}
.sidebar {flex:0 1 22rem;position:relative;height:100%;background:nth($cp, 1);
//
.user-box {width:18rem;height:16.5rem;margin:4rem 2rem 0;text-align:center;
.avatar {overflow:hidden;width:12rem;height:12rem;margin:0 3rem;border:.6rem solid #fff;border-radius:50%;box-shadow:0 .5rem 1.5rem rgba(0, 0, 0, .15);} .btn-box {position:absolute;left:1.2rem;top:0;width:auto;height:3rem;padding:.9rem 0;
img {width:100%;height:100%;}
.uname {line-height:2;font-weight:normal;}
}
//
.music-box {width:100%;height:auto;padding:0 1.5rem;
dt.title {line-height:4rem;color:nth($cgr, 1)}
dd.item {height:3rem;margin:.3rem 0;padding:0 .8rem;line-height:3rem;color:nth($cgr, 3);
.icon {float:left;width:3rem;height:3rem;padding:0 .5rem;font-size:2.4rem;}
&:hover {padding-left:.9rem;color:nth($ct, 1);}
&.active {border-radius:.3rem;background:nth($ct, 1);color:#fff;}
&.disabled {opacity:.25}
}
}
//
.play-contrl {position:absolute;left:0;bottom:0;width:100%;height:8rem;background:rgba(255,255,255,.3);
.item {position:absolute;top:2rem;width:4rem;height:4rem;line-height:4rem;font-size:3.5rem;text-align:center;color:nth($ct, 2);@include ts();
&:hover {color:nth($cp, 3)}
&:active {color:nth($ct, 1);transform:scale(1.1)}
}
.prev {left:2.5rem;}
.play {left:50%;top:1.5rem;width:5rem;height:5rem;margin-left:-2.5rem;line-height:5rem;font-size:4.5rem;}
.next {right:2.5rem;}
}
}
//
.main {flex:1;display:flex;flex-flow:column wrap;
.item {display:inline-block;width:1.2rem;height:1.2rem;margin:0 .2rem;background:url(/images/btn-grey.svg) no-repeat;background-size:cover;}
&.focus {
.quit {background-image:url(/images/btn-close.svg);}
.min {background-image:url(/images/btn-mini.svg);}
.max {background-image:url(/images/btn-maxi.svg);}
}
&:hover {
.quit {background-image:url(/images/btn-close_a.svg);}
.min {background-image:url(/images/btn-mini_a.svg);}
.max {background-image:url(/images/btn-maxi_a.svg);}
}
}
.btn-box-win {position:absolute;right:1.2rem;top:0;width:auto;height:4rem;padding:.9rem 0;line-height:1.8rem;
.item,.opt {position:relative;display:inline-block;width:2.2rem;height:2.2rem;margin:0 .2rem;padding:.2rem;font-size:1.6rem;
}
.item:hover {transform:scale(1.1)}
.opt i {font-size:1.8rem;}
.opt-list {position:absolute;z-index:100;right:0;top:2.2rem;width:13rem;height:auto;padding:.8rem 0;background:#fff;box-shadow:0 .5rem 2rem rgba(0, 0, 0, .1);font-size:1.4rem;
span {display:flex;align-items:center;height:3rem;padding:0 2rem;line-height:3rem;
i {padding-right:.8rem;}
&.pipe {height:.1rem;margin:.5rem 0;border-bottom:.1rem solid nth($cp, 1)}
&:hover {background:nth($cp, 1)}
}
}
}
// //
.tool-bar {flex:0 1 5rem;padding:1rem; .holder {flex:0 22rem;height:100%;background:nth($cp, 1);}
.tools {flex:1;padding:1rem;
.search {position:relative;display:inline-block;line-height:3rem;} .search {position:relative;display:inline-block;line-height:3rem;}
.icon {position:absolute;right:0;top:0;width:2.6rem;height:3rem;} .icon {position:absolute;right:0;top:0;width:2.6rem;height:3rem;}
input {width:20rem;padding:0 1.3rem;border-radius:1.5rem;} input {width:20rem;padding:0 1.3rem;border-radius:1.5rem;}
} }
}
//
.main-body {flex:1;display:flex;
//
.sidebar {flex:0 22rem;position:relative;height:100%;background:nth($cp, 1);
//
.user-box {width:18rem;height:16.5rem;margin:0 2rem;text-align:center;
.avatar {overflow:hidden;width:12rem;height:12rem;margin:0 3rem;border:.6rem solid #fff;border-radius:50%;box-shadow:0 .5rem 1.5rem rgba(0, 0, 0, .15);}
img {width:100%;height:100%;}
.uname {line-height:2;font-weight:normal;}
}
//
.music-box {width:100%;height:auto;padding:0 1.5rem;
dt.title {line-height:4rem;color:nth($cgr, 1)}
dd.item {height:3rem;margin:.3rem 0;padding:0 .8rem;line-height:3rem;color:nth($cgr, 3);
.icon {float:left;width:3rem;height:3rem;padding:0 .5rem;font-size:2.4rem;}
&:hover {padding-left:.9rem;color:nth($ct, 1);}
&.active {border-radius:.3rem;background:nth($ct, 1);color:#fff;}
&.disabled {opacity:.25}
}
}
}
// //
.module {position:relative;flex:1;display:flex;flex-flow:column wrap;} .module {position:relative;flex:1;display:flex;flex-flow:column wrap;}
}
.contrl-bar {position:relative;z-index:99;display:flex;flex:0 8rem;background:nth($cp, 1);
//
.play-box {flex:0 22rem;display:flex;justify-content:center;align-items:center;height:8rem;padding:1rem 2rem;text-align:center;
.item {flex:0 5rem;margin:0 .5rem;line-height:1;font-size:4.2rem;color:nth($ct, 2);@include ts();
&:hover {color:nth($cg, 1)}
&:active {color:nth($ct, 1);transform:scale(1.1)}
}
.play {font-size:5rem;}
}
// //
.play-bar {position:relative;flex:0 1 8rem;display:flex;justify-content:center;align-items:center;background:#f5f6fc; .stat-box {position:relative;flex:1;display:flex;justify-content:center;align-items:center;
.song-stat {flex:1;height:8rem;margin:0 2rem 0 0; .song-stat {flex:1;height:8rem;margin:0 2rem 0 0;
@ -178,15 +178,47 @@ table {overflow:auto;display:table;width:100%;line-height:2.5rem;
} }
//
&.blur {background:rgba(255, 255, 255, .85);backdrop-filter:blur(1rem); &.blur {background:rgba(255, 255, 255, .85);backdrop-filter:blur(1rem);
.title-bar {
.holder {background:rgba(255, 255, 255, .3);}
.tools input {background:rgba(255, 255, 255, .8);}
}
.sidebar {background:rgba(255, 255, 255, .3); .main-body {
.play-contrl {background:transparent} .sidebar {background:rgba(255, 255, 255, .3);}
} }
.main { .contrl-bar {background:rgba(255, 255, 255, .35)}
.play-bar {background:rgba(255, 255, 255, .35)}
}
// ktv
&.ktv {
.contrl-bar {background:rgba(233, 233, 233, .1);
.play-box .item,
.stat-box .ctrl {color:#fff;}
}
}
.ktv-box {overflow:hidden;position:absolute;z-index:80;left:0;top:0;width:100%;height:100%;background-color:nth($cd, 3);background-size:cover;background-repeat:no-repeat;color:#fff;
.inner-content {display:flex;width:100%;height:100%;padding-bottom:8rem;background:rgba(29, 35, 44, 0.767);backdrop-filter:blur(1rem);
.info {flex:1.5;display:flex;flex-flow:column wrap;justify-content:center;align-items:center;padding:0 10rem;line-height:2;text-align:center;
img {display:block;width:100%;height:auto}
h3 {line-height:3;font-size:1.8rem;}
}
.lrc-box {flex:3.5;}
} }
} }

View File

@ -11,79 +11,126 @@
<script>window.LIBS_BASE_URL = location.origin + '/dist';window.__ENV_LANG__ = 'zh'</script> <script>window.LIBS_BASE_URL = location.origin + '/dist';window.__ENV_LANG__ = 'zh'</script>
<script type="module" src="js/app.js"></script> <script type="module" src="js/app.js"></script>
</head> </head>
<body class="do-fn-noselect" anot="app" :css="{'background-image':'url(' + curr.cover + ')'}"> <body class="do-fn-noselect" anot="app" :css="{'background-image': coverBG}">
<div class="do-mod-app" :class="{blur: isPlaying}"> <div class="do-mod-app" :class="{blur: isPlaying && !ktvMode, ktv: ktvMode}">
<nav class="menubar do-fn-nodrag" :if="theme === 1" :class="{focus: winFocus}"> <div class="title-bar do-fn-drag">
<i class="item quit" :click="quit(false)"></i>
<i class="item min" :click="minimize"></i>
<i class="item max" :click="maximize"></i>
</nav>
<nav class="menubar-win do-fn-nodrag"> <nav class="btn-box do-fn-nodrag" :if="theme === 1" :class="{focus: winFocus}">
<div class="opt"> <i class="item quit" :click="quit(false)"></i>
<i class="do-icon-menu-right" :click="toggleOptBox"></i> <i class="item min" :click="minimize"></i>
<section class="opt-list" :visible="optBoxShow"> <i class="item max" :click="maximize"></i>
<span><i class="do-icon-maximized"></i> 迷你模式</span> </nav>
<span :click="toggleModule('profile')">
<i class="do-icon-setting"></i> 首选项
</span>
<span class="pipe"></span>
<span :click="quit(true)"><i class="do-icon-logout"></i> 退出</span>
</section>
</div>
<span :if="theme === 2">
<i class="item do-icon-minimize" :click="minimize"></i>
<i class="item do-icon-maximize" :click="maximize"></i>
<i class="item do-icon-close" :click="quit(false)"></i>
</span>
</nav>
<!-- 背景点位标签 -->
<aside class="sidebar do-fn-drag"> <div class="holder"></div>
<div class="tools do-fn-drag">
<div class="user-box"> <div class="search do-fn-nodrag">
<div class="avatar"> <input class="do-ui-input" placeholder="搜索 音乐/歌手/专辑" value="">
<img src="/images/avatar.jpg" alt="yutent"> <i class="icon do-icon-search"></i>
</div> </div>
<h2 class="uname">yutent</h2>
</div> </div>
<dl class="music-box">
<dt class="title">酷狗在线</dt>
<dd class="item"
:click="toggleModule('rank')"
:class="{active: mod === 'rank'}">
<i class="s-icon-rank"></i> 排行榜
</dd>
<dd class="item"
:click="toggleModule('artist')"
:class="{active: mod === 'artist'}">
<i class="s-icon-singer"></i> 歌手
</dd>
<dd class="item disabled"
:click="toggleModule('mv')"
:class="{active: mod === 'mv'}">
<i class="s-icon-mv"></i> MV
</dd>
<dt class="title">我的音乐</dt> <nav class="btn-box-win do-fn-nodrag">
<dd class="item" <div class="opt">
:click="toggleModule('search')" <i class="do-icon-menu-right" :click="toggleOptBox"></i>
:class="{active: mod === 'search'}"> <section class="opt-list" :visible="optBoxShow">
<i class="s-icon-heart"></i> 试听列表 <span><i class="do-icon-maximized"></i> 迷你模式</span>
</dd> <span :click="toggleModule('profile')">
<dd class="item" <i class="do-icon-setting"></i> 首选项
:click="toggleModule('local')" </span>
:class="{active: mod === 'local'}"> <span class="pipe"></span>
<i class="s-icon-play-list"></i> 本地音乐 <span :click="quit(true)"><i class="do-icon-logout"></i> 退出</span>
</dd> </section>
</dl> </div>
<span :if="theme === 2">
<i class="item do-icon-minimize" :click="minimize"></i>
<i class="item do-icon-maximize" :click="maximize"></i>
<i class="item do-icon-close" :click="quit(false)"></i>
</span>
</nav>
</div>
<div class="play-contrl">
<div class="main-body">
<aside class="sidebar do-fn-drag">
<div class="user-box">
<div class="avatar">
<img src="/images/avatar.jpg" alt="yutent">
</div>
<h2 class="uname">yutent</h2>
</div>
<dl class="music-box">
<dt class="title">酷狗在线</dt>
<dd class="item"
:click="toggleModule('rank')"
:class="{active: mod === 'rank'}">
<i class="s-icon-rank"></i> 排行榜
</dd>
<dd class="item"
:click="toggleModule('artist')"
:class="{active: mod === 'artist'}">
<i class="s-icon-singer"></i> 歌手
</dd>
<dd class="item disabled"
:click="toggleModule('mv')"
:class="{active: mod === 'mv'}">
<i class="s-icon-mv"></i> MV
</dd>
<dt class="title">我的音乐</dt>
<dd class="item"
:click="toggleModule('search')"
:class="{active: mod === 'search'}">
<i class="s-icon-heart"></i> 试听列表
</dd>
<dd class="item"
:click="toggleModule('local')"
:class="{active: mod === 'local'}">
<i class="s-icon-play-list"></i> 本地音乐
</dd>
</dl>
</aside>
<content class="module" :include="views" data-cache="true"></content>
</div>
<div
class="ktv-box"
:if="ktvMode"
:css="{'background-image': coverBG}">
<div class="inner-content">
<div class="info">
<img :attr-src="curr.cover" />
<h3 :text="curr.title"></h3>
<section>歌手: {{curr.artist}}</section>
<section>专辑: {{curr.album}}</section>
</div>
<div class="lrc-box">
</div>
</div>
</div>
<div class="contrl-bar">
<div class="play-box">
<span class="item prev s-icon-prev" :click="nextSong(-1)"></span> <span class="item prev s-icon-prev" :click="nextSong(-1)"></span>
<span <span
class="item play" class="item play"
@ -93,26 +140,7 @@
<span class="item next s-icon-next" :click="nextSong(1)"></span> <span class="item next s-icon-next" :click="nextSong(1)"></span>
</div> </div>
</aside> <div class="stat-box">
<div class="main">
<div class="tool-bar do-fn-drag">
<div class="search do-fn-nodrag">
<input class="do-ui-input" value="">
<i class="icon do-icon-search"></i>
</div>
</div>
<content class="module" :include="views" data-cache="true"></content>
<div class="play-bar">
<div class="song-stat"> <div class="song-stat">
<canvas ref="player"></canvas> <canvas ref="player"></canvas>
</div> </div>

View File

@ -31,6 +31,21 @@ const PLAY_MODE = {
1: 'single', 1: 'single',
2: 'random' 2: 'random'
} }
const COLORS = [
{
title: '#62778d',
lrc: '#98acae',
bar1: '#dae1e9',
bar2: '#3fc2a7'
},
{
title: '#fff',
lrc: '#d7d8db',
bar1: '#454545',
bar2: '#fff'
}
]
const FONTS_NAME = const FONTS_NAME =
' Helvetica, Arial,"WenQuanYi Micro Hei","PingFang SC","Hiragino Sans GB","Segoe UI", "Microsoft Yahei", sans-serif' ' Helvetica, Arial,"WenQuanYi Micro Hei","PingFang SC","Hiragino Sans GB","Segoe UI", "Microsoft Yahei", sans-serif'
@ -53,6 +68,7 @@ Anot({
winFocus: false, winFocus: false,
mod: 'local', mod: 'local',
playMode: Anot.ls('play-mode') >>> 0, // 0:all | 1:single | 2:random playMode: Anot.ls('play-mode') >>> 0, // 0:all | 1:single | 2:random
ktvMode: 0,
isPlaying: false, isPlaying: false,
optBoxShow: false, optBoxShow: false,
volumeCtrlShow: false, volumeCtrlShow: false,
@ -73,6 +89,13 @@ Anot({
return return
} }
return '/views/' + this.mod + '.htm' return '/views/' + this.mod + '.htm'
},
coverBG() {
if (this.curr.cover) {
return `url(${this.curr.cover})`
} else {
return 'none'
}
} }
}, },
watch: { watch: {
@ -103,6 +126,10 @@ Anot({
let ay = ev.pageY - rect.top let ay = ev.pageY - rect.top
log(aw, ax, ay) log(aw, ax, ay)
if (ax < 80 && this.curr.id) {
this.ktvMode = this.ktvMode ^ 1
return
}
if (ax > 124 && ay > 55 && ay < 64) { if (ax > 124 && ay > 55 && ay < 64) {
let pp = (ax - 124) / (aw - 124) let pp = (ax - 124) / (aw - 124)
this.curr.time = pp * this.curr.duration this.curr.time = pp * this.curr.duration
@ -217,6 +244,7 @@ Anot({
let ry = this.__HEIGHT__ / 2 // 旋转唱片的圆心坐标Y let ry = this.__HEIGHT__ / 2 // 旋转唱片的圆心坐标Y
let pw = this.__WIDTH__ - this.__HEIGHT__ - 180 // 进度条总长度 let pw = this.__WIDTH__ - this.__HEIGHT__ - 180 // 进度条总长度
let wl = this.__HEIGHT__ + 180 // 文字的坐标X let wl = this.__HEIGHT__ + 180 // 文字的坐标X
const draw = () => { const draw = () => {
let { time, duration } = this.curr let { time, duration } = this.curr
let pp = time / duration // 进度百分比 let pp = time / duration // 进度百分比
@ -244,12 +272,12 @@ Anot({
this.__CTX__.drawImage(img2, 0, 0, this.__HEIGHT__, this.__HEIGHT__) this.__CTX__.drawImage(img2, 0, 0, this.__HEIGHT__, this.__HEIGHT__)
// 歌曲标题和歌手 // 歌曲标题和歌手
this.__CTX__.fillStyle = '#62778d' this.__CTX__.fillStyle = COLORS[this.ktvMode].title
this.__CTX__.font = '56px' + FONTS_NAME this.__CTX__.font = '56px' + FONTS_NAME
this.__CTX__.fillText(`${title} - ${artist}`, wl, 100) this.__CTX__.fillText(`${title} - ${artist}`, wl, 100)
// 时间 // 时间
this.__CTX__.fillStyle = '#98acae' this.__CTX__.fillStyle = COLORS[this.ktvMode].lrc
this.__CTX__.font = '48px' + FONTS_NAME this.__CTX__.font = '48px' + FONTS_NAME
this.__CTX__.fillText( this.__CTX__.fillText(
`${time} / ${duration}`, `${time} / ${duration}`,
@ -258,14 +286,14 @@ Anot({
) )
// 歌词 // 歌词
this.__CTX__.fillStyle = '#98acae' this.__CTX__.fillStyle = COLORS[this.ktvMode].lrc
this.__CTX__.font = '48px' + FONTS_NAME this.__CTX__.font = '48px' + FONTS_NAME
this.__CTX__.fillText(`暂无歌词...`, wl, 180) this.__CTX__.fillText(`暂无歌词...`, wl, 180)
// 进度条 // 进度条
this.__CTX__.fillStyle = '#dae1e9' this.__CTX__.fillStyle = COLORS[this.ktvMode].bar1
this.__CTX__.fillRect(wl, 230, pw, 16) this.__CTX__.fillRect(wl, 230, pw, 16)
this.__CTX__.fillStyle = '#3fc2a7' this.__CTX__.fillStyle = COLORS[this.ktvMode].bar2
this.__CTX__.fillRect(wl, 230, pw * pp, 16) this.__CTX__.fillRect(wl, 230, pw * pp, 16)
this.__DEG__ += 0.01 this.__DEG__ += 0.01

View File

@ -23,7 +23,7 @@ function createWindow() {
win = new BrowserWindow({ win = new BrowserWindow({
title: 'sonist', title: 'sonist',
width: 1024, width: 1024,
height: 600, height: 640,
frame: false, frame: false,
resizable: false, resizable: false,
webPreferences: { webPreferences: {

View File

@ -1,6 +1,6 @@
{ {
"name": "sonist", "name": "sonist",
"version": "1.0.0", "version": "0.0.9",
"description": "Music Player", "description": "Music Player",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {