背景:audio的control樣式不符合產品需求,UI設計後需要滿足需求,自定義樣式唄,還能咋地。
UI設計圖如下:
拿到需求先別慌,具體拆分功能如下
- 第一,開始播放錄音和停止播放錄音的按鈕
- 第二,audio元素
- 第三,播放音頻的進度條
- 第四,模擬錄音的實時播放時間以及音頻總時長
接下來讓我們依次來說明:
1、引入需要用到的組件以及依賴
說明:項目中UI大框架用的是antd-design(React)
import { Icon, Progress } from 'antd';
import moment from 'moment';
2、開始播放錄音和停止播放錄音的按鈕
- 提前定義變量
const [audio, setAudio] = useState({
isPausing: true,
ref: createRef(),
});
- UI實現 直接使用antd的Icon來實現按鈕樣式,根據最先定義的isPausing狀態來控制是播放還是停止錄音
使用ICON的原因,停止的caret-right居中的時候看着總是不居中,因此我設置了一個小小的padding(less.audioBtnPadding)
<div className={less.audioBtn + ' ' + (audio.isPausing ? less.audioBtnPadding : '')} onClick={() => audioControlHandle()}>
<Icon type={audio.isPausing ? 'caret-right' : 'pause'} />
</div>
- audioControlHandle實現(核心)
寫之前需要思考一下,這個函數要實現的功能是什麼:
- 1、點擊按鈕的時候需要根據轉換狀態,停止的話就打開,打開的話就停止;
- 2、獲取到audio元素,根據audio的狀態去修改,打開的話就關閉currentDom.pause(),停止的話就打開currentDom.play();
- 3、實現進度條的功能
- 4、獲取音頻的實時時間以及音頻總時長(音頻總時長錄音結束的時候就可以拿到,即應是提前就獲取到的)
const audioControlHandle = useCallback(() => {
const cAudio = _.cloneDeep(audio);
cAudio.isPausing = !cAudio.isPausing;
setAudio(cAudio);
const currentDom = cAudio.ref.current;
if (cAudio.isPausing) {
currentDom.pause();
} else {
currentDom.play();
cAudio.playTimer = setInterval(() => {
if (currentDom.paused) {
clearInterval(cAudio.playTimer);
if (Math.floor(currentDom.currentTime) >= Math.floor(currentDom.duration)) {
cAudio.isPausing = true;
setAudio(cAudio);
}
}
setCurrentTimes(moment(Math.floor(currentDom.currentTime * 1000)).format('mm:ss'));
setProgressPercent(Math.floor(((currentDom.currentTime / currentDom.duration) * 100)));
}, 300);
}
}, [setAudio, setCurrentTimes, setProgressPercent, audio]);
3、audio元素
做再多的外在裝飾,其實真正實現播放音頻的還是audio元素,所以必須有audio元素做基礎。 src表示音頻的url,ref幫助我們獲取到audio元素
<audio src={audioUrl} ref={audio.ref} />
4、播放音頻的進度條
可以直接使用antd的process組件,非常方便,設置一些屬性即可
<Progress
percent={progressPercent}
showInfo={false}
strokeWidth={7}
strokeColor="#B6B6B6"
trailColor="#F1F1F1"
/>
5、模擬audio播放時候顯示的實時時間以及音頻時長
提前定義變量
const [currentTimes, setCurrentTimes] = useState('00:00');
const [audioDuration, setAudioDuration] = useState(0);
點擊播放按鈕的時候獲取音頻實時時間格式化之後用來顯示
<div className={less.audioTime}>
{currentTimes}
/
{moment(audioDuration).format('mm:ss')}
</div>
6、說了這麼多,直接上代碼
Html:
<div className={less.recordingPlay}>
<div className={less.audio}>
<div className={less.audioBtn + ' ' + (audio.isPausing ? less.audioBtnPadding : '')} onClick={() => audioControlHandle()}>
<Icon type={audio.isPausing ? 'caret-right' : 'pause'} />
</div>
<audio src={audioUrl} ref={audio.ref} />
<Progress
percent={progressPercent}
showInfo={false}
strokeWidth={7}
strokeColor="#B6B6B6"
trailColor="#F1F1F1"
/>
</div>
<div className={less.audioTime}>
{currentTimes}
/
{moment(audioDuration).format('mm:ss')}
</div>
</div>
css(less實現):
.recordingPlay{
width: 100%;
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
.audio{
width: 715px;
height: 44px;
display: flex;
justify-content: space-between;
align-items: center;
audio{
width: 715px;
height: 44px;
}
.audioBtn{
width: 44px;
height: 44px;
border:3px solid rgba(4,32,68,1);
border-radius: 50%;
font-size: 30px;
color: rgba(4,32,68,1);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
margin-right: 23px;
}
.audioBtnPadding{
padding-left: 4px;
}
}
.audioTime{
width: 112px;
height: 22px;
font-size:16px;
font-family:PingFangSC-Medium,@fontFamily;
font-weight:500;
color:rgba(4,32,68,1);
line-height:22px;
}
.button{
width:122px;
height:36px;
background:rgba(81,135,213,1);
border-radius:2px;
border: 0px;
font-size:14px;
font-family:PingFangSC-Regular,@fontFamily;
font-weight:400;
color:rgba(255,255,255,1);
line-height:20px;
}
}
JS:
const audioControlHandle = useCallback(() => {
const cAudio = _.cloneDeep(audio);
cAudio.isPausing = !cAudio.isPausing;
setAudio(cAudio);
const currentDom = cAudio.ref.current;
if (cAudio.isPausing) {
currentDom.pause();
} else {
currentDom.play();
cAudio.playTimer = setInterval(() => {
if (currentDom.paused) {
clearInterval(cAudio.playTimer);
if (Math.floor(currentDom.currentTime) >= Math.floor(currentDom.duration)) {
cAudio.isPausing = true;
setAudio(cAudio);
}
}
setCurrentTimes(moment(Math.floor(currentDom.currentTime * 1000)).format('mm:ss'));
setProgressPercent(Math.floor(((currentDom.currentTime / currentDom.duration) * 100)));
}, 300);
}
}, [setAudio, setCurrentTimes, setProgressPercent, audio]);