<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="multipart/form-data; charset=iso-8859-1">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<script src="js/jquery-3.2.1.min.js"></script>
<script src="js/recorder.mp3.min.js"></script><!--插件地址:https://github.com/xiangyuecn/Recorder-->
<script>
//rem
(function(doc, win) {
var docEl = doc.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function() {
var clientWidth = docEl.clientWidth;
if(!clientWidth) return;
if(clientWidth >= 750) {
docEl.style.fontSize = '100px';
} else {
docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
}
};
if(!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
</script>
<style>
*{margin: 0;padding: 0;}
input{outline: none;}
.press_box{
position: fixed;
top: 50%;
left: 50%;
transform:translate(-50%,-50%);
-webkit-transform:translate(-50%,-50%);
-moz-transform:translate(-50%,-50%);
-ms-transform:translate(-50%,-50%);
width: 2.5rem;
height: 2.5rem;
background: rgba(0,0,0,0.5);
text-align: center;
display: none;
}
.press_box2{
position: fixed;
top: 50%;
left: 50%;
transform:translate(-50%,-50%);
-webkit-transform:translate(-50%,-50%);
-moz-transform:translate(-50%,-50%);
-ms-transform:translate(-50%,-50%);
width: 2.5rem;
height: 2.5rem;
background: rgba(0,0,0,0.5);
text-align: center;
display: none;
}
.press_box3{
position: fixed;
top: 50%;
left: 50%;
transform:translate(-50%,-50%);
-webkit-transform:translate(-50%,-50%);
-moz-transform:translate(-50%,-50%);
-ms-transform:translate(-50%,-50%);
width: 2.5rem;
height: 2.5rem;
background: rgba(0,0,0,0.5);
text-align: center;
display: none;
}
.press_text3{
color: white;
font-size: 1rem;
line-height: 1rem;
margin-top: 0.5rem;
}
.press_text4{
color: white;
font-size: 0.24rem;
margin-top: 0.3rem;
}
.press_main2{
display: none;
}
.press_voice{
width: 1.28rem;
height: 1.28rem;
display: block;
margin-top: 0.4rem;
margin-left: 0.2rem;
}
.press_voice2{
width: 1.28rem;
height: 1.28rem;
display: block;
margin: auto;
margin-top: 0.4rem;
}
.press_text{
color: white;
font-size: 0.24rem;
margin-top: 0.3rem;
}
.press_text2{
color: white;
font-size: 0.24rem;
margin-top: 0.3rem;
}
.recpowerx{
width: 0.4rem;
height:0.1rem;
max-height: 1.28rem;
min-height: 0.1rem;
background:white;
position:absolute;
right: 0.5rem;
bottom: 0.83rem;
}
.add_yuyin .r_yuyin b.bofang {
background: url(images/mobile/yuyin.png) no-repeat -.17rem -.1rem;
-webkit-animation: bofang 1s steps(1,start) infinite;
background-size: 1.9rem .49rem;
}
.add_yuyin .r_yuyin b {
width: .27rem;
height: .32rem;
position: absolute;
right: .2rem;
top: .24rem;
background: url(images/mobile/icon.png) no-repeat -7.14rem -3.65rem;
background-size: 14.57rem 4.86rem;
transform:rotate(180deg);
}
/** 播放录音的动画效果 **/
@-webkit-keyframes bofang{
25% {
background-position:-.17rem -.1rem;
}
50% {
background-position:-.66rem -.11rem;
}
75% {
background-position:-1.1rem -.1rem;
}
100% {
background-position:-1.51rem -.1rem;
}
}
@keyframes bofang{
25% {
background-position:-.17rem -.1rem;
}
50% {
background-position:-.66rem -.11rem;
}
75% {
background-position:-1.1rem -.1rem;
}
100% {
background-position:-1.51rem -.1rem;
}
}
.foot_nav {
position: fixed;
left: 0;
width: 100%;
bottom: 0;
border-top: 0.01rem solid #E6EAEF;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.foot_navMain {
display: -webkit-box;
display: -webkit-flex;
display: flex;
-ms-flex-align: center;
-webkit-box-align: center;
box-align: center;
-webkit-align-items: center;
align-items: center;
padding: 0 0.2rem;
height: 1rem;
background: #ffffff;
}
.foot_navLi {
display: flex;
justify-content: center;
align-items: center;
margin-left: 0;
width: 0.8rem;
height: 0.8rem;
line-height: 0.8rem;
text-align: center;
}
.foot_navLIcon {
width: 0.48rem;
height: 0.48rem;
}
.foot_navInpBox {
background: #F4F4F4;
border-radius: 0.4rem;
-webkit-box-flex: 1;
-webkit-flex: 1;
flex: 1;
padding: 0 0.3rem;
background: none;
display: flex;
justify-content: center;
align-items: center;
}
.foot_navInput {
border-radius: 0.4rem;
background: #F4F4F4;
padding-left: 0.3rem;
box-sizing: border-box;
height: 0.6rem;
line-height: 0.6rem;
width: 100%;
}
.foot_talk {
height: 0.6rem;
line-height: 0.6rem;
width: 100%;
background: white;
border-radius: 0.4rem;
border: 1px solid #3D4A5E;
display: none;
}
.send {
color: white;
background: #87CEFA;
width: 0.9rem;
height: 0.6rem;
line-height: 0.6rem;
border-radius: 0.4rem;
text-align: center;
font-size: 0.24rem;
}
.r_yuyin {
display: -webkit-box;
display: -webkit-flex;
display: flex;
margin-top: 0.3rem;
align-items: flex-start;
justify-content: flex-end;
padding-left: 1rem;
}
.us_box {
display: flex;
justify-content: flex-end;
width: auto;
max-width: 60%;
min-width: 25%;
margin-right: 0.3rem;
background: #87CEFA;
color: #ffffff;
position: relative;
border-radius: 0.1rem;
min-height: 0.8rem;
height: 0.8rem;
}
.us_box:before {
position: absolute;
content: "";
display: block;
border-width: 0.16rem;
top: 0.2rem;
right: -0.32rem;
border-style: dashed solid solid dashed;
border-color: transparent transparent transparent #87CEFA;
}
.audio_time {
font-size: 0.24rem;
line-height: 0.8rem;
position: absolute;
left: 0.25rem;
}
.us_msgImg {
width: 1rem;
height: 1rem;
}
.us_img {
width: 100%;
display: block;
border-radius: 100%;
}
</style>
</head>
<body>
<!--音频-->
<div class="add_yuyin" id="add_yuyin">
</div>
<!--事件-->
<div class="foot_nav">
<ul class="foot_navMain">
<li class="foot_navLi" onclick="recOpen()"><img class="foot_navLIcon js_voice" src="images/mobile/icon_voice1.png" alt=""></li>
<li class="foot_navInpBox">
<input class="foot_navInput" type="text" value="" placeholder="请输入您要咨询的问题">
<button id="recStart" class="foot_talk">按住 说话</button>
</li>
<li class="foot_navLi send">发送</li>
</ul>
</div>
<!--按下说话-->
<div class="press_box">
<div class="press_main">
<img class="press_voice" src="images/mobile/icon_voice2.png" alt="">
<div class="recpowerx"></div>
</div>
<div class="press_main2">
<img class="press_voice2" src="images/mobile/icon_cancel.png" alt="">
</div>
<p class="press_text">手指上划,取消发送</p>
</div>
<div class="press_box2">
<div class="press_main3">
<img class="press_voice2" src="images/mobile/icon_hint.png" alt="">
</div>
<p class="press_text2">说话时间太短</p>
</div>
<div class="press_box3">
<p class="press_text3">10</p>
<p class="press_text4">手指上划,取消发送</p>
</div>
</body>
<script>
$(function(){
//点击语音
$('.js_voice').click(function () {
$('.js_mainMenu').removeClass('foot_mainMenu');
$('.js_mainMenu').removeClass('foot_boxMenu');
if ($(this).attr('src')==='images/mobile/icon_voice1.png'){
$(this).attr('src','images/mobile/icon_key.png')
} else {
$(this).attr('src','images/mobile/icon_voice1.png')
}
if($('.foot_navInput').is(':hidden')){//如果当前隐藏
$('.foot_navInput').show();//那么就显示div
$('.foot_talk').hide()
}else{
$('.foot_navInput').hide();//否则就隐藏div
$('.foot_talk').show()
}
})
//点击发送
$('.send').click(function () {
$('.foot_navInput').val("")
})
})
var rec;
/**调用open打开录音请求好录音权限**/
var recOpen=function(success){//一般在显示出录音按钮或相关的录音界面时进行此方法调用,后面用户点击开始录音时就能畅通无阻了
rec=Recorder({
type:"mp3",sampleRate:16000,bitRate:16 //mp3格式,指定采样率hz、比特率kbps,其他参数使用默认配置;注意:是数字的参数必须提供数字,不要用字符串;需要使用的type类型,需提前把格式支持文件加载进来,比如使用wav格式需要提前加载wav.js编码引擎
,onProcess:function(buffers,powerLevel){
$(".recpowerx").css("height",powerLevel+"%");
}
});
rec.open(function(){//打开麦克风授权获得相关资源
success&&success();
},function(msg,isUserNotAllow){//用户拒绝未授权或不支持
alert((isUserNotAllow?"UserNotAllow,":"")+"无法录音:"+msg);
$('.js_voice').attr('src','images/mobile/icon_voice1.png')
$('.foot_navInput').show();
$('.foot_talk').hide()
});
};
/**开始录音**/
var timer1 = null
var timer2 = null
$('#recStart').on('touchstart',function () {
$(this).text('松开 结束')
$('.press_box').show()
rec.start();
$('.press_text4').html('手指上划,取消发送')
//说话时间已到60s
var num = 9
timer1 = setTimeout(function () {
$('.press_box3').show()
$('.press_box').hide()
$('.press_box2').hide()
timer2 = setInterval(function () {
if(num==0){
clearInterval(timer2);
console.log('时间到')
$('.press_text4').html('说话时间已到60s')
setTimeout(function () {
$('.press_box3').hide()
},1000)
rec.stop(function(blob,duration){
console.log(blob,(window.URL||webkitURL).createObjectURL(blob),"时长111:"+duration+"ms");
var audio = (window.URL||webkitURL).createObjectURL(blob)
var time = Math.round(duration/1000)
var str = '<div class="r_yuyin">' +
'<div class="us_box">' +
'<span class="audio_time"></span>' +
'<audio id="audioTime" class="audio" src="'+audio+'"></audio>' +
'<b></b>' +
'</div>'+
'<div class="us_msgImg">'+
'<img src="images/tx02.jpg" class="us_img">'+
'</div>'+
'</div>'
$('#add_yuyin').append(str)
$('#add_yuyin .r_yuyin:last-child span').html(time+' ″')
$('#add_yuyin .r_yuyin:last-child .us_box').css("width",time+24+'%')
})
}else {
$('.press_text3').html(num)
num--
}
},1000)
},50000);
})
/**上划取消**/
var btnElem=document.getElementById("recStart");//获取ID
var posStart = 0;//初始化起点坐标
var posEnd = 0;//初始化终点坐标
var posMove = 0;//初始化终点坐标
btnElem.addEventListener("touchstart", function(event) {
event.preventDefault();//阻止浏览器默认行为
posStart = 0;
posStart = event.touches[0].pageY;//获取起点坐标
});
btnElem.addEventListener("touchmove", function(event) {
event.preventDefault();
posMove = 0;
posMove = event.changedTouches[0].pageY;//获取终点坐标
if(posStart - posMove > 20 ){
$('.press_text').text('松开手指,取消发送')
$('.press_main').hide()
$('.press_main2').show()
}else {
$('.press_text').text('手指上划,取消发送')
$('.press_main').show()
$('.press_main2').hide()
}
});
/**结束录音**/
$('#recStart').on('touchend',function (event) {
clearTimeout(timer1);
clearInterval(timer2);
$('.press_text3').html(10)
$('.press_box3').hide()
$(this).text('按住 说话')
$('.press_box').hide()
$('.press_text').text('手指上划,取消发送')
$('.press_main').show()
$('.press_main2').hide()
event.preventDefault();
posEnd = 0;
posEnd = event.changedTouches[0].pageY;//获取终点坐标
rec.stop(function(blob,duration){
console.log(blob,(window.URL||webkitURL).createObjectURL(blob),"时长:"+duration+"ms");
//播放上传
var audio = (window.URL||webkitURL).createObjectURL(blob)
var time = Math.round(duration/1000)
var str = '<div class="r_yuyin">' +
'<div class="us_box">' +
'<span class="audio_time"></span>' +
'<audio id="audioTime" class="audio" src="'+audio+'"></audio>' +
'<b></b>' +
'</div>'+
'<div class="us_msgImg">'+
'<img src="images/tx02.jpg" class="us_img">'+
'</div>'+
'</div>'
$('#add_yuyin').append(str)
$('#add_yuyin .r_yuyin:last-child span').html(time+' ″')
$('#add_yuyin .r_yuyin:last-child .us_box').css("width",time+24+'%')
//取消发送
if (posStart - posEnd > 20) {
if($('#add_yuyin .r_yuyin').length===1){
$('#add_yuyin .r_yuyin').remove()
}else {
$('#add_yuyin .r_yuyin:last-child').remove()
}
}
//说话时间太短
if(duration < 500){
$('.press_box').hide()
$('.press_box2').show()
if($('#add_yuyin .r_yuyin').length===1){
$('#add_yuyin .r_yuyin').remove()
}else {
$('#add_yuyin .r_yuyin:last-child').remove()
}
setTimeout(function () {
$('.press_box2').hide()
},500)
}
var TestApi="/test_request";
var api=TestApi;
/***方式一:将blob文件转成base64纯文本编码,使用普通application/x-www-form-urlencoded表单上传***/
var reader=new FileReader();
reader.onloadend=function(){
$.ajax({
url:api //上传接口地址
,type:"POST"
,data:{
mime:blob.type //告诉后端,这个录音是什么格式的,可能前后端都固定的mp3可以不用写
,upfile_b64:(/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result)||[])[1] //录音文件内容,后端进行base64解码成二进制
//...其他表单参数
}
,success:function(v){
console.log("上传成功",v);
}
,error:function(s){
console.error("上传失败",s);
}
});
};
reader.readAsDataURL(blob);
//方法二
// var form=new FormData();
// form.append("upfile",blob,"recorder.mp3"); //和普通form表单并无二致,后端接收到upfile参数的文件,文件名为recorder.mp3
// //...其他表单参数
// $.ajax({
// url:api //上传接口地址
// ,type:"POST"
// ,contentType:false //让xhr自动处理Content-Type header,multipart/form-data需要生成随机的boundary
// ,processData:false //不要处理data,让xhr自动处理
// ,data:form
// ,success:function(v){
// console.log("上传成功",v);
// }
// ,error:function(s){
// console.error("上传失败",s);
// }
// });
},function(msg){
if(msg){
console.log("录音失败:"+msg);
}else{
$('.press_box').hide()
$('.press_box2').show()
setTimeout(function () {
$('.press_box2').hide()
},500)
}
});
})
//录音播放
$(".add_yuyin").on("click",'.r_yuyin',function () {
let audios = $(this).find("audio")[0];
if (audios.paused) {
audios.play()
$(this).find("b").addClass("bofang");
}else {
audios.load()
$(this).find("b").removeClass("bofang");
}
// 多个语音切换
if($(this).siblings().length>0){
$(this).siblings().find("audio")[0].load();
$(this).siblings().find("b").removeClass("bofang");
}
//语音结束动画停止
let _this = $(this)
audios.addEventListener('ended', function () {
_this.find('b').removeClass("bofang");
}, false);
})
</script>
</html>