init glass player

This commit is contained in:
dengtongyu committed 2018-05-08 16:49:20 +08:00
commit adcc43cf8f
50 files changed
+13488

No files matched your search

+147
View File
@@ -0,0 +1,147 @@
@import '~antd/dist/antd.css';
@font-face {font-family: "iconfont";
src: url('./assets/iconfont.eot?t=1524288064827'); /* IE9*/
src: url('./assets/iconfont.eot?t=1524288064827#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'),
url('./assets/iconfont.ttf?t=1524288064827') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('./assets/iconfont.svg?t=1524288064827#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family:"iconfont" !important;
font-size:16px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-add:before { content: "\e623"; }
.icon-remove:before { content: "\e625"; }
.icon-search:before { content: "\e627"; }
.icon-play:before { content: "\e62e"; }
.icon-pause:before { content: "\e62f"; }
.icon-next:before { content: "\e631"; }
.icon-previous:before { content: "\e632"; }
.icon-repeat:before { content: "\e635"; }
.icon-volume:before { content: "\e636"; }
.icon-mute:before { content: "\e638"; }
.icon-fullscreen:before { content: "\e692"; }
.icon-list:before { content: "\e69b"; }
.icon-trash:before { content: "\e6e4"; }
ul,ol,li {
list-style-type: none;
margin: 0;
padding: 0;
}
p {
margin: 0;
padding: 0;
}
html,body {
height: 100%;
min-height: 682px;
}
.flex {
display: flex;
flex-direction:column;
}
.App {
width: 100%;
height: 100%;
min-width: 900px;
text-align: center;
position: relative;
background-color: #888;
}
.App a{
color: #fff;
}
.App button{
color: #fff;
border: none;
background-color: transparent;
outline: none;
cursor: pointer;
padding: 0;
margin: 0;
}
.bg-image-mask {
width: 100%;
height: 100%;
background-color: #000;
opacity: .3;
position:absolute;
top: 0;
left: 0;
}
.bg-image {
width: 100%;
height: 100%;
background-image: url(./assets/bg.png);
background-repeat: no-repeat;
background-size: cover;
background-position: 50%;
transform: translateZ(0);
opacity: .6;
position:absolute;
top: 0;
left: 0;
}
.content {
width: 90%;
height: 100%;
min-height: 630px;
margin: 0 5%;
}
.blur {
-webkit-filter: blur(65px);
-moz-filter: blur(65px);
-ms-filter: blur(65px);
filter: blur(65px);
}
.main {
width: 100%;
flex: 1;
position: relative;
overflow: hidden;
}
.song-list-box {
margin-left: 310px;
margin-right: 310px;
color: #fff;
height: 100%;
padding-top: 20px;
box-sizing: border-box;
}
.full {
margin-right: 0;
}
+58
View File
@@ -0,0 +1,58 @@
import React, { Component } from 'react';
import { message } from 'antd';
import { observer } from 'mobx-react';
import CoverLyrics from './components/CoverLyrics';
import SongList from './components/SongList';
import ControlBar from './components/ControlBar';
import SearchBar from './components/SearchBar';
import './App.css';
import { DataStore } from './DataStore';
import PlayList from './components/PlayList';
@observer
class App extends Component {
onWindowResize = (event) => {
DataStore.windowWidth = window.innerWidth;
DataStore.windowHeight = window.innerHeight;
}
componentDidMount() {
window.addEventListener('resize', this.onWindowResize);
this.onWindowResize();
}
componentDidUpdate() {
if(DataStore.errMassge) {
message.warning(DataStore.errMassge);
DataStore.errMassge = '';
}
}
componentWillUnmount() {
window.removeEventListener('resize', this.onWindowResize)
}
render() {
return (
<div className="App">
<div className="bg-image-mask"></div>
<div className="bg-image blur"
style={{backgroundImage:'url('+ DataStore.coutSongMassge.Img +')'}}></div>
<div className="content flex">
<div className="main">
<CoverLyrics />
<div className={"song-list-box flex" + (DataStore.showPlayList ? '' : ' full')}>
<SearchBar />
<SongList />
</div>
<PlayList />
</div>
<ControlBar />
</div>
</div>
);
}
}
export default App;
+9
View File
@@ -0,0 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
+455
View File
@@ -0,0 +1,455 @@
import { observable } from 'mobx'
let isFetching = false;
async function urlFetch(url) {
if(isFetching) return Promise.reject('您点太快了!');
isFetching = true;
let result = await fetch(url)
isFetching = false;
return result;
}
const searchUrl = '/data/songSearch';
async function getSearchSongList(keyWord,page,size) {
if(!keyWord) keyWord = '';
if(!page) page = 1;
if(!size) size = 10;
let url = searchUrl + '?keyword=' + keyWord + '&page=' + page + '&pagesize=' + size;
let result = await urlFetch(url);
return result;
}
const newSongUrl = '/data/newSong';
async function getNewSongList() {
let url = newSongUrl + '?json=true';
let result = await urlFetch(url);
return result;
}
const songMassgeUrl = '/data/songMsg';
async function getSongMassge(hash) {
let url = songMassgeUrl + '?hash=' + hash;
let result = await urlFetch(url)
return result;
}
function parseLyric(text) {
//将文本分隔成一行一行,存入数组
let lines = text.split('\n'),
//用于匹配时间的正则表达式,匹配的结果类似[xx:xx.xx]
pattern = /\[\d{2}:\d{2}.\d{2}\]/g,
//保存最终结果的数组
result = [];
//去掉不含时间的行
while (!pattern.test(lines[0])) {
lines = lines.slice(1);
};
//上面用'\n'生成生成数组时,结果中最后一个为空元素,这里将去掉
lines[lines.length - 1].length === 0 && lines.pop();
lines.forEach(function(v /*数组元素值*/ , i /*元素索引*/ , a /*数组本身*/ ) {
//提取出时间[xx:xx.xx]
var time = v.match(pattern),
//提取歌词
value = v.replace(pattern, '');
//因为一行里面可能有多个时间,所以time有可能是[xx:xx.xx][xx:xx.xx][xx:xx.xx]的形式,需要进一步分隔
time.forEach(function(v1, i1, a1) {
//去掉时间里的中括号得到xx:xx.xx
var t = v1.slice(1, -1).split(':');
//将结果压入最终数组
result.push([parseInt(t[0], 10) * 60 + parseFloat(t[1]), value]);
});
});
//最后将结果数组中的元素按时间大小排序,以便保存之后正常显示歌词
result.sort(function(a, b) {
return a[0] - b[0];
});
return result;
}
class DataPageStore {
@observable _audio = null;
@observable _searchSongList = [];
@observable _playSongList = [];
@observable _count = 0;
@observable _isPlay = false;
@observable _volume = 0.6;
@observable _silent = false;
@observable _disabled = false;
@observable _showPlayList = false;
@observable _currentTime = 0;
@observable _loop = false;
@observable _errMassge = '';
@observable _keyword = '';
@observable _page = 0;
@observable _pageSize = 10;
@observable _listType = 0;
@observable _windowWidth = 900;
@observable _windowHeight = 680;
get audio() {
return this._audio;
}
set audio(value) {
this._audio = value;
}
get searchSongList() {
return this._searchSongList;
}
set searchSongList(value) {
this._searchSongList = value;
}
get playSongList() {
return this._playSongList;
}
set playSongList(value) {
this._playSongList = value;
if(this.playSongList.length === 1) {
this.loop = true;
}else{
this.loop = false;
}
}
get count() {
return this._count;
}
set count(value) {
if(this.playSongList.length === 0) {
this._count = 0;
return;
}
if(value < 0) {
this._count = this.playSongList.length - 1;
return;
}
if(value > this.playSongList.length - 1) {
this._count = 0;
return;
}
this._count = value;
}
get isPlay() {
return this._isPlay;
}
set isPlay(value) {
this._isPlay = value;
if(this._audio) {
if(this._isPlay) this._audio.play();
else this._audio.pause();
}
}
get volume() {
return this._volume;
}
set volume(value) {
this._volume = value;
if(this._audio) this._audio.volume = this._volume;
}
get silent() {
return this._silent;
}
set silent(value) {
this._silent = value;
if(this._silent) {
this.volume = 0;
}else{
this.volume = 0.6;
}
}
get disabled() {
return this._disabled;
}
set disabled(value) {
this._disabled = value;
}
get showPlayList() {
return this._showPlayList;
}
set showPlayList(value) {
this._showPlayList = value;
}
get currentTime() {
return this._currentTime;
}
set currentTime(value) {
this._currentTime = value;
//if(this._audio) this._audio.currentTime = this._currentTime;
}
get loop() {
return this._loop;
}
set loop(value) {
this._loop = value;
}
get errMassge() {
return this._errMassge;
}
set errMassge(value) {
this._errMassge = value;
}
get keyword() {
return this._keyword;
}
set keyword(value) {
this._keyword = value;
}
get page() {
return this._page;
}
set page(value) {
this._page = value;
}
get pageSize() {
return this._pageSize;
}
get listType() {
return this._listType;
}
set listType(value) {
this._listType = value;
}
get windowWidth() {
return this._windowWidth;
}
set windowWidth(value) {
if(value < 800) value = 800;
this._windowWidth = value;
}
get windowHeight() {
return this._windowHeight;
}
set windowHeight(value) {
if(value < 680) value = 680;
this._windowHeight = value;
}
get scrollHeight() {
return this.windowHeight - 200;
}
addPlaySongList(value) {
this.playSongList.push(value);
if(this.playSongList.length === 1) {
this.loop = true;
}else{
this.loop = false;
}
}
get coutSongMassge() {
if(this._playSongList.length > 0) {
return this._playSongList[this._count];
}else{
return {
SongName: '未知歌曲',
PlayUrl: '',
SingerName: '未知艺术家',
AlbumName: '未知专辑',
Img: 'https://y.gtimg.cn/music/photo_new/T002R300x300M00000366x8I0pYkOp.jpg?max_age=2592000',
Lyrics: [],
Duration: 0,
Hash: ''
};
}
}
searchSongListStore() {
getSearchSongList(this.keyword, this.page, this.pageSize)
.then(result => result.json())
.then(json => {
//console.log(json);
if(json.result) {
this.listType = 0;
let songs = [];
let list = json.data.lists;
for (let index = 0; index < list.length; index++) {
let songMsg = {};
songMsg.Hash = list[index].FileHash;
songMsg.SongName = list[index].SongName;
songMsg.SingerName = list[index].SingerName;
songMsg.Duration = list[index].Duration;
songs.push(songMsg);
}
this.searchSongList = json.data.page > 1 ? this.searchSongList.concat(songs) : songs;
}
}).catch((err) => {
this.errMassge = err;
});
}
newSongListStore() {
getNewSongList()
.then(result => result.json())
.then(json => {
//console.log(json);
if(json.result) {
this.listType = 1;
let songs = [];
let list = json.data;
for (let index = 0; index < list.length; index++) {
let songMsg = {};
songMsg.Hash = list[index].hash;
songMsg.SongName = list[index].filename.split(' - ')[1];
songMsg.SingerName = list[index].filename.split(' - ')[0];
songMsg.Duration = list[index].duration;
songs.push(songMsg);
}
this.searchSongList = songs;
}
}).catch((err) => {
this.errMassge = err;
});
}
addSongInList(hash,callback) {
let songIndex = this.findInPlayList(hash);
//console.log(songIndex,songData);
if(songIndex >= 0) {
let songData = this.playSongList[songIndex];
if(callback) callback(songData,songIndex);
}else{
getSongMassge(hash)
.then(result => result.json())
.then(json => {
//console.log(json);
if(json.result) {
let songMassge = {};
songMassge.PlayUrl = json.data.play_url;
songMassge.SongName = json.data.song_name;
songMassge.SingerName = json.data.author_name;
songMassge.Img = json.data.img;
songMassge.Lyrics = json.data.lyrics ? parseLyric(json.data.lyrics) : [];
songMassge.AlbumName = json.data.album_name;
songMassge.Duration = Math.floor(json.data.timelength / 1000);
songMassge.Hash = json.data.hash;
//if(this.findInPlayList(hash) > 0) return;
this.addPlaySongList(songMassge);
if(callback) callback(songMassge,this.playSongList.length - 1);
}
}).catch((err) => {
//console.log(err);
this.errMassge = err;
});
}
}
addSongAndPlay(hash) {
this.addSongInList(hash,(songMassge,index)=>{
this.count = index;
})
}
findInPlayList(hash) {
return this.playSongList.findIndex((item, index, arr) => {
return hash === item.Hash;
})
}
removeInSongList(hash) {
let curHash = this.coutSongMassge.Hash;
this.playSongList = this.playSongList.filter((item, index, arr) => {
return hash !== item.Hash;
});
this.count = this.findInPlayList(curHash);
}
clearSongList() {
this.playSongList = [];
this.audio = null;
this.count = 0;
DataStore.isPlay = false;
}
createAudio(url) {
console.log(this.audio)
if(this._audio && this._audio.parent) {
document.getElementById('audio-box').removeChild(this._audio);
}
if(this._audio && this._audio.src === url) {
return null;
}
if(url) {
this.audio = new Audio();
this.audio.style.display = 'none';
this.audio.src = url;
this.audio.volume = this.volume;
this.audio.autoplay = true;
document.getElementById('audio-box').appendChild(this.audio);
}
}
}
const DataStore = new DataPageStore();
// getSearchSongList('goog').then(function (result) {
// console.log(result);
// });
export { DataStore }
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.
+72
View File
@@ -0,0 +1,72 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
<glyph glyph-name="add" unicode="&#58915;" d="M780.007475-62.730461 243.903497-62.730461c-98.599645 0-178.61298 79.969333-178.61298 178.61298L65.290517 651.98752c0 98.730629 80.013335 178.744987 178.61298 178.744987l536.105001 0c98.772584 0 178.700985-80.013335 178.700985-178.744987l0-536.105001C958.70846 17.239895 878.781082-62.730461 780.007475-62.730461L780.007475-62.730461zM869.44546 607.312018c0 74.03825-60.119239 134.069485-134.113487 134.069485L288.578999 741.381503c-73.994248 0-134.027529-60.031235-134.027529-134.069485l0-446.665992c0-74.124208 60.033281-134.069485 134.027529-134.069485l446.752973 0c73.994248 0 134.113487 59.945277 134.113487 134.069485L869.44546 607.312018 869.44546 607.312018zM556.630988 160.646026l-89.351004 0L467.279984 339.259006 288.578999 339.259006l0 89.440032 178.700985 0L467.279984 607.312018 556.630988 607.312018l0-178.61298 178.700985 0 0-89.440032L556.630988 339.259006 556.630988 160.646026 556.630988 160.646026z" horiz-adv-x="1024" />
<glyph glyph-name="remove" unicode="&#58917;" d="M288.623002 428.688805l446.752973 0 0-89.419566L288.623002 339.269239 288.623002 428.688805zM958.70846 651.935332l0-535.9996c0-98.625228-80.000032-178.579211-178.75522-178.579211L243.954663-62.64348c-98.579179 0-178.665169 79.953983-178.665169 178.579211L65.289494 651.935332c0 98.711186 80.08599 178.709171 178.665169 178.709171l535.998577 0C878.708428 830.644503 958.70846 750.646518 958.70846 651.935332zM869.374852 607.268016c0 74.023924-60.021002 134.044926-133.998877 134.044926L288.623002 741.312942c-73.979922 0-133.912919-60.021002-133.912919-134.044926L154.710083 160.690028c0-74.109882 59.932997-134.042879 133.912919-134.042879l446.752973 0c73.977875 0 133.998877 59.932997 133.998877 134.042879L869.374852 607.268016z" horiz-adv-x="1024" />
<glyph glyph-name="search" unicode="&#58919;" d="M556.630988 652.11748c-123.294071 0-223.290529-100.038414-223.290529-223.418442 0-47.120181 14.745842-90.660836 39.527249-126.696564l-128.96421-96.680946 89.436962-89.307002 96.680946 128.876206c36.037774-24.867364 79.57843-39.569204 126.610606-39.569204 123.380028 0 223.376487 99.996458 223.376487 223.37751C780.007475 552.079066 680.011017 652.11748 556.630988 652.11748zM556.630988 294.627507c-73.994248 0-134.027529 60.077284-134.027529 134.071532 0 74.036203 60.033281 133.981481 134.027529 133.981481 74.080206 0 134.025483-59.945277 134.025483-133.981481C690.656471 354.70479 630.711194 294.627507 556.630988 294.627507zM780.007475 830.732507 243.903497 830.732507c-98.599645 0-178.61298-79.969333-178.61298-178.615027l0-536.102954c0-98.730629 80.013335-178.744987 178.61298-178.744987l536.105001 0c98.772584 0 178.700985 80.013335 178.700985 178.744987L958.709483 652.11748C958.70846 750.763175 878.781082 830.732507 780.007475 830.732507zM869.44546 160.646026c0-73.994248-60.119239-134.069485-134.113487-134.069485L288.578999 26.576541c-73.994248 0-134.027529 60.075237-134.027529 134.069485L154.55147 607.356021c0 74.080206 60.033281 134.069485 134.027529 134.069485l446.752973 0c73.994248 0 134.113487-59.989279 134.113487-134.069485L869.44546 160.646026z" horiz-adv-x="1024" />
<glyph glyph-name="play" unicode="&#58926;" d="M377.971959 562.680519 690.656471 384.067538 377.971959 205.321528 377.971959 562.680519zM958.70846 652.11748l0-536.102954c0-98.730629-79.971379-178.744987-178.615027-178.744987L243.903497-62.730461c-98.642624 0-178.61298 80.013335-178.61298 178.744987L65.290517 652.11748c0 98.644671 79.969333 178.615027 178.61298 178.615027l536.190959 0C878.73708 830.732507 958.70846 750.763175 958.70846 652.11748zM869.401458 607.356021c0 74.080206-59.989279 134.069485-134.069485 134.069485L288.667004 741.425506c-74.082252 0-134.115534-59.989279-134.115534-134.069485L154.55147 160.690028c0-74.03825 60.033281-134.113487 134.115534-134.113487l446.664969 0c74.080206 0 134.069485 60.075237 134.069485 134.113487L869.401458 607.356021z" horiz-adv-x="1024" />
<glyph glyph-name="pause" unicode="&#58927;" d="M333.340459 562.680519l133.939525 0 0-357.357967L333.340459 205.322551 333.340459 562.680519zM556.716946 205.321528l133.983527 0L690.700473 562.680519 556.716946 562.680519 556.716946 205.321528zM958.70846 652.11748l0-536.102954c0-98.730629-79.971379-178.744987-178.700985-178.744987L243.903497-62.730461c-98.642624 0-178.61298 80.013335-178.61298 178.744987L65.290517 652.11748c0 98.644671 79.969333 178.615027 178.61298 178.615027l536.105001 0C878.73708 830.732507 958.70846 750.763175 958.70846 652.11748zM869.44546 607.356021c0 74.080206-60.119239 134.069485-134.113487 134.069485L288.667004 741.425506c-74.082252 0-134.071532-59.989279-134.071532-134.069485l0-446.709995c0-73.994248 59.989279-134.069485 134.071532-134.069485l446.664969 0c73.994248 0 134.113487 60.075237 134.113487 134.069485L869.44546 607.356021z" horiz-adv-x="1024" />
<glyph glyph-name="next" unicode="&#58929;" d="M779.991102 830.665992 243.964896 830.665992c-98.585319 0-178.674379-79.958076-178.674379-178.587398L65.290517 116.053412c0-98.717326 80.090083-178.718381 178.674379-178.718381l536.025183 0c98.759281 0 178.717358 80.002079 178.717358 178.718381L958.707436 652.078595C958.70846 750.707916 878.74936 830.665992 779.991102 830.665992zM869.414761 160.699238c0-74.003458-60.067051-134.049019-134.050042-134.049019L288.634258 26.650219c-73.982992 0-133.919059 60.045561-133.919059 134.049019L154.715199 607.323275c0 74.091462 59.936067 134.049019 133.919059 134.049019l446.731484 0c73.982992 0 134.050042-59.957557 134.050042-134.049019L869.415784 160.699238zM646.07102 562.652889l89.293699 0L735.364719 205.347111l-89.293699 0L646.07102 361.687832 333.389578 205.347111 333.389578 562.652889l312.682465-156.318208L646.072043 562.652889z" horiz-adv-x="1024" />
<glyph glyph-name="previous" unicode="&#58930;" d="M779.95324 830.644503 243.998665 830.644503c-98.623181 0-178.709171-79.953983-178.709171-178.579211l0-535.9996c0-98.711186 80.08599-178.709171 178.709171-178.709171l535.954575 0c98.755188 0 178.75522 79.997985 178.75522 178.709171L958.70846 652.065292C958.70846 750.69052 878.708428 830.644503 779.95324 830.644503zM869.286848 160.690028c0-73.979922-59.932997-134.042879-133.954875-134.042879L288.623002 26.647149c-73.979922 0-133.912919 60.062957-133.912919 134.042879L154.710083 607.312018c0 74.067926 59.932997 134.042879 133.912919 134.042879l446.708971 0c74.021877 0 133.954875-59.974953 133.954875-134.042879L869.286848 160.690028zM422.709883 406.312168l312.62209 156.333558 0-357.290429L422.709883 361.689878l0-156.333558-89.421612 0L333.288271 562.64675l89.421612 0L422.709883 406.312168z" horiz-adv-x="1024" />
<glyph glyph-name="repeat" unicode="&#58933;" d="M779.973706 830.709995 243.9168 830.709995c-98.639554 0-178.627307-79.987752-178.627307-178.627307l0-536.077372c0-98.727559 79.987752-178.715311 178.627307-178.715311l536.056905 0c98.747002 0 178.734754 79.987752 178.734754 178.715311L958.70846 652.082688C958.70846 750.722242 878.720707 830.709995 779.973706 830.709995zM869.385085 160.657282c0-73.990155-60.051701-134.063345-134.063345-134.063345L288.568766 26.593937c-74.011644 0-133.975341 60.07319-133.975341 134.063345l0 446.775486c0 73.990155 59.963697 133.975341 133.975341 133.975341l446.752973 0c74.011644 0 134.063345-59.985186 134.063345-133.975341L869.385085 160.657282zM377.980145 205.328691l401.99356 0-44.651966-89.323375L288.568766 116.005316l0 89.323375 0 89.303932-89.325421 0 134.084834 178.737824 133.954875-178.737824-89.303932 0L377.979122 205.328691zM735.32174 473.369424l89.411379 0-134.063345-178.737824-134.063345 178.737824 89.411379 0 0 89.301885L243.9168 562.671309l44.651966 89.411379 446.752973 0 0-89.411379L735.32174 473.369424z" horiz-adv-x="1024" />
<glyph glyph-name="volume" unicode="&#58934;" d="M646.042367 473.357144l44.665269 0 0-178.711218-44.665269 0L646.042367 473.357144zM556.620755 339.399199l44.753273 0 0 89.289606L556.620755 428.688805 556.620755 339.399199zM243.954663 473.357144l0-178.711218 89.421612 0L511.955486 160.690028 511.955486 607.400023 333.376275 473.357144 243.954663 473.357144zM735.375975 249.979634l44.578288 0L779.954263 517.978411l-44.578288 0L735.375975 249.979634zM958.70846 652.065292l0-535.9996c0-98.755188-80.000032-178.709171-178.75522-178.709171L243.954663-62.64348c-98.579179 0-178.665169 79.953983-178.665169 178.709171L65.289494 652.065292c0 98.581226 80.08599 178.579211 178.665169 178.579211l535.998577 0C878.708428 830.644503 958.70846 750.646518 958.70846 652.065292zM869.374852 607.400023c0 73.979922-60.021002 133.954875-133.998877 133.954875L288.623002 741.354897c-73.979922 0-133.912919-59.974953-133.912919-133.954875L154.710083 160.690028c0-73.979922 59.932997-134.042879 133.912919-134.042879l446.752973 0c73.977875 0 133.998877 60.062957 133.998877 134.042879L869.374852 607.400023z" horiz-adv-x="1024" />
<glyph glyph-name="mute" unicode="&#58936;" d="M779.780301 435.92972l-74.283844-74.283844 74.283844-74.283844-37.162388-37.164435-74.28589 74.283844-74.283844-74.283844-37.208437 37.164435 74.371848 74.283844-74.371848 74.283844 37.208437 37.208437 74.283844-74.28589 74.28589 74.28589L779.780301 435.92972zM243.998665 473.357144l0-178.711218 89.289606 0L511.999488 160.690028 511.999488 607.400023l-178.711218-134.042879L243.998665 473.357144zM958.70846 652.023336 958.70846 116.022713c0-98.711186-80.000032-178.665169-178.711218-178.665169L243.998665-62.642456c-98.623181 0-178.709171 79.953983-178.709171 178.665169L65.289494 652.023336c0 98.623181 80.08599 178.621167 178.709171 178.621167l535.998577 0C878.708428 830.644503 958.70846 750.646518 958.70846 652.023336zM869.286848 607.400023c0 73.979922-59.977 133.954875-133.954875 133.954875L288.664957 741.354897c-74.021877 0-133.954875-59.974953-133.954875-133.954875L154.710083 160.690028c0-73.979922 59.932997-134.042879 133.954875-134.042879l446.667016 0c73.977875 0 133.954875 60.062957 133.954875 134.042879L869.286848 607.400023z" horiz-adv-x="1024" />
<glyph glyph-name="fullscreen" unicode="&#59026;" d="M288.667004 160.558022l178.61298 0 0-44.675502L243.903497 115.88252l0 44.675502L243.903497 339.281519l44.763506 0L288.667004 160.558022zM377.974006 249.973494l268.049942 0L646.023948 518.003993 377.974006 518.003993 377.974006 249.973494zM958.70846 651.98752l0-536.105001c0-98.621135-79.969333-178.615027-178.700985-178.615027L243.903497-62.732507c-98.642624 0-178.61298 79.993892-178.61298 178.615027L65.290517 651.98752c0 98.752118 79.969333 178.744987 178.61298 178.744987l536.105001 0C878.739127 830.732507 958.70846 750.739639 958.70846 651.98752zM869.44546 607.333508c0 74.014714-60.119239 134.069485-134.113487 134.069485L288.667004 741.402993c-74.082252 0-134.069485-60.054771-134.069485-134.069485l0-446.775486c0-73.994248 59.987233-133.983527 134.069485-133.983527l446.664969 0c73.994248 0 134.113487 59.989279 134.113487 133.983527L869.44546 607.333508zM288.667004 607.333508l178.61298 0 0 44.654013L288.667004 651.98752l-44.763506 0 0-223.290529 44.763506 0L288.667004 607.333508zM556.718993 607.333508l178.61298 0 0-178.636516 44.675502 0L780.007475 607.333508l0 44.654013L556.718993 651.98752 556.718993 607.333508zM735.331973 160.558022 556.718993 160.558022l0-44.675502 178.61298 0 44.675502 0L780.007475 339.281519l-44.675502 0L735.331973 160.558022z" horiz-adv-x="1024" />
<glyph glyph-name="list" unicode="&#59035;" d="M243.998665 339.289706l89.289606 0 0-89.289606-89.289606 0L243.998665 339.289706zM243.998665 518.000923l89.289606 0 0 89.289606-89.289606 0L243.998665 518.000923zM243.998665 383.954975l89.289606 0 0 89.399099-89.289606 0L243.998665 383.954975zM243.998665 115.956198l89.289606 0 0 89.397053-89.289606 0L243.998665 115.956198zM958.70846 651.955798l0-535.9996c0-98.625228-79.997985-178.600701-178.753173-178.600701l-535.956621 0c-98.623181 0-178.709171 79.975473-178.709171 178.600701L65.289494 651.955798c0 98.711186 80.08599 178.688705 178.709171 178.688705l535.956621 0C878.710474 830.644503 958.70846 750.666984 958.70846 651.955798zM869.286848 607.289506c0 73.999364-59.932997 134.065392-133.954875 134.065392L288.623002 741.354897c-73.979922 0-133.912919-60.065004-133.912919-134.065392L154.710083 160.710494c0-74.087369 59.932997-134.042879 133.912919-134.042879l446.708971 0c74.021877 0 133.954875 59.95551 133.954875 134.042879L869.286848 607.289506zM422.709883 115.956198l357.245404 0 0 89.397053L422.709883 205.35325 422.709883 115.956198zM422.709883 518.000923l357.245404 0 0 89.289606L422.709883 607.290529 422.709883 518.000923zM422.709883 383.954975l357.245404 0 0 89.399099L422.709883 473.354074 422.709883 383.954975zM422.709883 250.0001l357.245404 0 0 89.289606L422.709883 339.289706 422.709883 250.0001z" horiz-adv-x="1024" />
<glyph glyph-name="trash" unicode="&#59108;" d="M556.630988 518.37443c0-0.088004 0.085958-0.217964 0.085958-0.391926-9.247618 0.085958-16.228613 0.085958-22.074761 0.085958C546.508443 518.286426 551.918663 518.286426 556.630988 518.37443zM646.024971 517.982504l133.983527 0 0-89.307002-44.675502 0-44.675502-312.728514-44.6315 0-44.6315 0L422.647461 115.946988 377.971959 115.946988l-44.6315 0-44.673455 312.728514-44.763506 0 0 89.307002L377.971959 517.982504c136.164193-1.264806 69.674872-0.176009 121.94126 0l-32.634258 0c55.843866 1.874697 20.068058 0 44.675502 0 0.871857 0 1.570775 0 2.356674 0l3.40147 0c5.41022 0.085958-21.289885 0.391926 16.928554 0.085958-2.574638 0-5.322216-0.085958-8.55177-0.085958l30.627555 0L646.024971 517.982504zM467.279984 428.675502 377.971959 428.675502l31.892361-223.421512 57.414641 0L467.278961 428.675502zM646.024971 428.675502l-89.307002 0 0-223.421512 57.414641 0L646.024971 428.675502zM958.70846 652.051989l0-536.105001c0-98.730629-79.971379-178.744987-178.700985-178.744987L243.903497-62.797999c-98.642624 0-178.61298 80.013335-178.61298 178.744987L65.290517 652.051989c0 98.730629 79.969333 178.744987 178.61298 178.744987l536.105001 0C878.73708 830.796976 958.70846 750.782617 958.70846 652.051989zM869.44546 607.375463c0 74.03825-60.119239 133.983527-134.113487 133.983527L288.667004 741.358991c-74.082252 0-134.071532-59.945277-134.071532-133.983527l0-446.753997c0-74.03825 59.989279-133.983527 134.071532-133.983527l446.664969 0c73.994248 0 134.113487 59.945277 134.113487 133.983527L869.44546 607.375463zM511.955486 562.743964c24.51944 0 44.455491-19.850094 44.675502-44.369533 10.556427 0.305969 17.97335 0.479931 89.39296-0.391926 0 74.080206-59.989279 134.069485-134.069485 134.069485-73.994248 0-133.983527-59.989279-133.983527-134.069485l89.307002 0C467.279984 542.719908 487.348042 562.743964 511.955486 562.743964z" horiz-adv-x="1024" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.
Binary file not shown.
+49
View File
@@ -0,0 +1,49 @@
.control-bal {
height: 70px;
width: 100%;
min-width: 688px;
/* position: absolute;
bottom: 0; */
color: #dddede;
}
.control-bal a {
color: #dddede;
}
.control-bal>.cb-bg {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: #000;
opacity: .3;
filter: alpha(opacity=30);
box-shadow: 0 -10px 20px #403f3f;
}
.control-bal>.cb-main {
height: 100%;
position: relative;
}
.control-bal .ant-slider .ant-slider-rail {
background-color: #fff;
opacity: .3;
}
.control-bal .ant-slider .ant-slider-track {
background-color: #fff;
opacity: .6;
}
.control-bal .ant-slider:hover .ant-slider-track {
background-color: #fff;
opacity: .8;
}
/* .control-bal .ant-slider .ant-slider-handle {
border-color: rgba(255, 255, 255, .6);
} */
+63
View File
@@ -0,0 +1,63 @@
import React, { Component } from 'react';
import PlayerButton from './PlayerButton';
import Progress from './Progress';
import MediaCtrList from './MediaCtrList';
import { observer } from 'mobx-react';
import './ControlBar.css';
import './Progress.css';
import './MediaCtrList.css';
import { DataStore } from '../DataStore';
@observer
class ControlBar extends Component {
timeUpdateHandler = (event) => {
DataStore.currentTime = event.target.currentTime;
}
canPlayHandler = (event) => {
DataStore.audio = event.target;
DataStore.volume = DataStore.volume;
}
endedHandler = (event) => {
DataStore.count = DataStore.count + 1;
}
setAudio = (url) => {
if(url) {
return (<audio
src={url}
style={{display:'none'}}
autoPlay={true}
loop={DataStore.loop}
ref='audio'
volume={DataStore.silent?0:DataStore.volume}
onTimeUpdate={this.timeUpdateHandler}
onCanPlay={this.canPlayHandler}
onEnded={this.endedHandler}
/>);
}else{
return null;
}
}
render() {
return (
<div className="control-bal">
<div id="audio-box"
style={{display:'none'}}>
{this.setAudio(DataStore.coutSongMassge.PlayUrl)}
</div>
<div className="cb-main">
<PlayerButton />
<Progress />
<MediaCtrList />
</div>
</div>
);
}
}
export default ControlBar;
+58
View File
@@ -0,0 +1,58 @@
.cover-lyrics {
width: 300px;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
.cover-box {
width: 100%;
/* height: 270px; */
margin: 20px 0 30px;
}
.cover-img {
width: 180px;
height: 180px;
margin: 0 auto 15px;
}
.cover-img>img {
width: 100%;
height: 100%;
display: block;
}
.cover-song-msg {
color: #fff;
opacity: .6;
text-align: center;
}
.cover-song-msg>p {
margin: 0;
height: 28px;
}
.lyrics-box {
width: 100%;
height: 250px;
overflow: hidden;
}
.lyrics-box>ul {
transition: all .3s;
}
.lyrics-box li {
line-height: 50px;
opacity: .6;
color: #fff;
}
.lyrics-box .cur {
opacity: 1;
font-size: 16px;
}
+70
View File
@@ -0,0 +1,70 @@
import React, { Component } from 'react';
//import FreeScrollBar from 'react-free-scrollbar';
import { observer } from 'mobx-react';
import './CoverLyrics.css';
import { DataStore } from '../DataStore';
@observer
class CoverLyrics extends Component {
lightPosition = () => {
let indexArr = [];
for (let i = 0; i < DataStore.coutSongMassge.Lyrics.length; i++) {
if (DataStore.currentTime > DataStore.coutSongMassge.Lyrics[i][0]) {
indexArr.push(i);
};
};
return indexArr[indexArr.length - 1];
}
listItems = () => {
if(DataStore.coutSongMassge.Lyrics.length > 0) {
return DataStore.coutSongMassge.Lyrics.map((item,index) =>
<li key ={index} className={this.lightPosition() === index ? 'cur' : ''}>{item[1]}</li>
);
}else{
return null;
}
}
marginSize = () => {
let row = 5;
let mid = Math.floor(row/2);
let step = 0;
if(this.lightPosition() - mid < 0) {
step = 0;
}else if(DataStore.coutSongMassge.Lyrics.length - this.lightPosition() < row) {
step = DataStore.coutSongMassge.Lyrics.length - row;
}else{
step = this.lightPosition() - mid
}
return -50 * step + 'px';
}
render() {
return (
<div className="cover-lyrics">
<div className="cover-box">
<div className="cover-img">
<img src={DataStore.coutSongMassge.Img}
alt={DataStore.coutSongMassge.SongName + '-' +DataStore.coutSongMassge.SingerName}/>
</div>
<div className="cover-song-msg">
<p>歌曲名:{DataStore.coutSongMassge.SongName}</p>
<p>歌手名:{DataStore.coutSongMassge.SingerName}</p>
<p>专辑名:{DataStore.coutSongMassge.AlbumName}</p>
</div>
</div>
<div className="lyrics-box">
<ul style={{marginTop:this.marginSize()}}>
{this.listItems()}
</ul>
</div>
</div>
);
}
}
export default CoverLyrics;
+33
View File
@@ -0,0 +1,33 @@
.media-ctr-list-box {
width: 182px;
height: 36px;
position: absolute;
right: 0;
top: 12px;
}
.icon-ctr {
width: 36px;
height: 36px;
float: left;
}
.icon-item {
width: 36px;
height: 36px;
line-height: 36px;
}
.icon-item>button {
width: 100%;
height: 100%;
font-size: 36px;
opacity: .8;
}
.icon-item>button:hover{
opacity: 1;
}
.volume-ctr {
margin-left: 20px;
float: left;
}
+32
View File
@@ -0,0 +1,32 @@
import React, { Component } from 'react';
import Volume from './Volume';
import { observer } from 'mobx-react';
import './MediaCtrList.css';
import { DataStore } from '../DataStore';
@observer
class MediaCtrList extends Component {
onClickListIcon = (event) => {
DataStore.showPlayList = !DataStore.showPlayList;
}
render() {
return (
<div className="media-ctr-list-box">
<div className="icon-ctr">
<div className="icon-item">
<button className={"iconfont icon-" + (DataStore.showPlayList ? 'fullscreen' : 'list')}
onClick={this.onClickListIcon}></button>
</div>
</div>
<div className="volume-ctr">
<Volume />
</div>
</div>
);
}
}
export default MediaCtrList;
+87
View File
@@ -0,0 +1,87 @@
.play-list-box {
width: 300px;
min-height: 612px;
color: #fff;
position: absolute;
right: 0;
top: 0;
/* opacity: 1;
transition: all .6s; */
}
.play-list-box-hide {
right: -100%;
opacity: 0;
}
.play-list-box>h1 {
color: #fff;
opacity: .8;
font-size: 18px;
height: 36px;
line-height: 36px;
margin-top: 20px;
margin-bottom: 10px;
}
.play-list-header {
overflow: hidden;
border-bottom:rgba(255,255,255,.1) solid 1px;
}
.play-list-body {
margin-top: 10px;
}
.play-list-row {
display: flex;
justify-content: flex-start;
text-align: left;
margin-bottom: 10px;
}
.play-list-row>li {
height: 30px;
line-height: 30px;
}
.play-list-row>.sort {
width: 30px;
text-align: center;
opacity: .6;
}
.play-list-row>.song-name {
flex:1;
opacity: .6;
}
.play-list-row>.singer-name {
width: 80px;
padding-left: 20px;
box-sizing: border-box;
opacity: .6;
}
.play-list-row>.operate {
width: 80px;
padding-right: 10px;
box-sizing: border-box;
display: flex;
justify-content: space-around;
}
.play-list-row>.operate>button {
font-size: 30px;
width: 30px;
height: 30px;
opacity: .6;
}
.play-list-row>.operate>button:hover {
opacity: .8;
}
ul.cur>.sort,ul.cur>.song-name,ul.cur>.singer-name {
opacity: .8;
}
+82
View File
@@ -0,0 +1,82 @@
import React, { Component } from 'react';
import { observer } from 'mobx-react';
import ScrollArea from 'react-scrollbar';
import './PlayList.css';
import { DataStore } from '../DataStore';
@observer
class PlayList extends Component {
onClickRemoveIcon = (event) => {
let hash = event.target.getAttribute('hash');
DataStore.removeInSongList(hash);
}
onClickPlayIcon = (event) => {
let hash = event.target.getAttribute('hash');
let index = DataStore.findInPlayList(hash);
DataStore.count = index;
DataStore.isPlay = true;
}
onClearSongList = (event) => {
DataStore.clearSongList();
}
ListItems = (list) => {
//console.log(list);
if(list.length > 0) {
let listItems = [];
for (let i = 0; i < list.length; i++) {
listItems.push(
<ul className={"play-list-row" + (i === DataStore.count ? ' cur' : '')}
key={list[i].Hash}>
<li className="sort">{i + 1}</li>
<li className="song-name ell">{list[i].SongName}</li>
<li className="singer-name ell">{list[i].SingerName}</li>
<li className="operate">
<button className="iconfont icon-remove"
hash={list[i].Hash}
onClick={this.onClickRemoveIcon}></button>
<button className="iconfont icon-play"
hash={list[i].Hash}
onClick={this.onClickPlayIcon}></button>
</li>
</ul>
)
}
return (<div className="play-list-body"
style={{height:DataStore.scrollHeight + 'px'}}>
<ScrollArea
horizontal={false}>
{listItems}
</ScrollArea>
</div>);
}else{
return (<div className="play-list-body">暂无数据</div>);
}
}
render() {
return (
<div className={"play-list-box" + (DataStore.showPlayList ? '' : ' hide')}>
<h1>我的歌曲播放列表</h1>
<div className="play-list-header">
<ul className="play-list-row">
<li className="sort"></li>
<li className="song-name">歌曲</li>
<li className="singer-name">歌手</li>
<li className="operate">
<button className="iconfont icon-trash"
onClick={this.onClearSongList}></button>
</li>
</ul>
</div>
{this.ListItems(DataStore.playSongList.slice())}
</div>
);
}
}
export default PlayList;
+32
View File
@@ -0,0 +1,32 @@
.player-button {
width: 140px;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
.player-button>button{
position: absolute;
font-size: 36px;
line-height: 36px;
opacity: .8;
top: 12px;
}
.player-button>button:hover{
opacity: 1;
}
.player-button>.left-btn {
left: 0;
}
.player-button>.mid-btn {
left:52px;
}
.player-button>.right-btn {
left: 104px;
}
+35
View File
@@ -0,0 +1,35 @@
import React, { Component } from 'react';
import { observer } from 'mobx-react';
import './PlayerButton.css';
import { DataStore } from '../DataStore';
@observer
class PlayerButton extends Component {
clickPlayButton = (event) => {
DataStore.isPlay = !DataStore.isPlay;
}
clickPrevButton = (event) => {
DataStore.count = DataStore.count - 1;
}
clickNextButton = (event) => {
DataStore.count = DataStore.count + 1;
}
render() {
return (
<div className="player-button">
<button className="iconfont left-btn icon-previous"
onClick={this.clickPrevButton}></button>
<button className={"iconfont mid-btn " + (DataStore.isPlay ? "icon-play" : "icon-pause")}
onClick={this.clickPlayButton}></button>
<button className="iconfont right-btn icon-next"
onClick={this.clickNextButton}></button>
</div>
);
}
}
export default PlayerButton;
+32
View File
@@ -0,0 +1,32 @@
.progress-box {
margin: 0 208px 0 166px;
}
.song-msg {
width: 100%;
height: 22px;
line-height: 22px;
/* color: #dddede; */
color: #fff;
position: relative;
opacity: .7;
}
.song-title {
margin-right: 100px;
text-align: left;
}
.song-time {
width: 100px;
text-align: right;
position: absolute;
right: 0;
top: 0;
}
.progress-box>.ant-slider {
margin-left: 0;
margin-right: 0;
}
+59
View File
@@ -0,0 +1,59 @@
import React, { Component } from 'react';
import { Slider } from 'antd';
import { observer } from 'mobx-react';
import './Progress.css';
import { DataStore } from '../DataStore';
@observer
class Progress extends Component {
timeFormat = (time) => {
let surplusMinite,
surplusSecond,
cTime;
//将剩余秒数转化为分钟
surplusMinite = Math.floor((time / 60) % 60);
//将剩余秒数转化为秒钟
surplusSecond = Math.floor(time % 60);
if(surplusMinite < 10){
surplusMinite = '0' + surplusMinite;
}
if(surplusSecond < 10){
surplusSecond = '0' + surplusSecond;
}
cTime = surplusMinite + ':' + surplusSecond;
return cTime;
}
sliderValue = (time) => {
if(time <= 0) return 0;
return Math.floor(time / DataStore.coutSongMassge.Duration * 100);
}
onChange = (value) => {
if(DataStore.audio) DataStore.audio.currentTime = Math.floor(value / 100 * DataStore.coutSongMassge.Duration);
//DataStore.currentTime = Math.floor(value / 100 * DataStore.coutSongMassge.Duration);
}
render() {
return (
<div className="progress-box">
<div className="song-msg">
<div className="song-title ell">{DataStore.coutSongMassge.SongName}-{DataStore.coutSongMassge.SingerName}</div>
<div className="song-time">{this.timeFormat(DataStore.currentTime)} / {this.timeFormat(DataStore.coutSongMassge.Duration)}</div>
</div>
<Slider
defaultValue={0}
value={this.sliderValue(DataStore.currentTime)}
disabled={DataStore.disabled}
tipFormatter={null}
onChange={this.onChange}
/>
</div>
);
}
}
export default Progress;
+43
View File
@@ -0,0 +1,43 @@
.search-box {
position: relative;
height: 36px;
}
.search-input {
margin-right: 46px;
height: 100%;
overflow: hidden;
}
.search-input>input {
width: 100%;
height: 30px;
line-height: 20px;
display: block;
border-radius: 6px;
padding: 5px 10px;
margin: 3px 0;
box-sizing: border-box;
background-color: rgba(255, 255, 255, .6);
border: none;
outline: none;
color: #333;
}
.search-btn {
position: absolute;
right: 0;
top: 0;
line-height: 36px;
height: 100%;
width: 36px;
}
.search-btn button {
font-size: 36px;
opacity: .6;
}
.search-btn button:hover {
opacity: .8;
}
+49
View File
@@ -0,0 +1,49 @@
import React, { Component } from 'react';
import { observer } from 'mobx-react';
import './SearchBar.css';
import { DataStore } from '../DataStore';
@observer
class SearchBar extends Component {
state = {
value:''
}
onClickBtn = () => {
if(this.state.value) {
DataStore.keyword = this.state.value;
DataStore.page = 1;
DataStore.searchSongListStore();
}else{
DataStore.keyword = '';
DataStore.page = 0;
DataStore.newSongListStore();
}
}
onTextChange = (event) => {
this.setState({
value: event.target.value
});
}
render() {
return (
<div className="search-box">
<div className="search-input">
<input type="text" placeholder="搜索歌曲或歌手"
value={this.state.value}
onChange={this.onTextChange} />
</div>
<div className="search-btn">
<button className="iconfont icon-search"
onClick={this.onClickBtn}></button>
</div>
</div>
);
}
}
export default SearchBar;
+94
View File
@@ -0,0 +1,94 @@
.song-list-header {
overflow: hidden;
border-bottom:rgba(255,255,255,.1) solid 1px;
}
.song-list-main {
margin-top: 10px;
overflow: hidden;
flex: 1;
}
.song-list-header>.song-list-row {
margin-bottom: 10px
}
.song-list-body {
margin-top: 10px;
/* margin-bottom: 10px; */
height: 480px;
}
.scrollarea {
height: 100%;
}
/* .scrollarea-content {
height: 100%;
} */
.scrollarea .scrollbar-container.vertical .scrollbar {
border-radius: 4px;
}
.song-list-row {
display: flex;
justify-content: flex-start;
text-align: left;
margin-bottom: 15px;
}
.song-list-row>li {
height: 30px;
line-height: 30px;
}
.song-list-row>.sort {
width: 30px;
text-align: center;
opacity: .6;
}
.song-list-row>.song-name {
flex:1;
opacity: .6;
}
.song-list-row>.singer-name {
width: 25%;
min-width: 150px;
padding-left: 20px;
box-sizing: border-box;
opacity: .6;
}
.song-list-row>.operate {
width: 15%;
min-width: 80px;
padding-right: 10px;
box-sizing: border-box;
display: flex;
justify-content: space-around;
}
.song-list-row>.operate>button {
font-size: 30px;
width: 30px;
height: 30px;
display: inline-block;
opacity: .6;
}
.song-list-row>.operate>button:hover {
opacity: .8;
}
.more-box {
height: 30px;
line-height: 30px;
}
.more-box>button {
opacity: .8;
font-size: 16px;
}
+92
View File
@@ -0,0 +1,92 @@
import React, { Component } from 'react';
import { observer } from 'mobx-react';
import ScrollArea from 'react-scrollbar';
import './SongList.css';
import { DataStore } from '../DataStore';
@observer
class SongList extends Component {
onClickAddIcon = (event) => {
let hash = event.target.getAttribute('hash');
DataStore.addSongInList(hash);
//console.log(DataStore.playSongList.slice());
}
onClickPlayIcon = (event) => {
let hash = event.target.getAttribute('hash');
DataStore.addSongAndPlay(hash);
DataStore.isPlay = true;
}
onClickMore = (event) => {
DataStore.page = DataStore.page + 1;
DataStore.searchSongListStore();
}
ListItems = (list) => {
//console.log(list);
if(list.length > 0) {
let listItems = [];
for (let i = 0; i < list.length; i++) {
listItems.push(
<ul className="song-list-row" key={list[i].Hash}>
<li className="sort">{i + 1}</li>
<li className="song-name ell">{list[i].SongName}</li>
<li className="singer-name ell">{list[i].SingerName}</li>
<li className="operate">
<button className="iconfont icon-add"
hash={list[i].Hash}
onClick={this.onClickAddIcon}></button>
<button className="iconfont icon-play"
hash={list[i].Hash}
onClick={this.onClickPlayIcon}></button>
</li>
</ul>
)
}
if(DataStore.listType === 0 && list.length >= DataStore.pageSize) {
listItems.push(
<div className="more-box">
<a onClick={this.onClickMore}>点击加载更多</a>
</div>
);
}
return (<div className="song-list-body"
style={{height:DataStore.scrollHeight + 'px'}}>
<ScrollArea
horizontal={false}>
{listItems}
</ScrollArea>
</div>);
}else{
return (<div className="song-list-body">空空如也,赶紧去添加歌曲吧!</div>);
}
}
componentDidMount = () => {
DataStore.newSongListStore();
}
render() {
return (
<div className="song-list-main">
<div className="song-list-header">
<ul className="song-list-row">
<li className="sort"></li>
<li className="song-name">歌曲</li>
<li className="singer-name">歌手</li>
<li className="operate"></li>
</ul>
</div>
{this.ListItems(DataStore.searchSongList.slice())}
</div>
);
}
}
export default SongList;
+34
View File
@@ -0,0 +1,34 @@
.volume-box {
width: 126px;
height: 36px;
}
.volume-icon {
width: 36px;
height: 36px;
line-height: 36px;
float: left;
}
.volume-icon>button {
width: 100%;
height: 100%;
font-size: 36px;
opacity: .8;
}
.volume-icon>button:hover{
opacity: 1;
}
.volume-slider {
width: 80px;
height: 36px;
margin-left: 10px;
float: left;
}
.volume-slider>.ant-slider {
margin-left: 0;
margin-right: 0;
}
+46
View File
@@ -0,0 +1,46 @@
import React, { Component } from 'react';
import { Slider } from 'antd';
import { observer } from 'mobx-react';
import './Volume.css';
import { DataStore } from '../DataStore';
@observer
class Volume extends Component {
handleClick = (e) => {
DataStore.silent = !DataStore.silent;
}
onChange = (value) => {
if(value === 0) {
DataStore.silent = true;
}else{
DataStore.silent = false;
}
DataStore.volume = value / 100;
}
render() {
return (
<div className="volume-box">
<div className="volume-icon">
<button className={(DataStore.silent?"icon-mute":"icon-volume") + " iconfont"}
onClick={this.handleClick}></button>
</div>
<div className="volume-slider">
<Slider
defaultValue={DataStore.volume * 100}
value={DataStore.volume * 100}
disabled={DataStore.disabled}
tipFormatter={null}
onChange={this.onChange}
/>
</div>
</div>
);
}
}
export default Volume;
+24
View File
@@ -0,0 +1,24 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
#root {
width: 100%;
height: 100%;
}
.hide {
display: none;
}
.show {
display: block;
}
.ell {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
+8
View File
@@ -0,0 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
+6
View File
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+117
View File
@@ -0,0 +1,117 @@
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ'
);
});
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}