你真的會自定義audio面板嗎?

前言

近期項目中經(jīng)常使用到音頻功能母廷,然而自帶的音頻控制面板大部分情況都不能滿足需
求糊肤,固用vue自定義了一個通用的的audio面板。主要功能有:播放馆揉、暫停、自定義進(jìn)度
條勤讽,靜音控制等內(nèi)容拗踢,廢話不多說,先上圖

頁面組成

  • 播放暫停模塊
  • 自定義進(jìn)度條模塊
  • 音頻時長模塊
  • 靜音控制模塊

主要思路

  • 使用ref獲取audio元素
  • 調(diào)用 audio play pause方法實(shí)現(xiàn)暫停播放
  • 為audio添加事件: @canplay @timeupdate @ended
  • computed中設(shè)置靜音和非靜音圖片資源

html

  <div class="app">
    <div class="dialogDetailAudio" onselectstart="return false">
      <img class="dialogAudioPlay" :src="audioImg" title="播控" @click="playAudio">
      <span class="dialogAudioTime">{{time}}</span>
      <div class="dialogAudioProgress" ref="dialogAudioProgress" @mousedown="controlAudioProgress($event)">
        <span class="progressDot" :style="dotStyle"></span>
        <span class="bar" :style="progressStyle"></span>
      </div>
      <span class="dialogAudioDuration">{{duration}}</span>
      <img class="dialogAudioListen" :src="dialogAudioListen" title="靜音" @click="listenDialogAudio">
      <img class="dialogAudioDownload" src="./callRecordDownload.png" title="下載" @click="downloadCallRecord">
      <audio ref="recordAudio" class="recordAudio" type="audio/mpeg" 
        @canplay="canPlay" @timeupdate="timeUpdate" @ended="onEnded" :src="audioUrl">
      </audio>
    </div>
  </div>

css

* {
  margin: 0;
  padding: 0;
}

.dialogDetailAudio {
  margin-top: 100px;
  margin-left: auto;
  margin-right: auto;
  width: 550px;
  height: 49px;
  line-height: 49px;
  background: rgba(255, 255, 255, 1);
  box-shadow: 0px 5px 30px 0px rgba(29, 34, 54, 0.18);
  border-radius: 6px;
}

.dialogAudioPlay {
  display: inline-block;
  position: relative;
  top: 6px;
  margin-left: 15px;
  cursor: pointer;
  width: 23px;
  height: 23px;
}

.dialogAudioTime {
  margin-left: 11px;
  font-size: 11px;
  font-weight: 400;
  color: rgba(51, 51, 51, 1);
}

.dialogAudioProgress {
  display: inline-block;
  width: 300px;
  height: 2px;
  background: rgba(212, 249, 232, 1);
  border-radius: 1px;
  margin-left: 12px;
  position: relative;
}

.progressDot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  -moz-border-radius: 50%;
  -webkit-border-radius: 50%;
  background-color: rgba(5, 180, 147, 1);
  position: absolute;
  left: 0;
  top: 50%;
  margin-top: -5px;
  margin-left: -5px;
  cursor: pointer;
}

.bar {
  height: 100%;
  background: rgba(5, 180, 147, 1);
  border-radius: 3px;
  display: inline-block;
  position: absolute;
}

.dialogAudioDuration {
  margin-left: 11px;
  font-size: 11px;
  font-weight: 400;
  color: rgba(34, 34, 34, 1);
}

.dialogAudioListen,
.dialogAudioDownload {
  width: 16px;
  height: 13px;
  cursor: pointer;
}

.dialogAudioListen {
  margin-left: 8px;
}

.dialogAudioDownload {
  margin-left: 5px;
}

js

var app = new Vue({
  el: ".app",
  data() {
    return {
      time: "00:00",
      duration: "00:00",
      progressStyle: { width: "" },
      dotStyle: { left: "" },
      audioUrl: "http://file.duzhai.net/Files/Audio/2017-08/af01ab05-d112-4ad7-9cff-2cfecced5bc6.mp3",
      audioImg: "./dialogDetailPlay.png",
      dialogAudioListenGroup: ["./callRecordListen.png", "./quite.png"],
      imgIndex: 0,
    }
  },

  //計算屬性 切換靜音圖片
  computed: {
    dialogAudioListen() {
      return this.dialogAudioListenGroup[this.imgIndex]
    }
  },

  methods: {
    //播放暫途遥控制
    playAudio() {
      let recordAudio = this.$refs.recordAudio; //獲取audio元素
      if (recordAudio.paused) {
        this.audioImg = "./dialogDetailPause.png"
        recordAudio.play();
      } else {
        this.audioImg = "./dialogDetailPlay.png"
        recordAudio.pause();
      }
    },

    //進(jìn)度條更新
    timeUpdate() {
      this.duration = this.transTime(this.$refs.recordAudio.duration);
      let timeStr = parseInt(this.$refs.recordAudio.currentTime);
      this.time = this.transTime(timeStr);
      let scales = this.$refs.recordAudio.currentTime / this.$refs.recordAudio.duration;
      this.progressStyle.width = scales * 100 + '%';
      this.dotStyle.left = scales * 100 + '%';
    },

    //播放結(jié)束
    onEnded() {
      this.audioImg = "./dialogDetailPlay.png";
      this.time = "00:00";
      this.progressStyle.width = 0;
      this.dotStyle.left = 0;
    },

    //用戶可以開始播放audio
    canPlay() {
      //獲取audio音頻文件總時長 并設(shè)置到界面并解決出現(xiàn) NAN 的問題
      this.duration = this.transTime(this.$refs.recordAudio.duration);
    },

    //靜音控制
    listenDialogAudio() {
      this.imgIndex = (this.imgIndex + 1) % (this.dialogAudioListenGroup).length;
      if (this.dialogAudioListen == "./quite.png") {
        this.$refs.recordAudio.volume = 0;
      } else {
        this.$refs.recordAudio.volume = 1;
      }
    },

    //鼠標(biāo)點(diǎn)擊移動播放進(jìn)度
    controlAudioProgress(event) {
      let audio = this.$refs.recordAudio;
      let dialogAudioProgress = this.$refs.dialogAudioProgress;
      if (!audio.paused || audio.currentTime != 0) {
        let pgsWidth = parseFloat(window.getComputedStyle(dialogAudioProgress, null).width.replace('px', ''));
        let rate = event.offsetX / pgsWidth;
        audio.currentTime = audio.duration * rate;
        this.timeUpdate();
      }
    },

    //下載音頻
    downloadCallRecord() {
      console.log("下載...");
    },

    //時間轉(zhuǎn)換
    transTime(value) {
      let time = "";
      let h = parseInt(value / 3600);
      value %= 3600;
      let m = parseInt(value / 60);
      let s = parseInt(value % 60);
      if (h > 0) {
        time = this.formatTime(h + ":" + m + ":" + s);
      } else {
        time = this.formatTime(m + ":" + s);
      }
      return time;
    },

    //時間格式化
    formatTime(value) {
      let time = "";
      let s = value.split(':');
      let i = 0;
      for (; i < s.length - 1; i++) {
        time += s[i].length == 1 ? ("0" + s[i]) : s[i];
        time += ":";
      }
      time += s[i].length == 1 ? ("0" + s[i]) : s[i];
      return time;
    }
  },
})

注意點(diǎn)

  • onselectstart 禁止用戶選中網(wǎng)頁上的內(nèi)容蓄髓。
    用戶點(diǎn)擊進(jìn)度條位置不會選中其他內(nèi)容
  • @canplay 設(shè)置audio總時長并防止出現(xiàn) NAN
  • @ended 視頻播放完畢后應(yīng)回到初始狀態(tài)
  • computed 設(shè)置靜音圖片資源。 音頻的volume默認(rèn)值是1

寫到最后

其實(shí)陡叠,實(shí)現(xiàn)自定義內(nèi)容很簡單,主要就是界面與js邏輯控制的結(jié)合枉阵,只要弄清楚元素對應(yīng)
的API并加以改進(jìn) 就可實(shí)現(xiàn)很多想要的功能。希望本文可以幫到您兴溜,感謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刨沦,一起剝皮案震驚了整個濱河市斋攀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淳蔼,老刑警劉巖裁眯,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件穿稳,死亡現(xiàn)場離奇詭異存皂,居然都是意外死亡逢艘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門疤孕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來央拖,“玉大人,你說我怎么就攤上這事鲜戒。” “怎么了伦腐?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵失都,是天一觀的道長颖系。 經(jīng)常有香客問我辩越,道長,這世上最難降的妖魔是什么黔攒? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮不傅,結(jié)果婚禮上赏胚,老公的妹妹穿的比我還像新娘。我一直安慰自己觉阅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布劫哼。 她就那樣靜靜地躺著割笙,像睡著了一般权烧。 火紅的嫁衣襯著肌膚如雪伤溉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天板祝,我揣著相機(jī)與錄音糯耍,去河邊找鬼。 笑死温技,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的舵鳞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼抛虏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了慕淡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤峰髓,失蹤者是張志新(化名)和其女友劉穎息尺,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搂誉,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡炭懊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凛虽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖至非,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荒椭,我是刑警寧澤舰蟆,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站身害,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏塌鸯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一涨颜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庭瑰,春花似錦、人聲如沸督暂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酒来。三九已至,卻和暖如春堰汉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翘鸭。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工就乓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汉匙,地道東北人生蚁。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像伤锚,于是被迫代替她去往敵國和親志衣。 傳聞我的和親對象是個殘疾皇子屯援,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348