小程序音樂播放器

效果圖

一、實現(xiàn)效果

  • 點擊播放按鈕歌曲進(jìn)行播放刽锤,點擊暫停停止播放
  • 顯示歌曲當(dāng)前播放時間和總時間
  • 隨著歌曲播放進(jìn)度條變紅,點擊進(jìn)度條任意位置都可以到達(dá)(快進(jìn)操作)
  • 歌唱哪句歌詞高亮具篇,一句結(jié)束自動滾動
    注意:實現(xiàn)整個效果膘融,需要將每個步驟的代碼整合在一起
    二舷夺、步驟

1.搭建靜態(tài)頁面

<!--pages/music1/index.wxml-->
<view class="container">
  <!-- 歌名和演唱者 -->
  <view class="header">
    <view>三十歲的女人</view>
    <view class="author">趙雷</view>
  </view>
  <!-- 歌詞部分茵瘾,可滾動 -->
  <scroll-view class="middle" scroll-y="true">
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>歌詞放置處</view>
    <view>末尾</view>

  </scroll-view>

  <!-- 底部進(jìn)度條 -->
  <view class="footer">

    <view class="progress-bar">
      <view>00:00</view>
      <view class="progress-line">
        <view class="progress-bg"></view>
        <view class="progress-red"></view>
        <view class="progress-dot">
          <icon class="iconfont iconyuandian1"></icon>
        </view>
      </view>
      <view>00:00</view>
    </view>

  </view>

  <!-- 操作按鈕 -->
  <view class="btn-group">
    <view class="prev">
      <icon class="iconfont iconshangyishou"></icon>
    </view>
    <view class="play">
      <icon class="iconfont iconweibiaoti--"></icon>
    </view>
    <view class="next">
      <icon class="iconfont iconxiayishou"></icon>
    </view>
  </view>

</view>
@import '/assets/fonts/iconfont.wxss';

.container {
  width: 100vw;
  height: 100vh;
  background: #000;
  color: #fff;
}

.header {
  padding: 20rpx;
  line-height: 30px;
  text-align: center;
}

.header .author{
  font-size: 16px;
}
.middle{
  height: 55vh;
  text-align: center;
  margin-top: 20px;
 font-size: 16px;
}
.middle view{
  height: 60rpx;
  line-height: 60rpx;
}
.footer{
  padding: 10px;
  width: 100%;
  box-sizing: border-box;
  margin-top: 30px;
}
.progress-bar{
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.progress-line{
  flex: 1;
  margin: 0 5px;
 position: relative;
}
.progress-bg{
  width: 100%;
  background: #fff;
  height: 4rpx;
}
.progress-red{
  width: 30%;
  height:100%;
  position: absolute;
  background: red;
  top: 0;
  left: 0;
}
.progress-dot{
  position: absolute;
  top: -15px;
  left: -5px;
}
.btn-group{
  display: flex;
  justify-content: center;
  align-items: center;
}
.play{
  margin: 0 5px;
}
.play icon{
  font-size: 50px;
}
.prev icon, .next icon{
  font-size: 30px;
}
樣式效果圖

2.點擊播放按鈕歌曲進(jìn)行播放踢关,點擊暫停停止播放

思路:使用isplaying來控制樣式,false顯示播放圖標(biāo),歌曲暫停播放,true顯示暫停圖標(biāo),歌曲播放

  • 2.1放置audio,不顯示在頁面中
<audio src="{{src}}" id="myAudio" >
  • 2.2點擊播放圖標(biāo)可以切換為暫停圖標(biāo),并且可以控制歌曲的播放和暫停
    <view class="play"  bindtap="changeState">
      <icon class="iconfont {{isplaying===false?' iconweibiaoti--':'iconzanting'}}"></icon>
    </view>
data: {
    isplaying: false, //控制
    src: 'http://audio01.dmhmusic.com/71_53_T10046221715_128_4_1_0_sdk-cpm/0206/M00/6D/81/ChR47FsrnyWAKVx0AE-m4DXLKqo190.mp3?xcode=039efb32a1d055924b8d829e8d3f68b006abe09', //音樂地址
  },
  /**
   * 生命周期函數(shù)--監(jiān)聽頁面顯示
   */
  onShow: function() {
    // 獲取當(dāng)前頁面的audio
    this.audioCtx = wx.createAudioContext('myAudio')
  },
  changeState() {
    
    if (!this.data.isplaying) {
      this.audioCtx.play()//歌曲播放
    } else {
      this.audioCtx.pause()//歌曲暫停
    }
     this.setData({
      isplaying: !this.data.isplaying
    })
  },

3.顯示歌曲當(dāng)前播放時間和總時間,隨著歌曲播放進(jìn)度條變紅,
點擊進(jìn)度條任意位置都可以到達(dá)(快進(jìn),后退操作);

思路

  • 播放時會觸發(fā)bindtimeupdate,獲取當(dāng)前時間currentTime和歌曲總時間duration;
  • 定義變量progressPercent控制 progress-red的寬度,progress-dot的left;
  • 獲取當(dāng)前位置offsetX占progress-line寬度width百分比p,使用this.audioCtx.seek(p在總時間的占比)跳轉(zhuǎn)歌曲的位置(實現(xiàn)快進(jìn)后退)
<view class="progress-line"  bindtap="seek">
        <view class="progress-bg"></view>
        <view class="progress-red" style="width:{{progressPercent}}%;"></view>
        <view class="progress-dot" style="left:calc({{progressPercent}}% - 5px)">
          <icon class="iconfont iconyuandian1"></icon>
        </view>
 </view>
data: {
    currentTime: '00:00', //當(dāng)前時間
    duration: "00:00", //總時間
    progressPercent: 0, //百分比笼吟,控制寬度
    width: 0, //獲取progress-line的寬度
    durationTime:0,//總時間库物,跳轉(zhuǎn)指定位置使用
  },
// 化為時分秒
  formatMs2Obj(total) {
    var h = this.repairZero(Math.floor(total / 3600))
    var m = this.repairZero(Math.floor((total - h * 3600) / 60))
    var s = this.repairZero(Math.floor(total - h * 3600 - m * 60))
    //ES6 結(jié)構(gòu)  h:h
    return {
      h,
      m,
      s
    }
  },
  /**
   * 補(bǔ)零
   */
  repairZero(num) {
    return num < 10 ? ("0" + num) : num
  },
 timeupdate(e) {
    var obj1 = this.formatMs2Obj(e.detail.currentTime)
    var obj2 = this.formatMs2Obj(e.detail.duration)
    var str1 = obj1.m + ":" + obj1.s
    var str2 = obj2.m + ":" + obj2.s
    // 
    if (this.data.currentTime !== str1) {
      // 更新當(dāng)前時間
      this.setData({
        currentTime: str1,
        progressPercent: e.detail.currentTime * 100 / e.detail.duration
      })
    }
    // 賦值總時間,每次總時間一致不用賦值
    if (this.data.duration !== str2) {
      this.data.durationTime=e.detail.duration//總時間
      this.setData({
        duration: str2
      })
    }
  },
  /**
   * 生命周期函數(shù)--監(jiān)聽頁面加載
   */
  onLoad: function(options) {

    // 獲取節(jié)點贷帮,獲取progress-line寬度width
    var query = wx.createSelectorQuery()
    var that = this;
    query.select('.progress-line').boundingClientRect(function(rect) {
      that.setData({
        width: rect.width//寬度
      })
    }).exec();
  },
  seek(e) {
    // 點擊白色進(jìn)度條任意位置戚揭,紅色進(jìn)度條到達(dá)點擊處
    // 獲取當(dāng)前位置
    var offsetX = e.touches[0].pageX - e.currentTarget.offsetLeft
    // 獲取當(dāng)前位置站總寬度的百分比
    var p = offsetX / this.data.width
    // seek跳轉(zhuǎn)至指定位置,
    // this.data.durationTime*p,獲取位置百分比在總時間中的占比
    this.audioCtx.seek(this.data.durationTime*p)
  }
快進(jìn)后退

4.歌曲播放哪句歌詞高亮撵枢,結(jié)束后歌詞自動滾動(到第六行的時候在滾動)

思路

  • 找到歌詞lrc(帶時間的),存儲在easy-mock中(也可以根據(jù)需求存儲在其他位置)民晒,需要在數(shù)組中增加分割的字符(此處加的是;)
  • 分割歌詞,最終目標(biāo)數(shù)組分割成[{time:" ",text:" "}],需要使用split,trim,substr
    split() 方法用于把一個字符串分割成字符串?dāng)?shù)組
    trim()去除字符串兩端的空格
    substr() 方法可在字符串中抽取從 start 下標(biāo)開始的指定數(shù)目的字符
  • 循環(huán)數(shù)組songtext來顯示歌詞
  • 定義變量activeIndex控制高亮,activeIndex等于i
  • scrolltop設(shè)置滾動距離,當(dāng)i>5的時候才可以滾動
easy-mock數(shù)據(jù)中代碼
 <scroll-view class="middle" scroll-y="true"  scroll-top="{{scrolltop}}">
    <view wx:for="{{songtext}}"  class="{{activeIndex===index?'active':''}}">{{item.text}}</view>
  </scroll-view>
 data: {
    songtext: '', //歌詞
    activeIndex: 0, //控制高亮
    scrolltop: 0 //滾動距離
  },
onLoad:function(){
 // 獲取歌詞,請求easy-mock數(shù)據(jù)
      wx.request({
      url: 'https://www.easy-mock.com/mock/5d032195cccf932f99d7422e/xcx/songtext',
      success: res => {
        var arr = res.data.songtext.split(";")
        // console.log(arr)
        arr = arr.map(r => {
          // 去除每一項的空格很重要,不然后面設(shè)置當(dāng)前項是空格的锄禽,上一項不是空格仍然高亮不起作用
          var arr1 = r.trim().substr(1).split("]")
          return {
            time: arr1[0],
            text: arr1[1]
          }
        })
        this.setData({
          songtext: arr
        })
      }
    })
},
timeupdate(e) {
   //控制高亮
    // 循環(huán)數(shù)組
    for (var i = 0; i < this.data.songtext.length; i++) {
      // 當(dāng)前播放時間等于數(shù)組time時activeIndex才改變值
      if (this.data.currentTime === this.data.songtext[i].time) {
        // 當(dāng)activeIndex不等于i時更新潜必,當(dāng)songtext有值為空格時也不更新
        if (this.data.activeIndex !== i && this.data.songtext[i].text) {
          this.setData({
            activeIndex: i,
            scrolltop: 30 * (i - 5)
          })
          if (i > 5) {
            this.setData({
              // 此處的滾動距離可以為高度*i,到第5行在進(jìn)行滾動
              scrolltop: 30 * (i - 5)
            })
          }
          break
        }
      }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市沃但,隨后出現(xiàn)的幾起案子磁滚,更是在濱河造成了極大的恐慌,老刑警劉巖宵晚,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垂攘,死亡現(xiàn)場離奇詭異,居然都是意外死亡淤刃,警方通過查閱死者的電腦和手機(jī)晒他,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逸贾,“玉大人陨仅,你說我怎么就攤上這事津滞。” “怎么了灼伤?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵据沈,是天一觀的道長。 經(jīng)常有香客問我饺蔑,道長,這世上最難降的妖魔是什么嗜诀? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任猾警,我火速辦了婚禮,結(jié)果婚禮上隆敢,老公的妹妹穿的比我還像新娘发皿。我一直安慰自己,他們只是感情好拂蝎,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布穴墅。 她就那樣靜靜地躺著,像睡著了一般温自。 火紅的嫁衣襯著肌膚如雪玄货。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天悼泌,我揣著相機(jī)與錄音松捉,去河邊找鬼。 笑死馆里,一個胖子當(dāng)著我的面吹牛隘世,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鸠踪,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼丙者,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了营密?” 一聲冷哼從身側(cè)響起械媒,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎卵贱,沒想到半個月后滥沫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡键俱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年兰绣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片编振。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡缀辩,死狀恐怖臭埋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情臀玄,我是刑警寧澤瓢阴,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站健无,受9級特大地震影響荣恐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜累贤,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一叠穆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧臼膏,春花似錦硼被、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至始鱼,卻和暖如春仔掸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背医清。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工嘉汰, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人状勤。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓鞋怀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親持搜。 傳聞我的和親對象是個殘疾皇子密似,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容