init glass player
This commit is contained in:
commit
adcc43cf8f
50 files changed
+13488
No files matched your search
+147
@@ -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
@@ -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;
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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 }
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 131 KiB |
Binary file not shown.
@@ -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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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.
@@ -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);
|
||||
} */
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
}
|
||||
}
|
||||
@@ -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 |
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user