要实现音频的播放如下图:
html代码如下:
<audio src="" preload="metadata" controls />
本来我以为在css3这么强大的年代,自定义一个audio的皮肤应该是完全没问题的,后来的事实证明too young too simple。
看了下audio的shadow dom结构,然后试了试用css去自定义,于是发现两个问题:
-webkit-appearance: media-play-button;
,一个样式控制两种状态,没招。 于是只好转向js来控制了,html修改如下:
<div class="audio-wrap"> <audio src="" preload="metadata" controls /> <i class="icon-play"></i> <!-- 播放/暂停按钮 通过js切换class --> <div clas="timeline"> <!-- 进度条 --> <div class="playhead"></div> </div> <div class="time-num"> <!-- 时间 --> <span class="num-current">00:00</span> / <span class="num-duration">00:00</span> </div> </div>
loadedmetadata
事件,读取音频的总时长 timeupdate
事件,用于更新播放进度 canplaythrough
事件,当播放结束,判断是否可以重播 icon-play
的点击事件,暂停或播放 timeline
的点击事件,用于跳跃播放 目前采用的es5,audio地址通过props传入,判断播放还是暂停采用state切换,进度条更新用了reactDOM操作。
var React = require('react'); var ReactDOM = React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; // 简单格式化时间,小于9的数字前面添加0 function formatTime(num) { var num = parseInt(num); if(num <= 60) { if(num < 10) { num = '0' + num; } return num; } } module.exports = React.createClass({ getInitialState: function() { return { isPlay: false // 默认暂停 } }, componentDidMount: function() { var audioNode = ReactDOM.findDOMNode(this.refs.audio), playNode = ReactDOM.findDOMNode(this.refs.play), timeline = ReactDOM.findDOMNode(this.refs.timeline), playhead = ReactDOM.findDOMNode(this.refs.playhead), timeCurrent = ReactDOM.findDOMNode(this.refs.timeCurrent), timeDuration = ReactDOM.findDOMNode(this.refs.timeDuration), timelineWidth = timeline.offsetWidth - playhead.offsetWidth, that = this, duration; // 得到初始数据 function loadedmetadata() { timeDuration = '00:'+ formatTime(audioNode.duration); timeCurrent = '00:00'; } this.loadedmetadata = loadedmetadata; // 播放进度 function timeUpdate() { var playPercent = timelineWidth * (audioNode.currentTime / duration); playhead.style.webkitTransform = "translateX("+playPercent + "px)"; playhead.style.transform = "translateX("+playPercent + "px)"; if (audioNode.currentTime == duration) { that.setState({ isPlay: false }) } timeCurrent = '00:'+ formatTime(audioNode.currentTime); } this.timeUpdate = timeUpdate; // 是否可以重新播放 function canplaythrough() { duration = audioNode.duration; } this.canplaythrough = canplaythrough; // 进度条点击 function timelineClick(e) { // 更新坐标位置 var newLeft = e.pageX - timeline.offsetLeft; if (newLeft >= 0 && newLeft <= timelineWidth) { playhead.style.transform = "translateX("+ newLeft +"px)"; } if (newLeft < 0) { playhead.style.transform = "translateX(0)"; } if (newLeft > timelineWidth) { playhead.style.transform = "translateX("+ timelineWidth + "px)"; } // 更新时间 audioNode.currentTime = duration * (e.pageX - timeline.offsetLeft) / timelineWidth; } this.timelineClick = timelineClick; // 监听事件 audioNode.addEventListener("loadedmetadata", that.loadedmetadata); audioNode.addEventListener("timeupdate", that.timeUpdate); audioNode.addEventListener("canplaythrough", that.canplaythrough); timeline.addEventListener("click", that.timelineClick); }, componentWillUnmount: function() { var audioNode = ReactDOM.findDOMNode(this.refs.audio), timeline = ReactDOM.findDOMNode(this.refs.timeline); // 注销事件 audioNode.removeEventListener("loadedmetadata", this.loadedmetadata); audioNode.removeEventListener("timeupdate", this.timeUpdate); audioNode.removeEventListener("canplaythrough", this.canplaythrough); timeline.removeEventListener("click", this.timelineClick); }, play: function(){ var audioNode = ReactDOM.findDOMNode(this.refs.audio); this.setState({ isPlay: !this.state.isPlay }) if (!this.state.isPlay) { audioNode.play(); } else { // pause music audioNode.pause(); } }, render: function() { return ( <div className="audio-wrap"> <audio ref="audio" src={this.props.audioUrl} preload="metadata" controls /> <i ref="play" className={"icon-play" + (this.state.isPlay ? " pause" : "")} onClick={this.play}></i> <div ref="timeline" className="timeline"> <div ref="playhead" className="playhead"></div> </div> <div className="time-num"> <span ref="timeCurrent" className="num-current">00:00</span> / <span ref="timeDuration" className="num-duration">00:00</span> </div> </div> ) }, propTypes: { audioUrl: React.PropTypes.string.isRequired } })