首先我们要下载一下图标,可以到 iconfont 下载,然后放在 project_root/static
下面,文档在这里。
由于动态组件,退出会销毁,这很明显不是我们想要的,所以只能单独提取出来,而却最好把状态都放到全局来。
首先添加三个状态。
files: [],
index: 0,
music: true
修改一下 index.ejs
, 因为我们等会要做 css
动画,把背景颜色做一下 兼容,并且把高度给撑开。
html {
background: #383a41;
min-height: 100vh;
}
单独提取出来就不会被销毁。Music 要是被销毁了,那就没法播放音频了。
<Tip/>
<svelte:component this="{$currentPage}" name="page" />
<Music/>
<script>
export default {
components: {
Tip: './components/Tip.svelte',
Music: './pages/Music.svelte'
},
};
</script>
通过全局状态来控制菜单的显隐,同样的方式,控制 Music.svelte
.
<div class="wrap {$music ? 'hidden': 'show'}">
<div className="music-page-btn" on:click="$set({music: true})">播放器</div>
</div>
.hidden {
display: none;
}
.wrap :global(a),
.wrap div {
display: inline-flex;
// .....
}
修改 Link.svelte
使点击 Back.svelte
将 Music.svelte
隐藏,所以我们每次点击都可以将这个转态置为 false
,这样就保证了 Music.svelte
的隐藏。
this.refs.link.addEventListener(
'click',
e => {
e.preventDefault()
this.store.set({
music: false
})
this.store.changePage(this.get().to)
},
false
)
首先我们需要把状态的更改都是用 this.store.set
和 $set
进行修改,然后还需要将文件名处理成正确的显示格式。
::-webkit-scrollbar-thumb
是简单的修改了一下滚动条的颜色。
<div class="music-page { $music ?'show':'hidden' } ">
<Back/>
<div>
<ul class="main">
{#each files as file, index}
<li on:click="$set({index})">{handleName(file)}</li>
{/each}
</ul>
<div class="control">
<div class="title">{title}</div>
<div class="flex">
<button on:click="file()"><img src="plus-circle.png" width="25" height="25" alt="add"></button>
<button on:click="play()"><img src="play-circle.png" width="25" height="25" alt="play"></button>
<button on:click="pause()"><img src="time-out.png" width="25" height="25" alt="stop"></button>
<button on:click="prev()"><img src="left-circle.png" width="25" height="25" alt="prev"></button>
<button on:click="next()"><img src="right-circle.png" width="25" height="25" alt="next"></button>
</div>
</div>
</div>
</div>
<script>
import {
ipcRenderer,
remote
} from "electron";
const {
dialog,
app
} = remote
const {
readdir
} = remote.require('fs-extra')
import {
resolve
} from 'path'
function handleName(name) {
return name.replace('.mp3', '').split('-')[1]
}
export default {
data() {
return {
files: [],
title: ''
};
},
helpers: {
handleName
},
components: {
Back: '../components/Back.svelte'
},
oncreate() {
this.player = new Audio()
document.querySelector('#app').appendChild(this.player)
const {
files
} = this.store.get()
this.set({
files
})
this.player.onended = this.next.bind(this) // 结束进入下一首
let listener = this.store.on('state', ({
changed
}) => {
if (changed.index || changed.files) { // 当重新选了文件,播放第一首
const {
files,
index
} = this.store.get()
this.player.src = "file://" + files[index]
this.set({
title: handleName(this.get().files[index])
})
this.play()
}
})
this.on('destroy', listener.cancel)
},
ondestroy() {
document.querySelectorAll("audio").forEach(a => a.remove())
},
methods: {
next() {
const {
index,
files
} = this.store.get()
let i = index + 1
if (i > files.length - 1) {
i = files.length - 1
}
this.store.set({
index: i
})
},
prev() {
const {
index,
files
} = this.store.get()
let i = index - 1
if (i < 0) {
i = 0
}
this.store.set({
index: i
})
},
play() {
if (!this.player.currentSrc) {
const {
files,
index
} = this.store.get()
this.set({
title: handleName(files[index])
})
this.player.src = "file://" + files[index]
}
this.player.play()
},
pause() {
this.player.pause()
},
file() {
dialog.showOpenDialog({
title: '选择音乐目录',
defaultPath: app.getPath('home'),
properties: ['openDirectory']
}, async(folders) => {
if (folders.length > 0) {
const folder = folders[0]
let files = await readdir(folder)
this.set({
files
})
const compare = (a, b) => {
let t1 = a.split('-')[0];
let t2 = b.split('-');
return parseInt(t1) - parseInt(t2)
}
files = files.sort(compare).map(file => resolve(folder, file))
this.store.set({
files,
index: 0
})
}
})
}
}
};
</script>
<style>
.music-page {
transition: all ease-in .1s;
height: 100vh;
}
.hidden {
transform: translate(100%, 0)
}
.show {
transform: translate(0, 0)
}
.control {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100px;
background: #383A41;
display: flex;
flex-direction: column;
}
.title {
color: #f8f8f8;
padding: 1rem 0;
text-align: center;
font-size: .8rem;
min-height: 54px;
user-select: none;
}
.flex {
display: flex;
justify-content: space-around;
align-items: center;
flex-basis: 100%;
}
.control button {
background: transparent;
border: none;
outline: none;
cursor: pointer;
}
.control button:hover {
background: #2f2f2f;
}
.main {
height: calc(100vh - 160px);
margin-bottom: 60px;
overflow: scroll;
background: #303238;
}
li {
color: #f8f8f8;
padding: 1.1rem 1rem;
font-size: .9rem;
cursor: pointer;
}
li:hover {
background: #2f2f2f;
}
::-webkit-scrollbar-thumb {
background: #383A41;
}
.control button {
line-height: 0;
padding: .5rem 1rem;
}
</style>