123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- <template>
- <view class="jsfun-record" @tap="showPicker">
- <slot></slot>
- <!-- 遮罩层 -->
- <view class="mask" @tap.stop="closePicker" v-if="isShow" @touchmove.stop.prevent="moveHandle"></view>
- <!-- 多选控件 -->
- <view class="conbox record" :class="{'pickerShow':isShow}">
- <!-- 此处可放置倒计时,可根据需要自行添加 -->
- <view class="time">
- {{showRecordTime}}
- </view>
- <view class="c999">
- 最短{{minTime}}秒,最长{{maxTime}}秒
- </view>
- <view class="record-box" @touchstart="start" @longpress="record" @touchend="end"
- @touchmove.stop.prevent="moveHandle">
- <span class="stop" @touchstart.stop="stopVoice" v-if="voicePath && playing==1"></span>
- <span class="paly" @touchstart.stop="playVoice" v-if="voicePath && playing==0"></span>
- <canvas class="canvas" canvas-id="canvas">
- <span class="recording"></span>
- </canvas>
- <span class="confirm" @touchstart.stop="okClick" v-if="voicePath"></span>
- </view>
- <view class="c666 fz32 domess">长按录音</view>
- </view>
- </view>
- </template>
- <script>
- const recorderManager = uni.getRecorderManager(); //录音
- var innerAudioContext; //播放
- export default {
- name: 'jsfun-record',
- props: {
- maxTime: { // 录音最大时长,单位秒
- type: Number,
- default: 15
- },
- minTime: { // 录音最大时长,单位毫秒
- type: Number,
- default: 5
- },
- },
- data() {
- return {
- isShow: false,
- frame: 50, // 执行绘画的频率,单位毫秒
- recordTime: 0, //录音时长
- recordTime1: 0, //播放录音倒计时
- isshowyuan: false, //是否显示圆圈
- playing: 0, //是否播放中
- timeObj: null, //计时id
- drawObj: null, //画布动画id
- countdownObj: null, //倒计时id
- voicePath: '',
- ctx: ''
- }
- },
- computed: {
- showRecordTime() {
- var strs = "";
- var m = Math.floor(this.recordTime / 60);
- if (m < 10) strs = "0" + m;
- var s = this.recordTime % 60;
- strs += (s < 10) ? ":0" + s : ":" + s;
- return strs
- },
-
- },
- watch: {},
- onLoad() {
- var _this = this;
- //获取录音权限
- uni.authorize({
- scope: 'scope.record',
- success() {}
- })
- //初始化
- _this.initValue();
- },
- mounted() {
- innerAudioContext = uni.createInnerAudioContext(); //播放
- let _this = this;
- //录音停止事件
- recorderManager.onStop(function(res) {
- console.log('recorder stop' + res.tempFilePath);
- _this.voicePath = res.tempFilePath;
- });
- //根据canvas 动态中心点
- let query = uni.createSelectorQuery().in(this);
- query.select(".canvas").boundingClientRect()
- query.exec(function(res) {
- _this.tempw = res[0].width; //使用canvas的宽度计算中心点的位置
- _this.temph = res[0].height;
- })
- //根据中心图片的大小计算圆环的大小
- query.select(".recording").boundingClientRect()
- query.exec(function(res) {
- _this.tempw1 = res[0].width; //使用点按图形的宽度计算圆环的宽高
- })
- },
- beforeDestroy() {
- innerAudioContext.destroy();
- },
- methods: {
- moveHandle() {
- return false;
- },
- //组件数据初始化 进入时、关闭时调用初始化
- initValue() {
- this.recordTime = 0
- },
- //显示组件
- showPicker() {
- setTimeout(() => {
- this.isShow = true;
- // this.$emit('show');
- }, 100);
- },
- //关闭组件
- closePicker() {
- this.isShow = false;
- //点遮罩 点取消关闭说明用户不想修改,所以这里对数据进行初始化
- this.initValue();
- this.stopVoice();
- },
- //点击确定
- okClick() {
- // var data = {},list = {},textStr = "",indexStr = "";
- this.$emit('okClick', this.voicePath)
- this.$emit('start', 2)
- //确定后更新默认初始值,这样再次进入默认初值就是最后选择的
- // this.defaultArr = textStr;
- //关闭
- this.closePicker();
- },
- start() {
- console.log('start')
- this.stopVoice();
- this.voicePath = ""; //音频地址
- this.recordTime = 0;
- //生成canvas对象
- this.ctx = uni.createCanvasContext('canvas',this);
- },
- end() {
- console.log('end')
- let recordTime = this.recordTime;
- this.recordTime1 = this.recordTime;
- clearInterval(this.timeObj); //清除计时器
- clearInterval(this.drawObj); //清除画布动画
- // this.recordTime = 0; //清除计时
- this.isshowyuan = false; //隐藏圆圈
- //清除canvas内容 方式一:不知道为啥 不起作用
- //this.ctx.clearRect(0,0,this.ctx.width,this.ctx.height);
- //清除canvas内容 方式二:填充canvas为白色
- this.ctx.setFillStyle('#fff')
- this.ctx.fillRect(0, 0, this.ctx.width, this.ctx.height)
- this.ctx.draw()
-
- if (recordTime < this.minTime) {
- if (recordTime <= 0) {
- //==点击事件==;
- return false;
- }
- //==小于5秒==;
- uni.showToast({
- title: "不能小于" + this.minTime + "秒,请重新录制",
- icon: "none"
- })
- return false;
- }
- recorderManager.stop();
- },
- record: function() {
- console.log('record')
- let _this = this;
- _this.isshowyuan = true
- // 开始录音
- recorderManager.start({
- format: "mp3"
- });
- _this.timeObj = setInterval(function() {
- _this.recordTime++;
- if (_this.recordTime == _this.maxTime) _this.end();
- }, 1000);
- //中心点坐标 这里如果直接除2发现位置有偏差,目前还没明白为什么要减1
- let pianyi = 0
- switch (uni.getSystemInfoSync().platform) {
- case 'android':
- pianyi = 0;
- break;
- case 'ios':
- pianyi = 1;
- break;
- default:
- pianyi = 1;
- break;
- }
- // #ifdef APP-PLUS
- let centerX = _this.tempw / 2 + pianyi;
- let centerY = _this.temph / 2 + pianyi;
- // #endif
- // #ifdef MP-WEIXIN
- let centerX = _this.tempw / 2 + pianyi-1;
- let centerY = _this.temph / 2 + pianyi-1;
- // #endif
- let yuanhuanW = _this.tempw1 / 2 -6; //圆环的半径 中间图片的宽度/2 + 4
- // 录音过程圆圈动画的背景园
- _this.ctx.beginPath();
- _this.ctx.setStrokeStyle("#1789FD");
- _this.ctx.setGlobalAlpha(0.3)
- _this.ctx.setLineWidth(3);
- _this.ctx.arc(centerX, centerY, yuanhuanW, 0, 2 * Math.PI);
- _this.ctx.stroke();
- _this.ctx.draw();
- // 录音过程圆圈动画
- let angle = -0.5;
- _this.drawObj = setInterval(function() {
- _this.ctx.beginPath();
- _this.ctx.setStrokeStyle("#1789FD");
- _this.ctx.setGlobalAlpha(1)
- _this.ctx.setLineWidth(3);
- _this.ctx.arc(centerX, centerY, yuanhuanW, -0.5 * Math.PI, (angle += 2 / (_this
- .maxTime * 1000 / _this.frame)) * Math.PI, false);
- _this.ctx.stroke();
- _this.ctx.draw(true);
- }, _this.frame);
- },
- playVoice() {
- if (this.voicePath && this.playing === 0) {
- innerAudioContext.src = this.voicePath;
- innerAudioContext.stop(); //todo 第一次play时若不先stop则播放不出来,未知原因
- innerAudioContext.play();
- this.playing = 1;
- this.recordTime = this.recordTime1;
- this.countdownObj = setInterval(() => {
- this.recordTime--;
- if (this.recordTime === 0) {
- this.stopVoice()
- return;
- }
- }, 1000)
- }
- },
- stopVoice() {
- innerAudioContext.stop();
- this.playing = 0;
- this.recordTime = 0;
- clearInterval(this.countdownObj);
- },
- }
- }
- </script>
- <style lang="scss">
- .jsfun-record {
- .mask {
- position: fixed;
- z-index: 1000;
- top: 0;
- right: 0;
- left: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.6);
- }
- .conbox {
- transition: all .3s ease;
- transform: translateY(100%);
- &.pickerShow {
- transform: translateY(0);
- }
- position: fixed;
- z-index: 1000;
- right: 0;
- left: 0;
- bottom: 0;
- background: #fff;
- }
- .c666 {
- color: #666;
- }
- .c999 {
- color: #999;
- }
- .fz28 {
- font-size: 28upx;
- }
- .fz32 {
- font-size: 32upx;
- }
- .record {
- text-align: center;
- .time {
- text-align: center;
- font-size: 60upx;
- color: #000;
- line-height: 100upx;
- margin-top: 50upx;
- }
- .domess {
- margin-bottom: 50upx;
- }
- .record-box {
- display: flex;
- flex-direction: row;
- justify-content: center;
- }
- canvas {
- margin: 10upx 60upx;
- position: relative;
- width: 200upx;
- height: 200upx;
- z-index: 10;
- .recording {
- position: absolute;
- top: 20upx;
- left: 20upx;
- width: 160upx;
- height: 160upx;
- border: 1px dashed #1789FD;
- border-radius: 100upx;
- background: #1789FD url(../../static/jsfun-record/recording.png) no-repeat 50% 50%;
- background-size: 50% 50%;
- z-index: 100;
- }
- }
- .btncom {
- margin-top: 70upx;
- width: 80upx;
- height: 80upx;
- border-radius: 80upx;
- }
- .stop {
- @extend .btncom;
- background: #1789FD url(../../static/jsfun-record/stop.png) no-repeat;
- background-size: 100% 100%;
- }
- .paly {
- @extend .btncom;
- background: #1789FD url(../../static/jsfun-record/play.png) no-repeat;
- background-size: 100% 100%;
- }
- .confirm {
- @extend .btncom;
- background: url(../../static/jsfun-record/confirm.png) no-repeat 100% 100%;
- background-size: 100% 100%;
- }
- }
- }
- </style>
|