小程序录音及其动画

功能比较独立&&简单,大概是这样的交互

这里写图片描述

先说录音吧,看 文档

基础功能可以这样写,挺简单的~

const recorderManager = wx.getRecorderManager()
const innerAudioContext = wx.createInnerAudioContext()

Page({
  data: {
    intervalId:0,
    recording:false,
    recordtime:0,
    filePath:"",
    proc:0
  },
  onLoad: function (options) {
    recorderManager.onStart(() => {
      this.setData({ recording: true })
    })
    recorderManager.onStop((res) => {
      this.cancelRecord()
      this.setData({ 
        recording: false ,
        filePath: res.tempFilePath,
      })
    })
    recorderManager.onFrameRecorded((res) => {
      const { frameBuffer } = res
      console.log('frameBuffer.byteLength', frameBuffer.byteLength)
    })
    this.fetch();
  },
  startRecord: function () {
    recorderManager.start(this.data.options)
    this.setData({ 
      recordtime: 0 ,
      filePath: ""
    })
    this.data.intervalId = setInterval(()=>{
      var recordtime = this.data.recordtime + 50
      this.setData({ 
        recordtime: recordtime ,
        proc: recordtime / this.data.options.duration
      })
    }, 50);
    console.log(this.data.intervalId)
  },
  cancelRecord: function () {
    recorderManager.stop()
    if (this.data.intervalId > 0){
      clearInterval(this.data.intervalId)
      this.data.intervalId = 0
    }
  },
  playVoice:function(){
    innerAudioContext.src = this.data.url.length > 0 ? this.data.url : this.data.filePath
    innerAudioContext.play()
  }
})

比较难画的是圆形进度条,这里也分享下思路:

  • 先画一个半圆环A(eg:黄色)
  • 再画一个半圆环B(eg:背景色)
  • 按进度旋转第二个半圆环B
  • 进度过一半的时候,反转第二个半圆环B的颜色(eg:背景色->黄色)

细节是这样的:

把圆环一半设置为透明

background-image: linear-gradient(to left, transparent 50%, #F4F4F4 0);

旋转 && 50%变色

transform:rotate({{proc<0.5?proc:(proc+0.5)}}turn);
background-color: {{proc<0.5?'#F4F4F4;':'#FCDF6A'}}

20190409更新

下面是之前的代码

voice.js

var api = require('../../../utils/api/index.js');
var cache = require('../../../utils/api/lib/cache');
var constants = require('../../../utils/api/lib/constants.js');
const app = getApp()

const recorderManager = wx.getRecorderManager()
const innerAudioContext = wx.createInnerAudioContext()

Page({
  data: {
    status: 0,//1:没有录音 2:录音完成/已有录音
    maxTime: 10,
    recording: false,
    proc:0.0,
    uploading: false,
    filePath:"",
    url:"",
    recordtime:0,
    intervalId:0,
    options: {
      duration: 15000,
      sampleRate: 44100,
      numberOfChannels: 1,
      encodeBitRate: 192000,
      format: 'aac',
      frameSize: 50
    }
  },
  onLoad: function (options) {
    recorderManager.onStart(() => {
      this.setData({ recording: true })
    })
    recorderManager.onStop((res) => {
      this.cancelRecord()
      this.setData({ 
        recording: false ,
        filePath: res.tempFilePath,
        status:2
      })
    })
    recorderManager.onFrameRecorded((res) => {
      const { frameBuffer } = res
      console.log('frameBuffer.byteLength', frameBuffer.byteLength)
    })
    this.fetch();
  },
  onPullDownRefresh: function () {
    wx.stopPullDownRefresh();
  },
  onShareAppMessage: () => { return app.getShare() },
  fetch: function () {
    wx.showNavigationBarLoading();
    api.user.fetch({
      success: data => {
        wx.hideNavigationBarLoading()
        var user = data.user_info
        if (user != null 
          && user.u_base_info
          && user.u_base_info.voice_desc) {
          
          var voice = user.u_base_info.voice_desc;
          if (voice.url.length > 0){
            this.setData({
              url: voice.url,
              recordtime: voice.seconds*1000,
              status: 2
            })
          }else{
            this.setData({ status: 1 })
          }
        } else {
          this.setData({ status: 1 })
        }
      },
      fail: resp => {
        this.setData({ status: 1 })
        var msg = '错误:';
        if (error.code) msg += error.code;
        if (error.message) msg += error.message;
        wx.showToast({ title: msg, duration: 2000 })
      }
    })
  },
  startRecord: function () {
    recorderManager.start(this.data.options)
    this.setData({ 
      recordtime: 0 ,
      url: ""
    })
    this.data.intervalId = setInterval(()=>{
      var recordtime = this.data.recordtime + 50
      this.setData({ 
        recordtime: recordtime ,
        proc: recordtime / this.data.options.duration
      })
    }, 50);
  },
  cancelRecord: function () {
    recorderManager.stop()
    if (this.data.intervalId > 0){
      clearInterval(this.data.intervalId)
      this.data.intervalId = 0
    }
  },
  playVoice:function(){
    innerAudioContext.src = this.data.url.length > 0 ? this.data.url : this.data.filePath
    innerAudioContext.play()
  },
  tapDelete:function(){
    wx.showNavigationBarLoading()
    api.user.modify({
      data: {
        u_base_info: JSON.stringify({
          voice_desc: { url: "", seconds: 0 }
        })
      },
      success: resp => {
        wx.hideNavigationBarLoading()
        this.setData({
          status: 1,
          url: ""
        })
      }
    })
  },
  tapUpload:function(){
    var filePath = this.data.filePath;
    var fileName = filePath.match(/(wxfile:\/\/)(.+)/)
    if (!fileName) {
      fileName = filePath.match(/(http:\/\/tmp\/)(.+)/)
    }
    fileName = fileName[2]
    api.upload.uploadFn({
      data: { 
        filePath: filePath, 
        object_name: fileName, 
        file_type: constants.FILE_TYPE.VOICE 
      },
      success: data => {
        // update user info
        this.setData({ url: data.cdn_access_url})
        api.user.modify({
          data: {
            u_base_info: JSON.stringify({
              voice_desc: {
                url: data.cdn_access_url,
                seconds: parseInt(this.data.recordtime/1000)
              }
            })
          },
          success: resp => {
            wx.hideNavigationBarLoading()
            wx.showToast({ title: '保存成功', duration: 1500 })
            setTimeout(function () { wx.navigateBack(); }, 1500)
            app.event.emit("onSettingModify", {
              full: false,
              u_base_info:{
                voice_desc: {
                  url: data.cdn_access_url,
                  seconds: parseInt(this.data.recordtime / 1000)
                }
              } 
            })
          }
        })
      },
      fail: err => {
        wx.hideNavigationBarLoading()
        wx.showToast({ title: '上传失败', duration:1500 })
      },
      onProgress: info => {},
      onStartUpload: info => {
        wx.showNavigationBarLoading()
      }
    })
  }
})

voice.wxml

<view class="voice">
  <view wx:if="{{status==1}}" class="recording">
    <view wx:if="{{recording}}" class="title">录音中</view>
    <view wx:else class="title">你还没有语音</view>
    <view class="foot">  
      <view 
        class="{{recording?'btn-ing':'btn'}}" 
        bindtouchend='cancelRecord'
        bindtouchstart='startRecord'>
        <view 
          class="process"
          style="transform:rotate({{proc<0.5?proc:(proc+0.5)}}turn);background-color: {{proc<0.5?'#F4F4F4;':'#FCDF6A'}}">
        </view>
        <view class="cont">
          <image src="{{recording?'/image/v3/icon_speaking.png':'/image/v3/icon_speak.png'}}"></image>
        </view>
      </view>
      <view>{{recording?'松开结束':'按住录音'}}</view>
    </view>
  </view>
  <view wx:if="{{status==2}}" class="record-done">
    <view class="title">录音完成</view>
    <view class="play" bindtap="playVoice">
      <span>{{recordtime/1000}}”</span>
      <image src="/image/v3/voice.png"></image>
    </view>
    <view class="foot">
      <view bindtap="tapDelete" class="del">
        <image src="/image/v3/icon_speak_close.png"></image>
      </view>
      <view bindtap="tapUpload" class="done">
        <image src="/image/v3/icon_speak_done.png"></image>
      </view>
    </view>
  </view>
</view>

voice.wxss

.voice{
  text-align: center;
  color: #787878;
}
.voice .title{
  font-size: 34rpx;
  padding-top: 238rpx;
}
.voice .recording .foot{
  font-size: 30rpx;
  position: absolute;
  bottom: 224rpx;
  left: 0;
  right: 0;
}
.voice .recording .foot image{
  width: 74rpx;
  height: 104rpx;
}
.voice .recording .foot .btn{
  width: 192rpx;
  height: 192rpx;
  border-radius: 192rpx;
  box-sizing: border-box;
  background: -webkit-linear-gradient(left, #CB6BCF, #EF6790, #FC5045);
  margin: 0 auto 44rpx auto;
  padding: 6rpx;
}
.voice .recording .foot .btn .cont{
  width: 180rpx;
  height: 180rpx;
  background-color: #f4f4f4;
  border-radius: 180rpx;
  padding-top: 40rpx;
  box-sizing:border-box;
}
.voice .recording .foot .btn-ing{
  position: relative;
  width: 216rpx;
  height: 216rpx;
  border-radius: 216rpx;
  box-sizing: border-box;
  background: #FCDF6A;
  margin: 0 auto 32rpx auto;
  background-image: linear-gradient(to left, transparent 50%, #F4F4F4 0); 
}
.voice .recording .foot .btn-ing .cont{
  position: absolute;
  right: 12rpx;
  top: 12rpx;
  bottom: 12rpx;
  left: 12rpx;
  z-index: 2;
  width: 192rpx;
  height: 192rpx;
  background: -webkit-linear-gradient(left, #CB6BCF, #EF6790, #FC5045);
  border-radius: 192rpx;
  padding-top: 44rpx;
  box-sizing:border-box;
}
.voice .recording .foot .btn-ing .process{
  z-index: 1;
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 108rpx;
  border-radius: 0 100% 100% 0 / 50%;
  transform-origin:0rpx 108rpx;
}
.voice .record-done .play{
  position: relative;
  width: 248rpx;
  height: 72rpx;
  border-radius: 72rpx;
  background: -webkit-linear-gradient(left, #CB6BCF, #EF6790, #FC5045);
  color: white;
  font-size: 28rpx;
  line-height: 72rpx;
  text-align: left;
  padding-left: 60rpx;
  box-sizing: border-box;
  margin: 194rpx auto 0 auto;
}
.voice .record-done .play image{
  position: absolute;
  right: 36rpx;
  top: 10rpx;
  width: 52rpx;
  height: 52rpx;
}
.voice .record-done .foot{
  position: absolute;
  bottom: 210rpx;
  left: 0;
  right: 0;
}
.voice .record-done .foot>view{
  width: 106rpx;
  height: 106rpx;
  border-radius:106rpx;
  display:inline-block;
  box-shadow: 0rpx 5rpx 10rpx 3rpx #c2c2c2;
}
.voice .record-done .foot .del{
  background-color: #C2C2C2;
}
.voice .record-done .foot .done{
  margin-left: 136rpx;
  background: -webkit-linear-gradient(left, #CB6BCF, #EF6790, #FC5045);
}
.voice .record-done .foot .del image{
  width: 39rpx;
  height: 39rpx;
  margin-top: 34rpx;
}
.voice .record-done .foot .done image{
  width: 55rpx;
  height: 33rpx;
  margin-top: 34rpx;
}

玩~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值