效果如↓↓↓        假装有声音。



很郁闷,没有做到完全解耦,试了试音频播放组件<audio></audio>与API wx.createInnerAudioContext()
在自定义控件中没有作用!


也就是说,不能只传一个语音的url给自定义的控件了!播放的控制还得放在页面中,控制播放、暂停、等标识数据都得传,说是自定义控件,其实也就是一个UI罢了,与安卓IOS的继承再封装没法比,与小程序中引用片段
<template name="×××"></template> 相比 还没有人家简单。

个人认为:小程序的自定义组件是没有灵魂的,与引用片段无本质区别,当然,这是我乱说的,欢迎大神指导...

然后上代码吧:希望能抛砖引玉

1.在合适的目录右键新建component 自定义控件voice-view

wxml:
<!-- 仿微信语音播放 --> <view class='voices'> <view class='voice'
hover-class='voice-hover' bindtap='playVoice' data-index='{{index}}'
style="width:{{100+220*length/10}}rpx"> <image
src="../images/{{playing?'stop':'play'}}.png" style='margin-left:22rpx;'
></image> <image src='../images/voice{{voiceImg}}.png'
style='margin-right:10rpx;'></image> </view> <text
style='font-size:24rpx;font-weight:400;color:rgba(155,155,155,1);line-height:34rpx;margin-left:16rpx'>{{length}}s</text>
</view>
wxss:
/* components/voice-view.wxss */ .voices { padding: 10rpx 0; display: flex;
flex-direction: row; align-items: center; } .voices .voice { width: 320rpx;
height: 40rpx; background: rgba(35, 69, 156, 1); border-radius: 20rpx; display:
flex; flex-direction: row; align-items: center; justify-content: space-between;
} .voices .voice-hover{ background: rgb(28, 54, 121); } .voices .voice image {
width: 26rpx; height:26rpx; }
js:
// components/voice-view.js Component({ /** * 组件的属性列表 */ properties: { index:
{ type: Number, value: 0 }, length: { type: Number, value: 10, }, url: { type:
String, value: '' }, playing: { type: Boolean, value: false, observer:
"playAnima" // observer 表示属性值被更改时的响应函数 } }, /** * 组件的初始数据 */ data: { voiceImg:
3, }, /** * 组件的方法列表 */ methods: { //伪动画播放方法 playAnima: function() { var that =
this if (!that.data.playing) { this.setData({ voiceImg: 3 }) } else { switch
(that.data.voiceImg) { case 1: that.setData({ voiceImg: 2 }) break case 2:
that.setData({ voiceImg: 3 }) break case 3: that.setData({ voiceImg: 1 }) break
} setTimeout(function() { that.playAnima() }, 500) } }, //播放、暂停音频 playVoice:
function(e) { this.triggerEvent('voiceClickss', { index: this.data.index,
url:this.data.url, playing:this.data.playing }); }, }, })
2.page引用:要引用的先加声明:page页面的json中:
{ "usingComponents": { "voice-view":"../../components/voice-view" },
"navigationBarTitleText": "test" }
 布局wxml:

注意:url、length、等都是自定义控件中properties的属性。
<!--pages/test/test.wxml--> <view wx:for="{{list}}"> <voice-view
url="{{item.url}}" length="{{item.length}}" playing="{{item.playing}}"
index="{{index}}" bind:voiceClickss='voiceClick' /> </view>
bind:voiceClickss='voiceClick' 
的意思是:绑定自定义控件中“voiceClickss”方法至page页面中“voiceClick”方法

当点击自定义控件时,执行控件中:
    playVoice: function(e) {
      this.triggerEvent('voiceClickss', {
        index: this.data.index,
        url:this.data.url,
        playing:this.data.playing
      });
    },

将执行page页面中方法,并传值index...

js:
// pages/test/test.js var playingSrc = '' var playingIndex = 0 const
innerAudioContext = wx.createInnerAudioContext() Page({ data: { list: [{ url:
'https://fanyi.baidu.com/gettts?lan=en&text=%20pop%2Fdance%2Fclassical%2Fchurch%20music%20&spd=3&source=web',
length: 3, playing: false }, { url:
'https://fanyi.baidu.com/gettts?lan=en&text=She%20could%20hear%20music%20playing%20somewhere.&spd=3&source=web',
length: 4, playing: false }, { url:
'https://fanyi.baidu.com/gettts?lan=en&text=the%20popularity%20of%20Mozart%27s%20music&spd=3&source=web',
length: 8, playing: false }, ], }, //播放、暂停音频 voiceClick: function(e) {
console.log('page,-----voiceClick',) var index = e.detail.index playingIndex =
index var playing = e.detail.playing var src = this.data.list[index].url
console.log('播放url', src) if (playingSrc != src) { innerAudioContext.stop()
innerAudioContext.src = src playingSrc = src innerAudioContext.play() } else if
(!playing) { console.log('暂停中---->播放') innerAudioContext.play() } else {
console.log('播放中---->暂停') innerAudioContext.pause() } }, /** * 生命周期函数--监听页面加载
*/ onLoad: function(options) { var that = this innerAudioContext.autoplay =
false innerAudioContext.obeyMuteSwitch = false
innerAudioContext.onPlay(function() { that.setStatus(true) })
innerAudioContext.onPause(function() { that.setStatus() })
innerAudioContext.onStop(function() { that.setStatus() })
innerAudioContext.onEnded(function() { that.setStatus() })
innerAudioContext.onError(function(res) { that.setStatus() var t = '未知错误'
switch (res.errCode) { case 10001: t = '系统错误' break; case 10002: t = '网络错误'
break; case 10003: t = '文件错误' break; case 10004: t = '格式错误' break; }
wx.showToast({ title: t, icon: 'none' }) }) }, setStatus: function(sta = false)
{ for (var i = 0; i < this.data.list.length; i++) { this.data.list[i].playing =
false } if (sta) this.data.list[playingIndex].playing = true this.setData({
list: this.data.list }) } })
开发中发现微信自带的判断音频播放是否暂停状态的方法不好用!无奈只好另写了个变量播放状态playing 

wx文档: boolean paused  当前是是否暂停或停止状态(只读)

哦,顺便把图片资源文件给大家:
voice3.pngvoice2.pngvoice1.png
​​​​​​stop.png
 play.png