背景: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]);