anexo
Mantenga presionado para grabar. Suelte para finalizar la grabación. Haga clic para reproducir. Haga clic nuevamente para pausar. Haga clic nuevamente para continuar con la reproducción. Muestre el efecto:
función de grabación
Función de grabación (presione el dedo para comenzar a grabar, suelte el dedo para finalizar la grabación):
Utilice la función de grabación nativa de wx para crear un nuevo wx.getRecorderManager() fuera del componente
RecorderManager | Documentación abierta de WeChat
const vedioControl = wx.getRecorderManager() //录音控制器
No busques el equivocado en esta ubicación
Primero pensamos en los eventos de prensa y lanzamiento de wx como bindtouchstart/bindtouchend respectivamente.
bindtouchstart="touchstart" bindtouchend="touchend"
//我们将 touchstart设置
touchstart: function(e:any) {
this.setData({
touchStartTime:e.timeStamp
//获取按下的时间戳
})
this.setData({
setTimeFn:setTimeout(()=>{
vedioControl.start({})
// 100ms后开始录音
this.setData({startVedio:true})
},100)
})
},
touchend: function(e:any) {
//如果touchstart 和 startend 按下时间大与100ms 就进行记录
if (e.timeStamp - this.data.touchStartTime >100){
vedioControl.stop()
// touch结束停止录音
// 监听事件 如果停止录音出发事件
vedioControl.onStop((res) => {
const ss = Math.floor(res.duration / 1000 % 60)
const mm = Math.floor(res.duration / 1000 / 60)
this.setData({
vedioTime: `${mm}:${ss>10?ss:'0'+ss}`
})
clearTimeout(this.data.setTimeFn)
this.setData({startVedio:false})
const vedioList = this.data.vedioList
// 我们将获取到的暂时的地址存储到vediolist中 duration为录音的时间
vedioList.push( {
fileName: '音频',
url: res.tempFilePath,
type:'mp4',
duration:res.duration
})
// 将新的录音添加到vedioList中
this.setData({
setTimeFn:null,
vedioList:vedioList
})
})
}else{
//如果touchstart 和 startend 按下时间小与100ms 清除掉 settimeout 不尽行任何处理
clearTimeout(this.data.setTimeFn)
this.setData({
setTimeFn:null,
startVedio:false
})
}
},
De esta forma obtenemos la dirección y el audio de la grabación, a continuación debemos reproducir el audio.
Función de reproducción de audio
Usando el reproductor de audio wx wx.createInnerAudioContext() también creamos uno nuevo fuera del componente.
InnerAudioContext | Documentación abierta de WeChat
const innerAudioContext = wx.createInnerAudioContext({
useWebAudioImplement: false,
})//音频播放器
innerAudioContext.onPause(()=>{
console.log('暂停了')
})
innerAudioContext.onPlay(()=>{
console.log('开始播放了')
})
Cuando hacemos clic en el botón reproducir, se ejecuta la función playVedio
playVedio (){
innerAudioContext.src = this.data.vedioList[this.data.vedioList.length-1].url
// 这里的url为上面的tempath
const vedio = this.data.vedioList[this.data.vedioList.length - 1].duration
const duration = this.data.vedioList[this.data.vedioList.length - 1].duration /20
//这里我设置为 每5%前进一个小格(小格变成深蓝色)
//设置 isPlayVedio变量如果 当前在播放则为true 否则是false
if(!this.data.isPlayVedio){
innerAudioContext.play()
this.setData({
isPlayVedio:true
})
//这个circle 的作用是每过 5%秒 将 用于记录的vedioProcess+5
//timeVedio作用为记录 已经播放到第几秒了
const circle = setInterval(()=>{
this.setData({
vedioProcess:this.data.vedioProcess+5,
timeVedio:this.data.timeVedio + duration
})
if(vedio<this.data.timeVedio||vedio===this.data.timeVedio){
//如果 播放的长度已经大于或者等于 音频长度 我们则应该清除 这个播放让她回归于0 并且将播放状态设置为 false
clearInterval(circle)
this.setData({
vedioProcess:0,
timeVedio:0,
isPlayVedio:false
})
}
// 如果在执行 setInterval的时候 用户点击了暂停 那么isPlayVedio的状态为false 我们就需要让 这个播放器的 process不再前进
if(!this.data.isPlayVedio){
clearInterval(circle)
}
},duration)
}else{
//如果当前播放器正在播放 状态 那么用户点击的时候就是希望暂停
innerAudioContext.pause()
this.setData({
isPlayVedio:false
})
}
},
La URL aquí es la tempath de arriba.
Aquí lo configuré para avanzar una cuadrícula pequeña cada 5% (la cuadrícula pequeña se vuelve azul oscuro)
isPlayVedio: se utiliza para determinar si se está reproduciendo
vedioProcess: El progreso de la reproducción es 100
timeVedio: el tiempo de reproducción actual
círculo: renderiza una pequeña cuadrícula cada 5% del tiempo
Acerca de mostrar cuadrículas pequeñas:
<view class="vedio-file" bindtap="playVedio">
<image src="{
{isPlayVedio?'../images/vedio-start.svg':'../images/vedio-end.svg'}}"></image>
<view class="item-list">
<view wx:for="{
{20}}" wx:for-item="item" class="item {
{vedioProcess>item*5?'active':''}}" wx:key="index"></view>
</view>
<view class="time">{
{vedioTime}}</view>
</view>
Dibuja una pequeña cuadrícula vertical y ciclala 20 veces. Lo que configuramos aquí es 100/20 y cambiamos el color cada 5%.
vedioProces es el progreso de reproducción actual, todos los cuales son 100
Si el índice de la cuadrícula pequeña*5>vedioProces, entonces la cuadrícula pequeña debería volverse azul oscuro (cambie el nombre de la clase)
A continuación necesitamos configurar pequeñas rejillas de diferentes longitudes.
Utilice la función n-ésima (n) de CSS para agrupar 6 pequeño->grande grande->pequeño
.item{
height: 12px;
width: 2px;
background-color: #3370FF33;
margin-left: 5px;
}
.active{
background-color: #3370FF;
}
.item:nth-child(6n-5),.item:nth-child(6n-1){
height: 4px;
}
.item:nth-child(6n-4), .item:nth-child(6n-2){
height: 8px;
}
.item:nth-child(6n-3){
height: 14px;
}
prestar atención:
No hay sonido cuando el reproductor reproduce en formato de silencio de ios. Necesitamos configurar obedientMuteSwitch:false globalmente en app.ts.
onLaunch() {
wx.setInnerAudioOption({
obeyMuteSwitch:false
})
},
wx.setInnerAudioOption(Objeto objeto) | Documento abierto de WeChat
Código fuente del componente de versión pura sin comentarios:
wxml:
<van-overlay show="{
{ vedioVisible }}" z-index="99">
<view class="vedioDialog">
<view class="close-icon" bindtap="closeVedio">
<image src="../images/close-icon.svg"></image>
</view>
<view class="bigCircle" bindtouchstart="touchstart" bindtouchend="touchend">
<view class="smallCircle {
{startVedio?'onVedio':''}}">
<text>{
{startVedio?'正在录音':'长按录音'}}</text>
</view>
</view>
<view>
<view class="vedio-player" style="{
{vedioTime==='0:00'?'display:none':null}}">
<view class="vedio-file" bindtap="playVedio">
<image src="{
{isPlayVedio?'../images/vedio-start.svg':'../images/vedio-end.svg'}}"></image>
<view class="item-list">
<view wx:for="{
{20}}" wx:for-item="item" class="item {
{vedioProcess>item*5?'active':''}}" wx:key="index"></view>
</view>
<view class="time">{
{vedioTime}}</view>
</view>
<view class="del-vedio" bindtap="delVedio">
<image src="../images/bg-close-icon.svg"></image>
</view>
</view>
</view>
</view>
</van-overlay>
ts
// components/multifunctionInput/multifunctionInput.ts
const vedioControl = wx.getRecorderManager() //录音
const innerAudioContext = wx.createInnerAudioContext({
useWebAudioImplement: false,
})//音频播放器
innerAudioContext.onPause(()=>{
console.log('暂停了')
})
innerAudioContext.onPlay(()=>{
console.log('开始播放了')
})
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
vedioVisible:false,
vedioProcess:0,
vedioTime:'0:00',
touchStartTime: 0,
startVedio: false,
setTimeFn:null as any,
vedioList:[{
url:'https://img.yzcdn.cn/vant/leaf.jpg',
fileName:'音频.mp4',
type: 'mp4',
duration:4000
}] ,
isPlayVedio:false,
timeVedio:0
},
/**
* 组件的方法列表
*/
methods: {
openVedio(){
this.setData({vedioVisible:true})
},
closeVedio(){
this.setData({vedioVisible:false})
},
touchstart: function(e:any) {
this.setData({
touchStartTime:e.timeStamp
})
this.setData({
setTimeFn:setTimeout(()=>{
vedioControl.start({})
this.setData({startVedio:true})
},100)
})
},
//touch end
touchend: function(e:any) {
if (e.timeStamp - this.data.touchStartTime >100){
vedioControl.stop()
vedioControl.onStop((res) => {
const ss = Math.floor(res.duration / 1000 % 60)
const mm = Math.floor(res.duration / 1000 / 60)
this.setData({
vedioTime: `${mm}:${ss>10?ss:'0'+ss}`
})
clearTimeout(this.data.setTimeFn)
this.setData({startVedio:false})
const vedioList = this.data.vedioList
vedioList.push( {
fileName: '音频',
url: res.tempFilePath,
type:'mp4',
duration:res.duration
})
this.setData({
setTimeFn:null,
vedioList:vedioList
})
})
}else{
clearTimeout(this.data.setTimeFn)
this.setData({
setTimeFn:null,
startVedio:false
})
}
},
playVedio (){
innerAudioContext.src = this.data.vedioList[this.data.vedioList.length-1].url
const vedio = this.data.vedioList[this.data.vedioList.length - 1].duration
const duration = this.data.vedioList[this.data.vedioList.length - 1].duration /20
if(!this.data.isPlayVedio){
innerAudioContext.play()
this.setData({
isPlayVedio:true
})
const circle = setInterval(()=>{
this.setData({
vedioProcess:this.data.vedioProcess+5,
timeVedio:this.data.timeVedio + duration
})
if(vedio<this.data.timeVedio){
clearInterval(circle)
this.setData({
vedioProcess:0,
timeVedio:0,
isPlayVedio:false
})
}
if(!this.data.isPlayVedio){
clearInterval(circle)
}
},duration)
}else{
innerAudioContext.pause()
this.setData({
isPlayVedio:false
})
}
// innerAudioContext.pause() // 暂停
// innerAudioContext.stop() // 停止
},
delVedio (){
this.setData({
vedioTime:'0:00',
isPlayVedio:false,
vedioProcess:0,
timeVedio:0,
})
},
removeVedio (e:any) {
const index = e.target.dataset.index
const {vedioList = []} = this.data
vedioList.splice(index,1)
this.setData({
vedioList:vedioList
})
},
}
})
menos
.vedioDialog{
height: 350px;
width: 90%;
background-color: white;
box-shadow: 0px 0px 4px 6px rgba(91, 202, 151, 0.03);
border-radius: 7px;
position: absolute;
top: calc(50% - 175px);
right: 5%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.close-icon{
position: absolute;
top: 20px;
right: 20px;
image{
height: 30rpx;
width: 30rpx;
}
}
.bigCircle{
height: 166px;
width: 166px;
border-radius: 100%;
background-color: #3370FF2E;
display: flex;
align-items: center;
justify-content: center;
color: white;
.smallCircle{
width: 100px;
height: 100px;
background-color: #3370FF;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
}
}
.onVedio{
background-color: #04D7B9 !important;
}
.vedio-player{
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
.vedio-file{
width: 250px;
height: 44px;
background: #F5F5F5;
border-radius: 34.5px;
display: flex;
align-items: center;
padding:0 10px;
box-sizing: border-box;
justify-content:space-evenly;
image{
height: 33px;
width: 33px;
}
.item-list{
width: 150px;
height: 15px;
display: flex;
align-items: center;
}
.time{
color: #0D296E;
font-size: 12px;
}
.item{
height: 12px;
width: 2px;
background-color: #3370FF33;
margin-left: 5px;
}
.active{
background-color: #3370FF;
}
.item:nth-child(6n-5),.item:nth-child(6n-1){
height: 4px;
}
.item:nth-child(6n-4), .item:nth-child(6n-2){
height: 8px;
}
.item:nth-child(6n-3){
height: 14px;
}
}
.del-vedio{
margin-left: 15px;
image{
height: 25px;
width: 25px;
}
}
}
}
json
{
"component": true,
"usingComponents": {
"van-overlay": "@vant/weapp/overlay/index",
}
}