20181023 更新
原來(lái)的代碼拖動(dòng)進(jìn)度點(diǎn)只寫(xiě)了mouse事件党涕,手機(jī)端的話應(yīng)該是touch事件。所以出現(xiàn)了有朋友說(shuō)的移動(dòng)端無(wú)法拖動(dòng)進(jìn)度條的問(wèn)題≡叻荩現(xiàn)在更新的代碼已經(jīng)加上touch事件,即無(wú)論是手機(jī)模式瀏覽,還是PC模式瀏覽都可以拖動(dòng)進(jìn)度點(diǎn)來(lái)調(diào)節(jié)音頻播放進(jìn)度噪珊。完整代碼也已經(jīng)放上碼云,需要的同學(xué)可以自行下載齐莲。
說(shuō)明:
需求要求這個(gè)音頻標(biāo)簽首先要是可適配移動(dòng)端瀏覽器的痢站,音頻樣式就是參考微信做的。
最終效果如下:具體實(shí)現(xiàn)
思路:
H5 的 <audio>
標(biāo)簽是由瀏覽器負(fù)責(zé)實(shí)現(xiàn)默認(rèn)樣式的选酗。所以不同的瀏覽器樣式不一樣阵难,有些還不太美觀。所以我們一般會(huì)去掉默認(rèn)樣式芒填,自己重新寫(xiě)呜叫。具體操作是定義 <audio>
的時(shí)候去掉 controls
屬性,這樣就可以隱藏原生的 audio
殿衰, 然后就可以加上自己寫(xiě)的 html + css 代碼了朱庆。最后用 js
捕獲 audio
對(duì)象,為它添加各種播放控制事件播玖。
1. 定義標(biāo)簽
這個(gè)很簡(jiǎn)單椎工,就是用H5 <audio>
標(biāo)簽定義音頻的方式。
html 代碼:
<div class="audio-wrapper">
<audio>
<source src="Files/Audio/2017-08/e259d760-5f1a-4ae0-a838-34d237ea93cc.mp3" type="audio/mp3">
</audio>
<div class="audio-left">![](image/play.png)</div>
<div class="audio-right">
<p style="max-width: 536px;">Beta-B_Kan R. Gao.mp3</p>
<div class="progress-bar-bg" id="progressBarBg"><span id="progressDot"></span>
<div class="progress-bar" id="progressBar"></div>
</div>
<div class="audio-time"><span class="audio-length-current" id="audioCurTime">00:00</span><span class="audio-length-total">01:06</span></div>
</div>
</div>
css 代碼:
.audio-wrapper {
background-color: #fcfcfc;
margin: 10px auto;
max-width: 670px;
height: 70px;
border: 1px solid #e0e0e0;
}
.audio-left {
float: left;
text-align: center;
width: 18%;
height: 100%;
}
.audio-left img {
width: 40px;
position: relative;
top: 15px;
margin: 0;
display: initial; /* 解除與app的樣式?jīng)_突 */
cursor: pointer;
}
.audio-right {
margin-right: 2%;
float: right;
width: 80%;
height: 100%;
}
.audio-right p {
font-size: 15px;
height: 35%;
margin: 8px 0;
/* 歌曲名稱只顯示在一行,超出部分顯示為省略號(hào) */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 243px; /* 要適配小屏幕手機(jī)维蒙,所以最大寬度先設(shè)小一點(diǎn)掰吕,后面js根據(jù)屏幕大小重新設(shè)置 */
}
.progress-bar-bg {
background-color: #d9d9d9;
position: relative;
height: 2px;
cursor: pointer;
}
.progress-bar {
background-color: #649fec;
width: 0;
height: 2px;
}
.progress-bar-bg span {
content: " ";
width: 10px;
height: 10px;
border-radius: 50%;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
background-color: #3e87e8;
position: absolute;
left: 0;
top: 50%;
margin-top: -5px;
margin-left: -5px;
cursor: pointer;
}
.audio-time {
overflow: hidden;
margin-top: -1px;
}
.audio-length-total {
float: right;
font-size: 12px;
}
.audio-length-current {
float: left;
font-size: 12px;
}
2. 添加播放控制事件
- 獲取音頻對(duì)象
var audio = document.getElementsByTagName('audio')[0];
- 播放/暫停控制
// 點(diǎn)擊播放/暫停圖片時(shí)颅痊,控制音樂(lè)的播放與暫停
var audioPlayer = document.getElementById('audioPlayer');
audioPlayer.addEventListener('click', function () {
// 改變播放/暫停圖片
if (audio.paused) {
audio.play();
audioPlayer.src = './image/pause.png';
} else {
audio.pause();
audioPlayer.src = './image/play.png';
}
}, false);
- 更新進(jìn)度條與當(dāng)前播放時(shí)間
// 監(jiān)聽(tīng)音頻播放時(shí)間并更新進(jìn)度條
audio.addEventListener('timeupdate', function () {
updateProgress(audio);
}, false);
/**
* 更新進(jìn)度條與當(dāng)前播放時(shí)間
* @param {object} audio - audio對(duì)象
*/
function updateProgress(audio) {
var value = audio.currentTime / audio.duration;
document.getElementById('progressBar').style.width = value * 100 + '%';
document.getElementById('progressDot').style.left = value * 100 + '%';
document.getElementById('audioCurTime').innerText = transTime(audio.currentTime);
}
/**
* 音頻播放時(shí)間換算
* @param {number} value - 音頻當(dāng)前播放時(shí)間殖熟,單位秒
*/
function transTime(value) {
var time = "";
var h = parseInt(value / 3600);
value %= 3600;
var m = parseInt(value / 60);
var s = parseInt(value % 60);
if (h > 0) {
time = formatTime(h + ":" + m + ":" + s);
} else {
time = formatTime(m + ":" + s);
}
return time;
}
/**
* 格式化時(shí)間顯示,補(bǔ)零對(duì)齊
* eg:2:4 --> 02:04
* @param {string} value - 形如 h:m:s 的字符串
*/
function formatTime(value) {
var time = "";
var s = value.split(':');
var 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;
}
- 播放完成時(shí)把進(jìn)度調(diào)回開(kāi)始的位置
// 監(jiān)聽(tīng)播放完成事件
audio.addEventListener('ended', function () {
audioEnded();
}, false);
/**
* 播放完成時(shí)把進(jìn)度調(diào)回開(kāi)始的位置
*/
function audioEnded() {
document.getElementById('progressBar').style.width = 0;
document.getElementById('progressDot').style.left = 0;
document.getElementById('audioCurTime').innerText = transTime(0);
document.getElementById('audioPlayer').src = './image/play.png';
}
3. 添加進(jìn)度調(diào)節(jié)事件
- 點(diǎn)擊進(jìn)度條跳到指定位置播放
// 點(diǎn)擊進(jìn)度條跳到指定點(diǎn)播放
// PS:此處不要用click斑响,否則下面的拖動(dòng)進(jìn)度點(diǎn)事件有可能在此處觸發(fā)菱属,此時(shí)e.offsetX的值非常小,會(huì)導(dǎo)致進(jìn)度條彈回開(kāi)始處(簡(jiǎn)直不能忍=⒎!Eγ拧)
var progressBarBg = document.getElementById('progressBarBg');
progressBarBg.addEventListener('mousedown', function (event) {
// 只有音樂(lè)開(kāi)始播放后才可以調(diào)節(jié),已經(jīng)播放過(guò)但暫停了的也可以
if (!audio.paused || audio.currentTime != 0) {
var pgsWidth = parseFloat(window.getComputedStyle(progressBarBg, null).width.replace('px', ''));
var rate = event.offsetX / pgsWidth;
audio.currentTime = audio.duration * rate;
updateProgress(audio);
}
}, false);
- 拖動(dòng)進(jìn)度條到指定位置播放
/**
* 鼠標(biāo)拖動(dòng)進(jìn)度點(diǎn)時(shí)可以調(diào)節(jié)進(jìn)度
* @param {*} audio
*/
function dragProgressDotEvent(audio) {
var dot = document.getElementById('progressDot');
var position = {
oriOffestLeft: 0, // 移動(dòng)開(kāi)始時(shí)進(jìn)度條的點(diǎn)距離進(jìn)度條的偏移值
oriX: 0, // 移動(dòng)開(kāi)始時(shí)的x坐標(biāo)
maxLeft: 0, // 向左最大可拖動(dòng)距離
maxRight: 0 // 向右最大可拖動(dòng)距離
};
var flag = false; // 標(biāo)記是否拖動(dòng)開(kāi)始
// 鼠標(biāo)按下時(shí)
dot.addEventListener('mousedown', down, false);
dot.addEventListener('touchstart', down, false);
// 開(kāi)始拖動(dòng)
document.addEventListener('mousemove', move, false);
document.addEventListener('touchmove', move, false);
// 拖動(dòng)結(jié)束
document.addEventListener('mouseup', end, false);
document.addEventListener('touchend', end, false);
function down(event) {
if (!audio.paused || audio.currentTime != 0) { // 只有音樂(lè)開(kāi)始播放后才可以調(diào)節(jié)营罢,已經(jīng)播放過(guò)但暫停了的也可以
flag = true;
position.oriOffestLeft = dot.offsetLeft;
position.oriX = event.touches ? event.touches[0].clientX : event.clientX; // 要同時(shí)適配mousedown和touchstart事件
position.maxLeft = position.oriOffestLeft; // 向左最大可拖動(dòng)距離
position.maxRight = document.getElementById('progressBarBg').offsetWidth - position.oriOffestLeft; // 向右最大可拖動(dòng)距離
// 禁止默認(rèn)事件(避免鼠標(biāo)拖拽進(jìn)度點(diǎn)的時(shí)候選中文字)
if (event && event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
// 禁止事件冒泡
if (event && event.stopPropagation) {
event.stopPropagation();
} else {
window.event.cancelBubble = true;
}
}
}
function move(event) {
if (flag) {
var clientX = event.touches ? event.touches[0].clientX : event.clientX; // 要同時(shí)適配mousemove和touchmove事件
var length = clientX - position.oriX;
if (length > position.maxRight) {
length = position.maxRight;
} else if (length < -position.maxLeft) {
length = -position.maxLeft;
}
var progressBarBg = document.getElementById('progressBarBg');
var pgsWidth = parseFloat(window.getComputedStyle(progressBarBg, null).width.replace('px', ''));
var rate = (position.oriOffestLeft + length) / pgsWidth;
audio.currentTime = audio.duration * rate;
updateProgress(audio);
}
}
function end() {
flag = false;
}
}
最后總的 js
代碼如下:
document.addEventListener('DOMContentLoaded', function () {
// 設(shè)置音頻文件名顯示寬度
var element = document.querySelector('.audio-right');
var maxWidth = window.getComputedStyle(element, null).width;
document.querySelector('.audio-right p').style.maxWidth = maxWidth;
// 初始化音頻控制事件
initAudioEvent();
}, false);
function initAudioEvent() {
var audio = document.getElementsByTagName('audio')[0];
var audioPlayer = document.getElementById('audioPlayer');
// 點(diǎn)擊播放/暫停圖片時(shí)赏陵,控制音樂(lè)的播放與暫停
audioPlayer.addEventListener('click', function () {
// 監(jiān)聽(tīng)音頻播放時(shí)間并更新進(jìn)度條
audio.addEventListener('timeupdate', function () {
updateProgress(audio);
}, false);
// 監(jiān)聽(tīng)播放完成事件
audio.addEventListener('ended', function () {
audioEnded();
}, false);
// 改變播放/暫停圖片
if (audio.paused) {
// 開(kāi)始播放當(dāng)前點(diǎn)擊的音頻
audio.play();
audioPlayer.src = './image/pause.png';
} else {
audio.pause();
audioPlayer.src = './image/play.png';
}
}, false);
// 點(diǎn)擊進(jìn)度條跳到指定點(diǎn)播放
// PS:此處不要用click,否則下面的拖動(dòng)進(jìn)度點(diǎn)事件有可能在此處觸發(fā)饲漾,此時(shí)e.offsetX的值非常小蝙搔,會(huì)導(dǎo)致進(jìn)度條彈回開(kāi)始處(簡(jiǎn)直不能忍!?即)
var progressBarBg = document.getElementById('progressBarBg');
progressBarBg.addEventListener('mousedown', function (event) {
// 只有音樂(lè)開(kāi)始播放后才可以調(diào)節(jié)吃型,已經(jīng)播放過(guò)但暫停了的也可以
if (!audio.paused || audio.currentTime != 0) {
var pgsWidth = parseFloat(window.getComputedStyle(progressBarBg, null).width.replace('px', ''));
var rate = event.offsetX / pgsWidth;
audio.currentTime = audio.duration * rate;
updateProgress(audio);
}
}, false);
// 拖動(dòng)進(jìn)度點(diǎn)調(diào)節(jié)進(jìn)度
dragProgressDotEvent(audio);
}
/**
* 鼠標(biāo)拖動(dòng)進(jìn)度點(diǎn)時(shí)可以調(diào)節(jié)進(jìn)度
* @param {*} audio
*/
function dragProgressDotEvent(audio) {
var dot = document.getElementById('progressDot');
var position = {
oriOffestLeft: 0, // 移動(dòng)開(kāi)始時(shí)進(jìn)度條的點(diǎn)距離進(jìn)度條的偏移值
oriX: 0, // 移動(dòng)開(kāi)始時(shí)的x坐標(biāo)
maxLeft: 0, // 向左最大可拖動(dòng)距離
maxRight: 0 // 向右最大可拖動(dòng)距離
};
var flag = false; // 標(biāo)記是否拖動(dòng)開(kāi)始
// 鼠標(biāo)按下時(shí)
dot.addEventListener('mousedown', down, false);
dot.addEventListener('touchstart', down, false);
// 開(kāi)始拖動(dòng)
document.addEventListener('mousemove', move, false);
document.addEventListener('touchmove', move, false);
// 拖動(dòng)結(jié)束
document.addEventListener('mouseup', end, false);
document.addEventListener('touchend', end, false);
function down(event) {
if (!audio.paused || audio.currentTime != 0) { // 只有音樂(lè)開(kāi)始播放后才可以調(diào)節(jié),已經(jīng)播放過(guò)但暫停了的也可以
flag = true;
position.oriOffestLeft = dot.offsetLeft;
position.oriX = event.touches ? event.touches[0].clientX : event.clientX; // 要同時(shí)適配mousedown和touchstart事件
position.maxLeft = position.oriOffestLeft; // 向左最大可拖動(dòng)距離
position.maxRight = document.getElementById('progressBarBg').offsetWidth - position.oriOffestLeft; // 向右最大可拖動(dòng)距離
// 禁止默認(rèn)事件(避免鼠標(biāo)拖拽進(jìn)度點(diǎn)的時(shí)候選中文字)
if (event && event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
// 禁止事件冒泡
if (event && event.stopPropagation) {
event.stopPropagation();
} else {
window.event.cancelBubble = true;
}
}
}
function move(event) {
if (flag) {
var clientX = event.touches ? event.touches[0].clientX : event.clientX; // 要同時(shí)適配mousemove和touchmove事件
var length = clientX - position.oriX;
if (length > position.maxRight) {
length = position.maxRight;
} else if (length < -position.maxLeft) {
length = -position.maxLeft;
}
var progressBarBg = document.getElementById('progressBarBg');
var pgsWidth = parseFloat(window.getComputedStyle(progressBarBg, null).width.replace('px', ''));
var rate = (position.oriOffestLeft + length) / pgsWidth;
audio.currentTime = audio.duration * rate;
updateProgress(audio);
}
}
function end() {
flag = false;
}
}
/**
* 更新進(jìn)度條與當(dāng)前播放時(shí)間
* @param {object} audio - audio對(duì)象
*/
function updateProgress(audio) {
var value = audio.currentTime / audio.duration;
document.getElementById('progressBar').style.width = value * 100 + '%';
document.getElementById('progressDot').style.left = value * 100 + '%';
document.getElementById('audioCurTime').innerText = transTime(audio.currentTime);
}
/**
* 播放完成時(shí)把進(jìn)度調(diào)回開(kāi)始的位置
*/
function audioEnded() {
document.getElementById('progressBar').style.width = 0;
document.getElementById('progressDot').style.left = 0;
document.getElementById('audioCurTime').innerText = transTime(0);
document.getElementById('audioPlayer').src = './image/play.png';
}
/**
* 音頻播放時(shí)間換算
* @param {number} value - 音頻當(dāng)前播放時(shí)間僚楞,單位秒
*/
function transTime(value) {
var time = "";
var h = parseInt(value / 3600);
value %= 3600;
var m = parseInt(value / 60);
var s = parseInt(value % 60);
if (h > 0) {
time = formatTime(h + ":" + m + ":" + s);
} else {
time = formatTime(m + ":" + s);
}
return time;
}
/**
* 格式化時(shí)間顯示勤晚,補(bǔ)零對(duì)齊
* eg:2:4 --> 02:04
* @param {string} value - 形如 h:m:s 的字符串
*/
function formatTime(value) {
var time = "";
var s = value.split(':');
var 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;
}
參考:
音頻(audio)自定義樣式以及控制操作面板
HTML5中 audio標(biāo)簽的樣式修改