react+ts比较严格,一般的ref会一直提示没有这个属性,一定都要声明后才能使用
标签上的ref要用大括号的形式:
<audio ref={
this.audio} src={
url} controls>
你的浏览器不支持音频播放哦!(;´༎ຶД༎ຶ`)
</audio>
在class里面的最外侧定义audio的类型
audio: React.RefObject<any>;
然后在constructor这个生命周期中创建ref句柄
this.audio = React.createRef();
打印一下this.audio:
componentDidMount() {
console.log(this.audio);
}
会发现有一个current的属性,所有原生js的方法和属性全都在这个属性里面,所以我们要进行原生的js操作要加上这个属性,否则会为空:
this.audio.current.play();
html结构:进度条外框outer包裹着进度条内核inner
<div
className="outer"
ref={
this.outer}
onTouchMove={
e => this.audioX(e)}
onTouchStart={
e => this.audioX(e)}
>
<div className="inner" ref={
this.inner}>
<div className="innerBtn"></div>
</div>
</div>
css样式如下:
.outer{
width: 500px;
height:10px;
background-color: black;
border-radius: 5px;
}
.inner{
position: relative;
width: 0;
height:10px;
background-color: red;
border-radius: 5px;
}
.innerBtn{
position: absolute;
width: 20px;
height: 20px;
top:-5px;
right: -5px;
border-radius: 50%;
background: blue;
}
然后使用audio标签自带的方法和属性
this.audio.current.duration音频总时长
this.audio.current.currentTime当前播放到的时长
this.audio.current.playbackRate音频播放速度
this.audio.current.play();开始播放
this.audio.current.pause();暂停
this.audio.current.οncanplay=()=>{ } 当音频可以播放时执行
this.audio.current.οnwaiting=()=>{ } 当音频在缓冲时执行
this.audio.current.οntimeupdate=()=>{ } 当播放时间变动时(播放中)执行
handleAudio() {
let audio = this.audio.current;
// timeupdate,播放位置改变时触发
audio.addEventListener("timeupdate", () => {
console.log("播放位置改变");
});
// canplay,表示音频可以播放了,准备就绪
audio.addEventListener("canplay", () => {
console.log("可以播放了");
});
// progress,音频在加载或缓冲
audio.addEventListener("progress", () => {
console.log("音频在加载或缓冲");
});
// waiting,需要缓冲下一帧而停止播放时触发
audio.addEventListener("waiting", () => {
console.log("需要缓冲下一帧");
});
// playing,音频或视频已开始播放时触发
audio.addEventListener("playing", () => {
console.log("开始播放");
});
// pause,音频或视频文件暂停时触发
audio.addEventListener("pause", () => {
console.log("已经暂停");
});
}
audio的duration和currentTime显示的是总秒数,需要我们自己转换成常规的时间格式。获取分钟的时间是总秒数除以60并对结果向下取整,获取秒数的时间是总秒数取余60并对结果向下取整
(Math.floor(this.audio.current.currentTime / 60) + "").padStart(2,"0")+":"+(Math.floor(this.audio.current.currentTime % 60) +"").padStart(2, "0");
padStart是字符串的补全方法,当位数不足两位数时就在字符串的开头补上第二个参数"0",之所以对结果加一个空的字符串也是运用"+“运算符有隐式类型转换(”+"只要有一侧是字符串,则两边都转换为字符串)将结果转换为字符串才能调用padStart
当前时长除以总时长乘100得到进度条的百分比,然后把这个结果给到内核的宽
this.inner.current.style.width =
(this.audio.current.currentTime / this.audio.current.duration) * 100 +"%";
这要用到事件对象event,用鼠标到可视区左侧的距离减去父元素到可视区左侧的距离,这个距离就是内核的宽了
var innerWidth =e.targetTouches[0].clientX-this.outer.current.getBoundingClientRect().left;
内核的宽除以外框的宽,再用这个结果乘以总时长就得到了当前的播放时长
this.audio.current.currentTime=(innerWidth / outerWidth) * this.audio.current.duration;
import React, {
Component } from "react";
import "./pack.css";
interface state {
url: string;
current: string;
allTime: string;
}
interface Props {
}
class Audio extends Component<any, state, Props> {
outer: React.RefObject<any>;
inner: React.RefObject<any>;
audio: React.RefObject<any>;
constructor(props: Props) {
super(props);
this.state = {
url: "https://music.163.com/song/media/outer/url?id=1349292048.mp3", //歌曲地址
current: "00:00", //当前时间
allTime: "00:00", //当前歌曲时长
};
this.outer = React.createRef(); //自定义进度条外框
this.inner = React.createRef(); //自定义进度条内核
this.audio = React.createRef();
this.audioX = this.audioX.bind(this);
this.canplay = this.canplay.bind(this);
this.timeUpdate = this.timeUpdate.bind(this);
this.audioPlayBackRate = this.audioPlayBackRate.bind(this);
this.audioPlay = this.audioPlay.bind(this);
this.audioStop = this.audioStop.bind(this);
this.audioGo = this.audioGo.bind(this);
this.audioBack = this.audioBack.bind(this);
this.audioWaiting = this.audioWaiting.bind(this);
this.handleAudio = this.handleAudio.bind(this);
}
componentDidMount() {
// console.log(this.audio);
this.canplay();
this.timeUpdate();
this.handleAudio();
}
//在音频可以播放时就显示总时长
canplay() {
this.audio.current.oncanplay = () => {
var allTime =
(Math.floor(this.audio.current.duration / 60) + "").padStart(2, "0") +
":" +
(Math.floor(this.audio.current.duration % 60) + "").padStart(2, "0");
this.setState({
allTime
});
};
}
audioWaiting() {
this.audio.current.onwaiting = () => {
alert("加载中");
};
}
//在音频播放时当前时间也跟着变化
timeUpdate() {
this.audio.current.ontimeupdate = () => {
// console.log(this.audio.current.currentTime)
// console.log(this.audio.current.duration)
var currentTime =
(Math.floor(this.audio.current.currentTime / 60) + "").padStart(
2,
"0"
) +
":" +
(Math.floor(this.audio.current.currentTime % 60) + "").padStart(2, "0");
this.setState({
current: currentTime
});
this.inner.current.style.width =
(this.audio.current.currentTime / this.audio.current.duration) * 100 +
"%";
};
}
//播放速度
audioPlayBackRate(str: string) {
this.audio.current.playbackRate = str;
}
//播放开始
audioPlay() {
this.audio.current.play();
}
//播放暂停
audioStop() {
this.audio.current.pause();
}
//快进
audioGo() {
this.audio.current.currentTime += 20;
}
//快退
audioBack() {
this.audio.current.currentTime -= 20;
}
//设置播放进度
audioX(e: any) {
console.log(e.targetTouches[0].clientX);
var outerWidth = this.outer.current.offsetWidth; //父元素宽
var innerWidth =
e.targetTouches[0].clientX -
this.outer.current.getBoundingClientRect().left; //鼠标到可视区左侧的距离-父元素到可视区左侧的距离
if (innerWidth > outerWidth || innerWidth < 0) {
return;
}
this.inner.current.style.width = innerWidth + "px";
this.audio.current.currentTime =
(innerWidth / outerWidth) * this.audio.current.duration;
}
handleAudio() {
let audio = this.audio.current;
// timeupdate,播放位置改变时触发
audio.addEventListener("timeupdate", () => {
console.log("播放位置改变");
});
// canplay,表示音频可以播放了,准备就绪
audio.addEventListener("canplay", () => {
console.log("可以播放了");
});
// progress,音频在加载或缓冲
audio.addEventListener("progress", () => {
console.log("音频在加载或缓冲");
});
// waiting,需要缓冲下一帧而停止播放时触发
audio.addEventListener("waiting", () => {
console.log("需要缓冲下一帧");
});
// playing,音频或视频已开始播放时触发
audio.addEventListener("playing", () => {
console.log("开始播放");
});
// pause,音频或视频文件暂停时触发
audio.addEventListener("pause", () => {
console.log("已经暂停");
});
}
render() {
const {
url, current, allTime } = this.state;
return (
<div>
<h1>onTouchMove</h1>
<h1>当前时间:{
current}</h1>
<h1>总时间:{
allTime}</h1>
<div
className="outer"
ref={
this.outer}
onTouchMove={
e => this.audioX(e)}
onTouchStart={
e => this.audioX(e)}
>
<div className="inner" ref={
this.inner}>
<div className="innerBtn"></div>
</div>
</div>
<br />
<br />
<br />
<audio ref={
this.audio} src={
url} controls>
你的浏览器不支持音频播放哦!(;´༎ຶД༎ຶ`)
</audio>
<button onClick={
() => this.audioPlay()}>播放</button>
<button onClick={
() => this.audioStop()}>暂停</button>
<button onClick={
() => this.audioGo()}>快进</button>
<button onClick={
() => this.audioBack()}>快退</button>
<button onClick={
() => this.audioPlayBackRate("4.0")}>
播放速度4.0
</button>
</div>
);
}
}
export default Audio;