前言
淺淺記錄一下自己開發音樂播放器的曆程,鞏固自己的所學。同時也是深感基礎不牢,地動山搖。
一、audio标簽的使用
1、Audio 對象屬性
屬性 | 描述 |
audioTracks | 傳回表示可用音頻軌道的 AudioTrackList 對象。 |
autoplay | 設定或傳回是否在就緒(加載完成)後随即播放音頻。 |
buffered | 傳回表示音頻已緩沖部分的 TimeRanges 對象。 |
controller | 傳回表示音頻目前媒體控制器的 MediaController 對象。 |
controls | 設定或傳回音頻是否應該顯示控件(比如播放/暫停等)。 |
crossOrigin | 設定或傳回音頻的 CORS 設定。 |
currentSrc | 傳回目前音頻的 URL。 |
currentTime | 設定或傳回音頻中的目前播放位置(以秒計)。 |
defaultMuted | 設定或傳回音頻預設是否靜音。 |
defaultPlaybackRate | 設定或傳回音頻的預設播放速度。 |
duration | 傳回音頻的長度(以秒計)。 |
ended | 傳回音頻的播放是否已結束。 |
error | 傳回表示音頻錯誤狀态的 MediaError 對象。 |
loop | 設定或傳回音頻是否應在結束時再次播放。 |
mediaGroup | 設定或傳回音頻所屬媒介組合的名稱。 |
muted | 設定或傳回是否關閉聲音。 |
networkState | 傳回音頻的目前網絡狀态。 |
paused | 設定或傳回音頻是否暫停。 |
playbackRate | 設定或傳回音頻播放的速度。 |
played | 傳回表示音頻已播放部分的 TimeRanges 對象。 |
preload | 設定或傳回音頻的 preload 屬性的值。 |
readyState | 傳回音頻目前的就緒狀态。 |
seekable | 傳回表示音頻可尋址部分的 TimeRanges 對象。 |
seeking | 傳回使用者目前是否正在音頻中進行查找。 |
src | 設定或傳回音頻的 src 屬性的值。 |
textTracks | 傳回表示可用文本軌道的 TextTrackList 對象。 |
volume | 設定或傳回音頻的音量。 |
2、對象方法
方法 | 描述 |
addTextTrack() | 向音頻添加新的文本軌道。 |
canPlayType() | 檢查浏覽器是否能夠播放指定的音頻類型。 |
fastSeek() | 在音頻播放器中指定播放時間。 |
getStartDate() | 傳回新的 Date 對象,表示目前時間線偏移量。 |
load() | 重新加載音頻元素。 |
play() | 開始播放音頻。 |
pause() | 暫停目前播放的音頻。 |
二、效果
效果如下:
三、代碼
代碼如下:
MusicPlayer.vue
<template>
<div class="music">
<!-- 占位 -->
<div class="m_hold">
</div>
<div class="m_img">
<img :src="this.$parent.songNames[this.$parent.index].png" width="90px" :class="this.$parent.isRun">
</div>
<!-- 歌曲資訊 -->
<div class="m_text">
{{ this.$parent.songNames[this.$parent.index].name }}
<div class="block" style="margin-top:5px">
<el-slider :v-model="value1"></el-slider>
</div>
</div>
<!-- 按鈕 -->
<div class="m_btn">
<a href="#" class="m_prev" @click="playLastSong()"></a>
<a href="#" class="m_play" @click="changeState()" v-show="this.$parent.isShow"></a>
<a href="#" class="m_pause" @click="changeState()" v-show="!this.$parent.isShow"></a>
<a href="#" class="m_next" @click="playNextSong()"></a>
</div>
<!-- 折疊功能 -->
<div class="m_close" @click="changeCloseState()">
<a href=""></a>
</div>
</div>
</template>
<script>
export default {
name: 'MusicPlayer',
data() {
return {
songName: '',
value1:0
}
},
methods: {
changeState() {
this.$emit("play")
},
changeCloseState() {
this.$emit("hello");
},
playNextSong() {
this.$emit("nextSongs");
this.songName = this.$parent.songNames[this.$parent.index].name
},
playLastSong() {
this.$emit("lastSongs");
this.songName = this.$parent.songNames[this.$parent.index].name
}
},
watch:
{
}, mounted() {
this.songName = this.$parent.songNames[this.$parent.index].name
}
}
</script>
<style scoped>
/* 關于播放器的樣式 */
.music {
width: 100%;
height: 120px;
background: black;
/* 相對浏覽器定位 */
position: absolute;
left: 0px;
bottom: 100px;
border-bottom: 50px;
/* 透明度 */
opacity: 0.8;
/* 陰影值 */
box-shadow: 10px 15px 15px 1px black
}
.music .m_hold {
float: left;
width: 90px;
height: 90px;
}
/* 調整音樂盒圖檔 */
.music .m_img {
margin-top: 15px;
margin-left: 10px;
margin-right: 10px;
/* 左浮動 */
float: left;
width: 90px;
height: 90px;
border-radius: 50%;
overflow: hidden;
}
/* 修改文字 */
.music .m_text {
/* 左浮動 */
float: left;
color: white;
font-size: 20px;
/* 字型加粗 */
font-weight: bold;
margin-top: 25px;
margin-left: 20px;
margin-bottom: 10px;
width: 25%;
}
/* 使得所有a标簽一起移動 */
.music .m_btn {
float: left;
position: absolute;
/* 絕對定位:防止歌曲名稱過長,擠出div */
left: 40%;
}
/* 修改a标簽 */
.music .m_btn a {
width: 32px;
height: 32px;
float: left;
margin-top: 50px;
margin-left: 20px;
background: url(@/assets/player_bg.png);
}
.music .m_btn .m_prev {
background-position: -69px 0px;
}
.music .m_btn .m_next {
background-position: -150px 0px;
}
.music .m_btn .m_play {
background-position: -107px -5px;
}
.music .m_btn .m_prev:hover {
background-position: -69px -32px;
}
.music .m_btn .m_next:hover {
background-position: -150px -32px;
}
.music .m_btn .m_play:hover {
background-position: -107px -47px;
}
.music .m_btn .m_pause {
background-position: -292px -94px;
}
.music .m_btn .m_pause:hover {
background-position: -334px -94px;
}
/* 還有一個懸停 沒寫 */
/* 設定最右邊的關閉樣式 */
.music .m_close {
float: right;
background: white;
cursor: pointer;
width: 23px;
height: 100px;
margin-top: 10px;
background: url(@/assets/player_bg.png);
}
/* 設定最右邊的關閉樣式 */
.music_hide {
float: left;
background: white;
cursor: pointer;
width: 23px;
height: 100px;
margin-top: 2px;
}
.go {
animation: bounce-in 2s linear infinite;
}
.come {
animation: none;
}
@keyframes bounce-in {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.open-enter-active {
animation: slide-in linear 0.5s;
}
.open-leave-active {
animation: slide-in reverse linear 0.5s;
}
@keyframes slide-in {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0%);
}
}
</style>
HideMusic.vue
<template>
<div class="music_hide" @click="changeCloseState()"><a href="#" class="m_open"></a></div>
</template>
<script>
export default {
name:'HidePlayer',
methods:{
changeCloseState()
{
this.$emit("hello");
}
}
}
</script>
<style scoped>
.music_hide {
float: left;
background: url(@/assets/player_bg.png);
cursor: pointer;
width: 23px;
height: 100px;
margin-top: 10px;
bottom: 100px;
position: absolute;
background-position-x: -45px;
}
</style>
MyPlayer.vue
<template>
<div>
<transition name="open" mode="out-in">
<component v-bind:is="view" @hello="changeSlideState" @play="changePlayState" @lastSongs="lastSongs"
@nextSongs="nextSongs"></component>
</transition>
<audio class="m_mp3" id="m_mp3" :src="this.songNames[this.index].Url" autoplay loop>
</audio>
</div>
</template>
<script>
import HidePlayer from '@/part/HidePlayer'
import MusicPlayer from '@/part/MusicPlayer'
export default {
name: 'MyPlayer',
data() {
return {
view: MusicPlayer,
isClose: false,
isShow: true,
isRun: 'come',
index: 0,
songNum: 2,
currentTime: '0:00',
duration: '0:00',
songNames: [
{
id: 1,
name: '張韶涵-篇章',
Url: require('@/assets/張韶涵-篇章.mp3'),
png: require('@/assets/篇章.png'),
},
{
id: 2,
name: '愛就一個字 抒情版',
Url: require('@/assets/愛就一個字 抒情版.mp3'),
png: require('@/assets/愛就一個字.png'),
},
{
id: 3,
name: '最偉大的作品-周傑倫',
Url: require('@/assets/最偉大的作品-周傑倫.mp3'),
png: require('@/assets/周傑倫.jpg'),
},
{
id: 4,
name: '等你下課 (with 楊瑞代)-周傑倫',
Url: require('@/assets/等你下課 (with 楊瑞代)-周傑倫.mp3'),
png: require('@/assets/等你下課.png'),
},
{
id: 5,
name: '告白氣球-周傑倫',
Url: require('@/assets/告白氣球-周傑倫.mp3'),
png: require('@/assets/告白氣球.png'),
},
{
id: 6,
name: '還在流浪-周傑倫',
Url: require('@/assets/還在流浪-周傑倫.mp3'),
png: require('@/assets/還在流浪.png'),
},
]
}
},
components: {
HidePlayer,
MusicPlayer
},
methods: {
changeSlideState() {
this.isClose = !this.isClose;
if (this.isClose) {
this.view = HidePlayer;
} else {
this.view = MusicPlayer;
}
},
changePlayState() {
if (!this.isShow) {
this.isShow = true;
this.isRun = "come";
document.getElementById("m_mp3").pause();
} else {
this.isShow = false;
this.isRun = "go";
var my_mp3 = document.getElementById("m_mp3");
my_mp3.play();
}
},
nextSongs() {
if (this.isShow) {
this.isShow = false;
this.isRun = "go";
}
this.index = (this.index + 1) % this.songNum;
},
lastSongs() {
if (this.isShow) {
this.isShow = false;
this.isRun = "go";
}
if (this.index == 0) {
this.index = this.songNum - 1;
} else {
this.index = this.index - 1;
}
}
}, mounted() {
this.songNum = this.songNames.length;
}
}
</script>
<style scoped>
.open-enter-active {
animation: slide-in linear 0.5s;
}
.open-leave-active {
animation: slide-in reverse linear 0.5s;
}
@keyframes slide-in {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0%);
}
}
</style>
四、難點解析
1、過渡動畫的實作
參考了vue文檔過渡&動畫中多個元件的過渡(下面三份代碼)。vue文檔
<transition name="component-fade" mode="out-in">
<component v-bind:is="view"></component>
</transition>
new Vue({
el: '#transition-components-demo',
data: {
view: 'v-a'
},
components: {
'v-a': {
template: '<div>Component A</div>'
},
'v-b': {
template: '<div>Component B</div>'
}
}
})
.component-fade-enter-active, .component-fade-leave-active {
transition: opacity .3s ease;
}
.component-fade-enter, .component-fade-leave-to
/* .component-fade-leave-active for below version 2.1.8 */ {
opacity: 0;
}
是以分化出MusicPlayer.vue 和 HideMusic.vue,由此又産生了元件内通信的問題。
2、元件内通信
為什麼會産生元件内的通信?原因在于:MusicPlayer元件和HidePlayer元件,隻能有一個展示,但是在不展示的過程中,他的資料應該也是實時改變的。例如MusicPlayer元件上有播放按鈕,如果不采用元件通信,那麼MusicPlayer重新渲染的時候,播放按鈕會回到最初的設定,是不符合邏輯的。是以需要采用元件内通信的方式。實作的方式也比較簡單,子元件直接通路父元件的資料,子元件通過$emit調用父元件的方法,修改父元件的資料。
3、旋轉動畫的實作
.go {
animation: bounce-in 2s linear infinite;
}
.come {
animation: none;
}
@keyframes bounce-in {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<div class="m_img">
<img :src="this.$parent.songNames[this.$parent.index].png" width="90px" :class="this.$parent.isRun">
</div>