js跳转页面与打开新窗口的方法

1.超链接<a href=”http://www.jb51.net” title=”脚本之家”>Welcome</a>

等效于js代码

window.location.href=”http://www.jb51.net”;     //在同当前窗口中打开窗口

2.超链接<a href=”http://www.jb51.net” title=”脚本之家” target=”_blank”>Welcome</a>

等效于js代码

window.open(“http://www.jb51.net”);                 //在另外新建窗口中打开窗口

//详细介绍

第一种:
    <script language=”JavaScript” type=”text/javascript“>
           window.location.href=”http://www.dollare.com.cn/login.PHP?backurl=”+window.location.href; 
    </script>

第二种:
    <script language=”javascript”>
alert(“返回”);
window.history.back(-1);
   </script>

第三种:
   <script language=”javascript”>
window.navigate(“dollare.php“);
  </script>

第四种:
   <script language=”JavaScript”>
          self.location=’dollare.htm’;
   </script>

第五种:
   <script language=”javascript”>
          alert(“非法访问!”);
          top.location=’dollare.html;
   </script>

1.在原来的窗体中直接跳转用
window.location.href=”你所要跳转的页面”;
2、在新窗体中打开页面用:
window.open(‘你所要跳转的页面’);

window.history.back(-1);返回上一页


3、一些用法
按钮式:
<INPUT name=”pclog” type=”button” value=”/Go” onClick=”location.href=’连接地址'”>
链接式:<a href=”javascript:history.go(-1)”>返回上一页</a>
<a href=”<%=Request.ServerVariables(“HTTP_REFERER”)%>”>返回上一页</a>
直接跳转式:
<script>window.location.href=’连接地址’;</script>
开新窗口:
<a href=”/javascript:” onClick=”window.open(‘http://www.dolalre.com.cn’,”,’height=500,width=611,scrollbars=yes,status =yes’)”>123</a>

<SCRIPT>  
<!–  
window.open  

(‘dollare.html’,’newwindow’,’height=100,width=400,top=0,left=0,toolbar=no,menubar=no,scrollbars=no,  

resizable=no,location=no, status=no’)  
//写成一行  
–>  
</SCRIPT> 

  脚本运行后,page.html将在新窗体newwindow中打开,宽为100,高为400,距屏顶0象素,屏左0象素,无工

具条,无菜单条,无滚动条,不可调整大小,无地址栏,无状态栏。请对照。 
  上例中涉及的为常用的几个参数,除此以外还有很多其他参数,请见四。 

四、各项参数 
  其中yes/no也可使用1/0;pixel value为具体的数值,单位象素。 

参数 | 取值范围 | 说明  

alwaysLowered | yes/no | 指定窗口隐藏在所有窗口之后  
alwaysRaised | yes/no | 指定窗口悬浮在所有窗口之上  
depended | yes/no | 是否和父窗口同时关闭  
directories | yes/no | Nav2和3的目录栏是否可见  
height | pixel value | 窗口高度  
hotkeys | yes/no | 在没菜单栏的窗口中设安全退出热键  
innerHeight | pixel value | 窗口中文档的像素高度  
innerWidth | pixel value | 窗口中文档的像素宽度  
location | yes/no | 位置栏是否可见  
menubar | yes/no | 菜单栏是否可见  
outerHeight | pixel value | 设定窗口(包括装饰边框)的像素高度  
outerWidth | pixel value | 设定窗口(包括装饰边框)的像素宽度  
resizable | yes/no | 窗口大小是否可调整  
screenX | pixel value | 窗口距屏幕左边界的像素长度  
screenY | pixel value | 窗口距屏幕上边界的像素长度  
scrollbars | yes/no | 窗口是否可有滚动栏  
titlebar | yes/no | 窗口题目栏是否可见  
toolbar | yes/no | 窗口工具栏是否可见  
Width | pixel value | 窗口的像素宽度  
z-look | yes/no | 窗口被激活后是否浮在其它窗口之上 

===================================================== 

【1、最基本的弹出窗口代码】  
  其实代码非常简单:  




<SCRIPT LANGUAGE=”javascript”>  
<!–  
window.open (‘dollare.html’)  
–>  
</SCRIPT> 
  因为着是一段javascripts代码,所以它们应该放在<SCRIPT LANGUAGE=”javascript”>标签和</script>之间 

。<!– 和 –>是对一些版本低的浏览器起作用,在这些老浏览器中不会将标签中的代码作为文本显示出来。要养 

成这个好习惯啊。  
  Window.open (‘dollare.html’) 用于控制弹出新的窗口page.html,如果page.html不与主窗口在同一路径下, 

前面应写明路径,绝对路径(http://www.dollare.com.cn/dollare.html)和相对路径(../)均可。用单引号和双引号都可以,只是不要混用。  
  这一段代码可以加入HTML的任意位置,<head>和</head>之间可以,<body>间</body>也可以,越前越早执行 

,尤其是页面代码长,又想使页面早点弹出就尽量往前放。  

【2、经过设置后的弹出窗口】  

  下面再说一说弹出窗口的设置。只要再往上面的代码中加一点东西就可以了。  
  我们来定制这个弹出的窗口的外观,尺寸大小,弹出的位置以适应该页面的具体情况。  




<SCRIPT LANGUAGE=”javascript”>  
<!–  
window.open (‘dollare.html’, ‘newwindow’, ‘height=100, width=400, top=0,left=0, toolbar=no,  

menubar=no, scrollbars=no, resizable=no,location=no, status=no’)  
//写成一行  
–>  
</SCRIPT> 
参数解释:  
<SCRIPT LANGUAGE=”javascript”> js脚本开始;  
window.open 弹出新窗口的命令;  
‘page.html’ 弹出窗口的文件名;  
‘newwindow’ 弹出窗口的名字(不是文件名),非必须,可用空’代替;  
height=100 窗口高度;  
width=400 窗口宽度;  
top=0 窗口距离屏幕上方的象素值;  
left=0 窗口距离屏幕左侧的象素值;  

width=250,toolbar=no,scrollbars=”+scroll+”,menubar=no”);  
//写成一行  
OpenWindow.document.write(“<TITLE>dollare雄仔工作室</TITLE>”)  
OpenWindow.document.write(“<BODY BGCOLOR=#ffffff>”)  
OpenWindow.document.write(“<h1>Hello!</h1>”)  
OpenWindow.document.write(“New window opened!<br> is Good”)  
OpenWindow.document.write(“</BODY>”)  
OpenWindow.document.write(“</HTML>”)  
OpenWindow.document.close()}  
</SCRIPT>  
</head>  
<body>  
<a href=”#” onclick=”openwin()”>打开一个窗口</a>  
<input type=”button” onclick=”openwin()” value=”打开窗口”>  
</body>  
</html> 
  看看 OpenWindow.document.write()里面的代码不就是标准的HTML吗?只要按照格式写更多的行即可。千万注意多一个标签或少一个标签就会出现错误。记得用OpenWindow.document.close()结束啊。  

【9、终极应用–弹出的窗口之Cookie控制】  
  回想一下,上面的弹出窗口虽然酷,但是有一点小毛病(沉浸在喜悦之中,一定没有发现吧?)比如你将上面的脚本放在一个需要频繁经过的页面里(例如首页),那么每次刷新这个页面,窗口都会弹出一次,是不是非常烦人?:-(有解决的办法吗?Yes! 😉 Follow me. 我们使用cookie来控制一下就可以了。首先,将如下代码加入主页面HTML的<HEAD>区:  
<script>  
function openwin()  
{window.open(“page.html”,””,”width=200,height=200″)}  
function get_cookie(Name)  
{var search = Name + “=”  
var returnvalue = “”;  
if (document.cookie.length > 0) {  
offset = document.cookie.indexOf(search)  
if (offset != -1) {  
offset += search.length  
end = document.cookie.indexOf(“;”, offset);  
if (end == -1)  
end = document.cookie.length;  
returnvalue=unescape(document.cookie.substring(offset,end))  
}  
}  
return returnvalue;  
}  
function loadpopup(){  
if (get_cookie(‘popped’)==’){  
openwin()  
document.cookie=”popped=yes”  
}  
}  
</script> 
  然后,用<body onload=”loadpopup()”>(注意不是openwin而是loadpop啊!)替换主页面中原有的<BODY>这一句即可。你可以试着刷新一下这个页面或重新进入该页面,窗口再也不会弹出了。真正的Pop-Only-Once!

基于java的nio聊天室demo

实现的界面

客户端

张飞用户

刘备用户

关羽用户:

聊天室的具体功能如下: 

1: 群聊

2: 显示在线人数

3: 用户名信息

4: 用户离线, 通知其他在线的用户

聊天室代码:

链接:https://pan.baidu.com/s/1GNHRLAa3ij5Wb0tyT8jYMw
提取码:dd0x

参考: http://ifeve.com/selectors/

代码如下:

服务端代码

 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
 
public class chatServer {
    private int port;
    private Selector selector;
 
    private ByteBuffer readBuffer = ByteBuffer.allocate(1024);//调整缓冲区大小为1024字节
    private ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
    private static final String USER_NAME_TAG = "$%%^&*()!@#$^%#@*()*";
    private HashSet<String> users = new HashSet<String>();
    private HashMap<String, String> Users = new HashMap<>();
    private String user_msg;
 
    public chatServer(int port){
        this.port = port;
    }
 
    public static void main(String[] args){
        new chatServer(8081).start();
    }
 
    public void start() {
        ServerSocketChannel ssc = null;
        try {
            ssc = ServerSocketChannel.open();
            ssc.configureBlocking(false);  //服务器配置为非阻塞 即异步IO
            ssc.socket().bind(new InetSocketAddress(port));   //绑定本地端口
            selector = Selector.open();
            ssc.register(selector, SelectionKey.OP_ACCEPT);   //ssc注册到selector准备连接
            System.out.println("ChatServer started ......");
        }catch (Exception e){
            e.printStackTrace();
        }
 
        while(true){
            try {
                int events = selector.select();
                if (events > 0) {
                    Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
                    while (selectionKeys.hasNext()) {
                        SelectionKey key = selectionKeys.next();
                        selectionKeys.remove();  //移除当前的key
                        if (key.isValid()) {
                            if (key.isAcceptable()) {
                                accept(key);
                            }
                            if(key.isReadable()){
                                read(key);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
 
    private void accept(SelectionKey key) throws IOException {
        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
        SocketChannel clientChannel = ssc.accept();
        clientChannel.configureBlocking(false);
        clientChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("a new client connected "+clientChannel.getLocalAddress());
    }
 
    private void read(SelectionKey key) throws IOException{
        SocketChannel socketChannel = (SocketChannel) key.channel();
        this.readBuffer.clear();//清除缓冲区,准备接受新数据
        System.out.println("===============read");
        int numRead;
        try{
            numRead = socketChannel.read(this.readBuffer);
        }catch (IOException e){    // 客户端断开连接,这里会报错提示远程主机强迫关闭了一个现有的连接。
            offlineUser(key);
            key.cancel();
            socketChannel.close();
            return;
        }
        user_msg = new String(readBuffer.array(),0, numRead);
        for (String s: users) System.out.println("在线用户: " + s);
 
        if (user_msg.contains(USER_NAME_TAG)){   // 用户第一次登陆, 输入登录名
            String user_name = user_msg.replace(USER_NAME_TAG, "");
            user_msg = "欢迎: " + user_name + " 登录聊天室";
            users.add(socketChannel.getRemoteAddress().toString() + "===" + user_name);   // 客户端地址和用户名拼接在一起作为唯一标识
            brodcast(socketChannel, user_msg);
        }
        else if (user_msg.equals("1")){       // 显示在线人数
            user_msg = onlineUser();
            write(socketChannel, user_msg);
        }
        else {                                // 群聊
            String user = "";
            for (String s: users) {
                if (s.contains(socketChannel.toString())){
                    String[] s1 = s.split("===");
                    if (s1.length == 2){
                        user = "用户" + s1[1] + "对大家说:";
                    }else{
                        continue;
                    }
                }
            }
            brodcast(socketChannel, user + user_msg);
        }
    }
 
    private void write(SocketChannel channel, String content) throws IOException, ClosedChannelException {
        sendBuffer.clear();
        sendBuffer.put(content.getBytes());
        sendBuffer.flip();
        channel.write(sendBuffer);
        //注册读操作 下一次进行读
        channel.register(selector, SelectionKey.OP_READ);
    }
 
    /**
     *  用户下线,同时通知线上用户哪些用户下线了。
     */
    public void offlineUser(SelectionKey key) throws IOException{
        SocketChannel socketChannel = (SocketChannel) key.channel();
        for (String user: users){
            String[] s1 = user.split("===");
            if (s1.length == 2){
                String user_name = s1[1];
                if (user.contains(socketChannel.getRemoteAddress().toString())){
                    users.remove(user);
                    String message = "用户: " + user_name + " 下线了, 拜拜";
                    brodcast(socketChannel, message);
                }
            }else{
                continue;
            }
        }
    }
 
    /**
     *   在线用户
     */
    private String onlineUser(){
        String online_users = "在线用户:\n";
        String user = "";
        for (String s: users) {
            String[] s1 = s.split("===");
            if (s1.length == 2){
                user = s1[1];
            }else{
                continue;
            }
            online_users += "\t" + user + "\n";
        }
        System.out.println(" " + online_users);
        return online_users;
    }
 
    /**
     *   群聊
     */
    public void brodcast(SocketChannel except, String content) throws IOException{
        for (SelectionKey key: selector.keys()) {
            Channel targetchannel = key.channel();
            System.out.println("broadcast write:" + content);
            if(targetchannel instanceof SocketChannel && targetchannel != except) {
                SocketChannel channel = (SocketChannel) key.channel();
                write(channel, content);
            }
        }
    }
}

 

客户端代码

  
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
 
public class chatClient {
    private static final String host = "127.0.0.1";
    private static final int port = 8081;
    private  Selector selector;
    private  SocketChannel sc;
    private ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
    private ByteBuffer readBuffer = ByteBuffer.allocate(1024);
 
    private static final String USER_NAME_TAG = "$%%^&*()!@#$^%#@*()*";
 
    volatile boolean running = true;
 
    private static final Logger LOG = LoggerFactory.getLogger(chatClient.class);
 
    public chatClient() throws IOException{
        connect(host, port);
//         // 读写分离
        listen();
        
        Reader reader = new Reader();
        reader.start();
    }
 
    public static void main(String[] args) throws IOException{
        System.out.println("===================================================================================");
        System.out.println("输入1: 显示在线用户");
        System.out.println("===================================================================================");
        new chatClient();
    }
 
    public void connect(String host, int port) {
        try {
            sc = SocketChannel.open();
            sc.configureBlocking(false);
            sc.connect(new InetSocketAddress(host, port));
            this.selector = Selector.open();
            sc.register( selector, SelectionKey.OP_CONNECT);   //将channel注册到selector中
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void listen() {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            try {
                int events = selector.select();
                if (events > 0) {
                    Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
                    while (selectionKeys.hasNext()) {
                        SelectionKey selectionKey = selectionKeys.next();
                        selectionKeys.remove();
 
                        //连接事件
                        if (selectionKey.isConnectable()) {
                            sc.finishConnect();
                            System.out.println("server connected...");
                            // 人员登录
                            login(scanner);
                            //注册写操作
                            sc.register(selector, SelectionKey.OP_WRITE);
                            break;
                        }
                        else if (selectionKey.isWritable()){
                            String message = scanner.nextLine();
                            writeBuffer.clear();
                            writeBuffer.put(message.getBytes());
                            //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
                            writeBuffer.flip();
                            sc.write(writeBuffer);
 
                            sc.register(selector,  SelectionKey.OP_WRITE);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    private void login(Scanner scanner) throws IOException{
        System.out.println("请输入登录名: ");
        String message = scanner.nextLine();
        writeBuffer.clear();
        writeBuffer.put((USER_NAME_TAG + message).getBytes());
        //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
        writeBuffer.flip();
        sc.write(writeBuffer);
    }
 
    protected class Reader extends Thread {
        private final Selector writeSelector;
 
        Reader() throws IOException {
            this.setName("Reader");
            this.setDaemon(true);
            writeSelector = Selector.open(); // create a selector
            sc.register(writeSelector,  SelectionKey.OP_READ);
        }
 
        @Override
        public void run() {
            try {
                doRunLoop();
            } finally {
                LOG.info(getName() + ": stopping");
                try {
                    writeSelector.close();
                } catch (IOException ioe) {
                    LOG.error(getName() + ": couldn't close write selector", ioe);
                }
            }
        }
 
        private void doRunLoop() {
            while (running) {
                try {
                    int keyCt = writeSelector.select();
                    if (keyCt == 0) {
                        continue;
                    }
                    Set<SelectionKey> keys = writeSelector.selectedKeys();
                    Iterator<SelectionKey> iter = keys.iterator();
                    while (iter.hasNext()) {
                        SelectionKey key = iter.next();
                        iter.remove();
                        try {
                            if (key.isValid() && key.isReadable()) {
                                Thread.sleep(1);
                                SocketChannel client = (SocketChannel) key.channel();
                                //将缓冲区清空以备下次读取
                                readBuffer.clear();
                                int num = client.read(readBuffer);
                                System.out.println(new String(readBuffer.array(),0, num));
                                //注册读操作,下一次读
                                sc.register(selector, SelectionKey.OP_READ);
                            }
                        } catch (IOException e) {
                            LOG.debug(getName() + ": Reader", e);
                        }
                    }
                } catch (Exception e) {
                    LOG.warn(getName() + ": exception in Reader " + e);
                }
            }
        }
    }
}

在Vue项目中用Audio实现语音的播放

跟据项目的需求,有些时候我们需要实现,通过后台获取语音流,然后网页进行语音播放,在网页播放语音就可以避免用户的本地语音库的安装。

从后台获取语音流,生成Audio标签并赋值

​ 这里我用了axios请求

 getAudio() {
        this.$axios({
          url: "api/system/tts",//获取语音流的api
          data: { data},//这个是给后台传送需要播放的语音信息
          method: "post",
          responseType: "blob"//后台返回的为语音的流数据
        })
          .then(res => {
            let url = URL.createObjectURL(res.data);//通过这个API让语音数据转为成一个url地址
            let audio = new Audio();//在VUE中使用audio标签
            audio.src = url;//设置audio的src为上面生成的url
            let playPromiser = audio.play();//进行播放
            //在谷歌内核中,audio.play()会返回一个promise的值,在IE内核中就不会返回任何的值
            //所以如果你要分浏览器,可以判断playPromiser的值来进行操作哦
            audio.onended = () => {
            //onended可以检测语音是否播完
            //dosometing
            };
          })
          .catch(err => {});
    },

思路就是向后台发送需要播放的语音信息(文字),然后后台返回给你的语音流数据,通过URL.createObjectURL(data) 这个API生成一个URL,然后给audio标签附上url。

防止因为快速的请求语音数据造成语音播放叠在一起

​ 上面的方法是获取语音并播放,有时候用户会进行多次点击或请求,然后语音就会叠在一起播放,那就炸了(这里其实可以让后端做一个排队的队列,一个一个播,这里我前端也做了一个排队队列)

​ 对上面的方法进行改造改造

   getAudio() {
      if (this.callmsg.length > 0) {//如果队列是有人在排队的,这进行播放操作
        this.$axios({
          url: "api/system/tts",
          data: { data },
          method: "post",
          responseType: "blob"
        })
          .then(res => {
            let url = URL.createObjectURL(res.data);
            let audio = new Audio();
            audio.src = url;
            let playPromiser = audio.play();
            localStorage.setItem("audio", "1");//在这里我用一个标志,设置语音开始播放
            audio.onended = () => {
              this.callmsg.splice(0, 1);//队列的第一个播放完毕,所以删除
              localStorage.setItem("audio", "0");//这里是语音播放完毕
              this.getAudio();//进行下一个请求并播放
            };
          })
          .catch(err => {});
      } else {
          //this.audio是一个data数据,用来记录是否有人排队
        this.audio = true; //如果队列没人排队,就告诉外面已经读完啦
      }

​ 下面是用户的操作,就是触发播放函数的地方

     this.callmsg.push(data);//this.callmsg就是排队队列,点击一次,放进去一个需要播放的信息
          if (this.audio) {//如果没人
            this.audio = false;//改为有人排队了
            this.getAudio();//进行播放操作
          }

一开始的this.audio设置为true

思路是一开始为没人,需要播放信息时,将标志设置为有人,并向队列里面加入数据,然后进行请求,如果在播放中途有新的信息需要播放,就会再插入,但是标志不变,所以上面的递归函数会继续执行,直到队列为空,标志就会改回来。

Vue——前端生成二维码

 与后端生成二维码相比,前端生成二维码更具有灵活性,下面就介绍两种前端生成二维码的方式,两种方式相比之下,vue-qr比qrcode多了一个再中间添加logo的功能。

方式一:qrcode

  • npm
npm install --save qrcodejs2
  • import
import QRCode from 'qrcodejs2'
  • 使用
<div class="qrcode" ref="qrCodeUrl"></div> <script>methods: {creatQrCode() {var qrcode = new QRCode(this.$refs.qrCodeUrl, {text: 'xxxx'// 需要转换为二维码的内容width: 100,height: 100,colorDark: '#000000',colorLight: '#ffffff',correctLevel: QRCode.CorrectLevel.H})},},mounted() {this.creatQrCode();},</script>
  • 样式(这里再提供一个给二维码添加边框的小技巧:如下图所示,我们生成的二维码是没有边框的,看起来不是很好看)    
.qrcode{display: inline-block;img {width132px;height132px;background-color#fff; //设置白色背景色padding6px; // 利用padding的特性,挤出白边box-sizing: border-box;}}

  就有了下面的效果:

方式二:vue-qr

  • npm
1npm install vue-qr --save
  • import
1import vueQr from 'vue-qr'
  • 使用
// logoSrc为logo的url地址(使用require的方式);text为需要转换为二维码的内容<vue-qr :logoSrc="imageUrl" text="xxx" :size="200"></vue-qr> <script>export default {name: "qecode",data() {return {imageUrl: require("../assets/logo.png"),}},components: {vueQr},},}</script>

Web端轻松实现音视频聊天通话

网络上视频主播的火热带动了网络视频聊天室开发行业的火热。现在网上企业或者工作室在弄网页的视频聊天室。通过个人学习,借用别人的开发Demo(AnyChat SDK,网上随便搜索一下就可以下载的),加上几十行JavaScript脚本就能轻松实现视频通话;也不用去下载指定的什么浏览器,因为IE、firefox、chrome等windows平台主流浏览器全部通过,完美运行。下边就跟大伙分享分享我的成果,布局代码就不贴出来了,只贴JavaScript脚本。下面是我所使用的开发包的特点:1.       支持Windows平台浏览器上的音频即时通讯应用开发2.       提供JavaScript语言API接口,脚本编程3.       兼容IE、Chrome、Firefox、360、遨游等主流浏览器4.       支持iOS、Android、PC等设备和Web之间的互联互通一、加载AnyChat for Web SDK首先还是得先加载AnyChatfor Web SDK库<scriptlanguage=”javascript” type=”text/javascript”src=”./javascript/anychatsdk.js”charset=”GB2312″></script><scriptlanguage=”javascript” type=”text/javascript”src=”./javascript/anychatevent.js”charset=”GB2312″></script>二、全局变量定义定义全局变量var mDefaultServerAddr =”demo.anychat.cn”;                 // 默认服务器地址var mDefaultServerPort = 8906;                                       // 默认服务器端口号var mSelfUserId = -1;                                                // 本地用户IDvar mTargetUserId = -1;                                    // 目标用户ID(请求了对方的音视频)三、调用初始化函数网页加载完成后判断有没有安装插件和插件是否是最新// 页面加载完成 初始化function LogicInit(){    // 初始化      varNEED_ANYCHAT_APILEVEL = “0”;    varerrorcode = BRAC_InitSDK(NEED_ANYCHAT_APILEVEL);    if(errorcode == GV_ERR_SUCCESS)    // 初始化插件成功         document.getElementById(“login_div”).style.display =”block”;  // 显示登录界面     else    // 没有安装插件,或是插件版本太旧,显示插件下载界面         document.getElementById(“prompt_div”).style.display =”block”;    // 显示提示层  }四、调用登录函数在这里服务器地址和端口都写死,输入用户名就可以登录登录按钮点击事件:// 登录系统function LoginToHall() {   BRAC_Connect(mDefaultServerAddr, mDefaultServerPort);  // 连接服务器     BRAC_Login(document.getElementById(“username”).value, “”,0);    // 登录系统,密码为空也可登录  }调用登录函数后首先会触发连接服务器函数// 客户端连接服务器,bSuccess表示是否连接成功,errorcode表示出错代码functionOnAnyChatConnect(bSuccess, errorcode) {    if(errorcode == 0) { }    // 连接服务器成功           elsealert(“连接服务器失败”);     //连接失败作提示,此时系统不会触发登录系统函数}连接服务器成功后会触发登录系统回调函数// 客户端登录系统,dwUserId表示自己的用户ID号,errorcode表示登录结果:0 成功,否则为出错代码,参考出错代码定义functionOnAnyChatLoginSystem(dwUserId, errorcode) {    if(errorcode == 0) {    // 登录成功,显示大厅界面,隐藏登录界面。失败的话什么也不做,维持原状         mSelfUserId = dwUserId;       document.getElementById(“login_div”).style.display =”none”;   //隐藏登录界面         document.getElementById(“hall_div”).style.display =”block”;   //显示大厅界面      }}五、调用进入房间函数登录成功后显示大厅,大厅里就个输入框和一个 进入房间 按钮点击 进入房间 按钮 调用函数// 进入房间functionEnterRoom(){    // 进入自定义房间   BRAC_EnterRoom(parseInt(document.getElementById(“customroomid”).value),””, 0);  //进入房间  }进入房间触发回调函数// 客户端进入房间,dwRoomId表示所进入房间的ID号,errorcode表示是否进入房间:0成功进入,否则为出错代码functionOnAnyChatEnterRoom(dwRoomId, errorcode) {    if(errorcode == 0) {  // 进入房间成功,显示房间界面,隐藏大厅界面;进入房间失败时不作任何动作         document.getElementById(“hall_div”).style.display = “none”;//隐藏大厅界面         document.getElementById(“room_div”).style.display =”block”;  //显示房间界面         BRAC_UserCameraControl(mSelfUserId, 1);  // 打开本地视频         BRAC_UserSpeakControl(mSelfUserId, 1);   // 打开本地语音                         // 设置本地视频显示位置         BRAC_SetVideoPos(mSelfUserId,document.getElementById(“AnyChatLocalVideoDiv”),”ANYCHAT_VIDEO_LOCAL”);       // 设置远程视频显示位置(没有关联到用户,只是占位置)                               BRAC_SetVideoPos(0, document.getElementById(“AnyChatRemoteVideoDiv”),”ANYCHAT_VIDEO_REMOTE”);    }}
进入房间时,会触发在线用户回调函数// 收到当前房间的在线用户信息,进入房间后触发一次,dwUserCount表示在线用户数(包含自己),dwRoomId表示房间IDfunctionOnAnyChatRoomOnlineUser(dwUserCount, dwRoomId) {    // 判断是否需要关闭之前已请求的用户音视频数据          if(mTargetUserId != -1) {      // mTargetUserId 表示  上次视频会话的用户ID  为自定义变量                BRAC_UserCameraControl(mTargetUserId, 0);     // 关闭远程视频            BRAC_UserSpeakControl(mTargetUserId, 0);     // 关闭远程语音       mTargetUserId = -1;    }    if(dwUserCount > 1)     // 在该函数中判断是否有在线用户,有的话就打开其中一个远程视频       SetTheVideo();}有用户退出房间时判断是否远程用户,并作出相应操作// 用户进入(离开)房间,dwUserId表示用户ID号,bEnterRoom表示该用户是进入(1)或离开(0)房间functionOnAnyChatUserAtRoom(dwUserId, bEnterRoom) {    if(bEnterRoom == 1)       if (mTargetUserId == -1) SetTheVideo();    else {       if (mTargetUserId == dwUserId)           mTargetUserId = -1;    }}发送信息时调用函数// 发送信息function SendMessage() {   BRAC_SendTextMessage(0, 0,document.getElementById(“SendMsg”).innerHTML);    //调用发送信息函数   Msg:信息内容   document.getElementById(“ReceiveMsg”).innerHTML += “我:” + document.getElementById(“SendMsg”).innerHTML +”<br />”;   document.getElementById(“SendMsg”).innerHTML = “”;}收到在线用户发来信息时会触发函数// 收到文字消息functionOnAnyChatTextMessage(dwFromUserId, dwToUserId, bSecret, lpMsgBuf, dwLen) {   document.getElementById(“ReceiveMsg”).innerHTML +=BRAC_GetUserName(dwFromUserId) + “:” + lpMsgBuf +”<br />”;  // 收到信息显示到接收框}自定义函数//自定义函数 请求远程视频用户function SetTheVideo() {    varuseridlist = BRAC_GetOnlineUser();    // 获取所有在线用户ID     BRAC_UserCameraControl(useridlist[0], 1);   // 请求对方视频     BRAC_UserSpeakControl(useridlist[0], 1);   // 请求对方语音     BRAC_SetVideoPos(useridlist[0],document.getElementById(“AnyChatRemoteVideoDiv”),”ANYCHAT_VIDEO_REMOTE”);    // 设置远程视频显示位置   mTargetUserId = useridlist[0];}六、退出房间退出房间调用函数functionOutOfRoom(){         BRAC_LeaveRoom(dwRoomid);}七、退出系统退出系统调用函数functionOutOfSystem(){         BRAC_Logout();}

到此,简单的视频聊天室就完成了,如果你是个人的研究,不讲究界面和风格,甚至可以直接试用,可以和朋友直接视频聊天了,如果你是开发工作室,那么请一个设计师,优化界面和其他功能部署,那么你的开发基本完成了!

原文链接:http://www.itpub.net/thread-1898060-1-7.html

Vue中使用js-pinyin包实现城市按首字母排序

最近做项目中,碰到了点小麻烦:

后台从接口请求回来的城市相关的数据只有城市名称,没有排序,铺页面的时候要排序就很麻烦;

面向百度编程时候找到了一个包,用它来将字符串转成拼音,就可以通过字符串截取取出拼音首字母,这样就可以进行首字母排序了。

这个包的名字叫js-pinyin

  • npm下载

npm i js-pinyin

  • 下载完了之后在main.js中引入

import pinyin from ‘js-pinyin’

这样使用环境就配置好了。

当在组件中使用时,要先在export default前引用node_modules/js-pinyin中的index.js文件:

import pinyin from ‘../../../node_modules/js-pinyin/index’

注意node_modules/js-pinyin的文件路径

因为我要在页面加载时就使用这个需要排序的数据,所以我将代码写入mounted()中。

在使用真实数据前,我们先写一个小demo:

在组件中使用时,要添加

let pinyin = require(‘js-pinyin’);

pinyin.setOptions({checkPolyphone: false, charCase: 0});

两行代码,一个是引入js-pinyin,一个是配置js-pinyin。

完整的demo就是这样:

mounted() {
    let pinyin = require('js-pinyin');
    pinyin.setOptions({checkPolyphone: false, charCase: 0});
    console.log(pinyin.getFullChars('管理员'));    //GuanLiYuan
    console.log(pinyin.getCamelChars('管理员'));    //GLY
    console.log(pinyin.getCamelChars('1234'));    //1234
    console.log(pinyin.getCamelChars('english'));    //english
}

上述2个console.log中使用的函数,是js-pinyin中设计好的2个函数,作用分别是:

  • getFullChars():获取字符串全部拼音,并且首字母大写;
  • getCamelChars() : 获取字符串拼音首字母,并大写;

getCamelChars()中传入的参数不是汉字时,不会进行转换,仍然输出源字符串。

接下来使用真实数据:

首先后台给我请求回来的数据结构是这个样子的:

我需要使用js-pinyin给请求回来的数据添加一个“first: 首字母”键值对,用来标识首字母。

data() {
      return {
            FristPin: ["A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "W", "X", "Y", "Z"],
            cityjson: {},
      }
},
mounted() {
    let pinyin = require('js-pinyin');
    pinyin.setOptions({checkPolyphone: false, charCase: 0});
   
    //先拿到全部城市的名字
    let cityArr = [];
    index.tools.getAllCity().then(res => {        //这是axios请求,请求回来的结果是res
        for (let i = 0; i < res.data.city.length; i++) {
            //遍历数组,拿到城市名称
            let cityName = res.data.city[i].name;
            //取全部城市的首字母
            let fristName = pinyin.getCamelChars(cityName).substring(0, 1);    //这里截取首字母的第一位
            //给原json添加首字母键值对
            res.data.city[i].first = fristName;
            //放入新数组
            cityArr.push(res.data.city[i]);
        }
        let cityJson = {};
        //根据首字母键值对给原数据按首字母分类
         for (let i = 0; i < _this.FristPin.length; i++) {    //这里的FirstPin是一个写入了所有字母的数组,见data中
                cityJson[_this.FristPin[i]] = cityArr.filter(function (value) {
                    return value.first === _this.FristPin[i];
                })
          }
          _this.cityjson = cityJson;
    }
});

最后请求回来的数据经过我们处理后变成了data中的this.cityjson,就是以首字母开头的结构了。

vue 扫描二维码,获取二维码上的信息

扫描二维码和相册识别二维码 html5plus


<template>
<div id="bcid">
</div>
</template>

<script lang="ts">
import {$, Component, getAjax, postAjax, putAjax, url, Vue, Watch,MessageBox} from '../../untis/common';

@Component({
components: {},
})
export default class login extends Vue {
mounted(){
this.startRecognize();
}
// 创建扫描控件
startRecognize() {
console.log(1);
//@ts-ignore
if (!window.plus) return;
//@ts-ignore
console.log('window.plus',window.plus);
//@ts-ignore
let scan = new plus.barcode.Barcode('bcid');
console.log('scan',scan);
scan.onmarked = onmarked;
function onmarked(type:any, result:any, file:any) {
switch (type) {
//@ts-ignore
case plus.barcode.QR:
type = 'QR'
break
//@ts-ignore
case plus.barcode.EAN13:
type = 'EAN13'
break
//@ts-ignore
case plus.barcode.EAN8:
type = 'EAN8'
break
default:
type = '其它' + type
break
}
// 获得code
result = result.replace(/\n/g, '')
console.log('result',result);
}
scan.start()
}

}
</script>

<style lang="less">
@import "../../assets/less/common.less";
#app{
height:100%;
}
#bcid {
width: 100%;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom:3rem;
text-align: center;
color: #fff;
background: #ccc;
}
</style>

vue实现录音功能及播放amr文件(pc端)

录音功能一般来说在移动端比较常见,但是在pc端也要实现按住说话的功能呢?项目需求:按住说话,时长不超过60秒,生成语音文件并上传,我这里用的是recorder.js

1.项目中新建一个recorder.js文件,内容如下,也可在百度上直接搜一个

// 兼容window.URL = window.URL || window.webkitURLnavigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedialet HZRecorder = function (stream, config) {  config = config || {}  config.sampleBits = config.sampleBits || 8 // 采样数位 8, 16  config.sampleRate = config.sampleRate || (44100 / 6) // 采样率(1/6 44100)  let context = new (window.webkitAudioContext || window.AudioContext)()  let audioInput = context.createMediaStreamSource(stream)  let createScript = context.createScriptProcessor || context.createJavaScriptNode  let recorder = createScript.apply(context, [4096, 1, 1])  let audioData = {    size: 0, // 录音文件长度    buffer: [], // 录音缓存    inputSampleRate: context.sampleRate, // 输入采样率    inputSampleBits: 16, // 输入采样数位 8, 16    outputSampleRate: config.sampleRate, // 输出采样率    oututSampleBits: config.sampleBits, // 输出采样数位 8, 16    input: function (data) {      this.buffer.push(new Float32Array(data))      this.size += data.length    },    compress: function () { // 合并压缩      // 合并      let data = new Float32Array(this.size)      let offset = 0      for (let i = 0; i < this.buffer.length; i++) {        data.set(this.buffer[i], offset)        offset += this.buffer[i].length      }      // 压缩      let compression = parseInt(this.inputSampleRate / this.outputSampleRate)      let length = data.length / compression      let result = new Float32Array(length)      let index = 0; let j = 0      while (index < length) {        result[index] = data[j]        j += compression        index++      }      return result    },    encodeWAV: function () {      let sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate)      let sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits)      let bytes = this.compress()      let dataLength = bytes.length * (sampleBits / 8)      let buffer = new ArrayBuffer(44 + dataLength)      let data = new DataView(buffer)      let channelCount = 1// 单声道      let offset = 0      let writeString = function (str) {        for (let i = 0; i < str.length; i++) {          data.setUint8(offset + i, str.charCodeAt(i))        }      }      // 资源交换文件标识符      writeString('RIFF'); offset += 4      // 下个地址开始到文件尾总字节数,即文件大小-8      data.setUint32(offset, 36 + dataLength, true); offset += 4      // WAV文件标志      writeString('WAVE'); offset += 4      // 波形格式标志      writeString('fmt '); offset += 4      // 过滤字节,一般为 0x10 = 16      data.setUint32(offset, 16, true); offset += 4      // 格式类别 (PCM形式采样数据)      data.setUint16(offset, 1, true); offset += 2      // 通道数      data.setUint16(offset, channelCount, true); offset += 2      // 采样率,每秒样本数,表示每个通道的播放速度      data.setUint32(offset, sampleRate, true); offset += 4      // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8      data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4      // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8      data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2      // 每样本数据位数      data.setUint16(offset, sampleBits, true); offset += 2      // 数据标识符      writeString('data'); offset += 4      // 采样数据总数,即数据总大小-44      data.setUint32(offset, dataLength, true); offset += 4      // 写入采样数据      if (sampleBits === 8) {        for (let i = 0; i < bytes.length; i++ , offset++) {          let s = Math.max(-1, Math.min(1, bytes[i]))          let val = s < 0 ? s * 0x8000 : s * 0x7FFF          val = parseInt(255 / (65535 / (val + 32768)))          data.setInt8(offset, val, true)        }      } else {        for (let i = 0; i < bytes.length; i++ , offset += 2) {          let s = Math.max(-1, Math.min(1, bytes[i]))          data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)        }      }      return new Blob([data], { type: 'audio/mp3' })    }  }  // 开始录音  this.start = function () {    audioInput.connect(recorder)    recorder.connect(context.destination)  }  // 停止  this.stop = function () {    recorder.disconnect()  }  // 获取音频文件  this.getBlob = function () {    this.stop()    return audioData.encodeWAV()  }  // 回放  this.play = function (audio) {    let downRec = document.getElementById('downloadRec')    downRec.href = window.URL.createObjectURL(this.getBlob())    downRec.download = new Date().toLocaleString() + '.mp3'    audio.src = window.URL.createObjectURL(this.getBlob())  }  // 上传  this.upload = function (url, callback) {    let fd = new FormData()    fd.append('audioData', this.getBlob())    let xhr = new XMLHttpRequest()    /* eslint-disable */    if (callback) {      xhr.upload.addEventListener('progress', function (e) {        callback('uploading', e)      }, false)      xhr.addEventListener('load', function (e) {        callback('ok', e)      }, false)      xhr.addEventListener('error', function (e) {        callback('error', e)      }, false)      xhr.addEventListener('abort', function (e) {        callback('cancel', e)      }, false)    }    /* eslint-disable */    xhr.open('POST', url)    xhr.send(fd)  }  // 音频采集  recorder.onaudioprocess = function (e) {    audioData.input(e.inputBuffer.getChannelData(0))    // record(e.inputBuffer.getChannelData(0));  }}// 抛出异常HZRecorder.throwError = function (message) {  alert(message)  throw new function () { this.toString = function () { return message } }()}// 是否支持录音HZRecorder.canRecording = (navigator.getUserMedia != null)// 获取录音机HZRecorder.get = function (callback, config) {  if (callback) {    if (navigator.getUserMedia) {      navigator.getUserMedia(        { audio: true } // 只启用音频        , function (stream) {          let rec = new HZRecorder(stream, config)          callback(rec)        }        , function (error) {          switch (error.code || error.name) {            case 'PERMISSION_DENIED':            case 'PermissionDeniedError':              HZRecorder.throwError('用户拒绝提供信息。')              break            case 'NOT_SUPPORTED_ERROR':            case 'NotSupportedError':              HZRecorder.throwError('浏览器不支持硬件设备。')              break            case 'MANDATORY_UNSATISFIED_ERROR':            case 'MandatoryUnsatisfiedError':              HZRecorder.throwError('无法发现指定的硬件设备。')              break            default:              HZRecorder.throwError('无法打开麦克风。异常信息:' + (error.code || error.name))              break          }        })    } else {      HZRecorder.throwErr('当前浏览器不支持录音功能。'); return    }  }}export default HZRecorder复制代码

2.页面中使用,具体如下

<template>  <div class="wrap">    <el-form v-model="form">      <el-form-item>        <input type="button" class="btn-record-voice" @mousedown.prevent="mouseStart" @mouseup.prevent="mouseEnd" v-model="form.time"/>        <audio v-if="form.audioUrl" :src="form.audioUrl" controls="controls" class="content-audio" style="display: block;">语音</audio>      </el-form-item>    <el-form>  </div></template><script>// 引入recorder.jsimport recording from '@/js/recorder/recorder.js'export default {  data() {    return {      form: {        time: '按住说话(60秒)',        audioUrl: ''      },      num: 60, // 按住说话时间      recorder: null,      interval: '',      audioFileList: [], // 上传语音列表      startTime: '', // 语音开始时间      endTime: '', // 语音结束    }  },  methods: {    // 清除定时器    clearTimer () {      if (this.interval) {        this.num = 60        clearInterval(this.interval)      }    },    // 长按说话    mouseStart () {      this.clearTimer()      this.startTime = new Date().getTime()      recording.get((rec) => {        // 当首次按下时,要获取浏览器的麦克风权限,所以这时要做一个判断处理        if (rec) {          // 首次按下,只调用一次          if (this.flag) {            this.mouseEnd()            this.flag = false          } else {            this.recorder = rec            this.interval = setInterval(() => {              if (this.num <= 0) {                this.recorder.stop()                this.num = 60                this.clearTimer()              } else {                this.num--                this.time = '松开结束(' + this.num + '秒)'                this.recorder.start()              }            }, 1000)          }        }      })    },    // 松开时上传语音    mouseEnd () {      this.clearTimer()      this.endTime = new Date().getTime()      if (this.recorder) {        this.recorder.stop()        // 重置说话时间        this.num = 60        this.time = '按住说话(' + this.num + '秒)'        // 获取语音二进制文件        let bold = this.recorder.getBlob()        // 将获取的二进制对象转为二进制文件流        let files = new File([bold], 'test.mp3', {type: 'audio/mp3', lastModified: Date.now()})        let fd = new FormData()        fd.append('file', files)        fd.append('tenantId', 3) // 额外参数,可根据选择填写        // 这里是通过上传语音文件的接口,获取接口返回的路径作为语音路径        this.uploadFile(fd)      }    }  }}</script><style scoped></style>复制代码

3.除了上述代码中的注释外,还有一些地方需要注意

  • 上传语音时,一般会有两个参数,一个是语音的路径,一个是语音的时长,路径直接就是this.form.audioUrl,不过时长这里需要注意的是,由于我们一开始设置了定时器是有一秒的延迟,所以,要在获取到的时长基础上在减去一秒
  • 初次按住说话一定要做判断,不然就会报错啦
  • 第三点也是很重要的一点,因为我是在本地项目中测试的,可以实现录音功能,但是打包到测试环境后,就无法访问麦克风,经过多方尝试后,发现是由于我们测试环境的地址是http://***,而在谷歌浏览器中有这样一种安全策略,只允许在localhost下及https下才可以访问 ,因此换一下就完美的解决了这个问题了
  • 在使用过程中,针对不同的浏览器可能会有些兼容性的问题,如果遇到了还需自己单独处理下

上面说过了语音的按住说话功能,接下来说一说语音的播放,播放这里分为两种,一种是返回的mp3格式,通过展示audio播放,另一种是返回amr格式的播放

  1. 正常播放(mp3、wav)

一般来说,这种格式的播放不需要格外的转换,接口一般会返回语音的url以及播放时长,通过audio标签展示即可

  1. mar格式播放

由于html5标签并不支持播放amr格式的文件,所以需要单独处理,步骤如下

下载 npm install benz-amr-recorder -S

在文件中引入的前提下,进行如下操作

// 播放,playurl为按钮的点击事件,url为获取的amr格式文件    playUrl (url) {      let amr = new BenzAMRRecorder()      amr.initWithUrl(url).then(function () {        amr.play()      })    }复制代码

如果需要使用amr格式的其他操作,可参考 https://www.npmjs.com/package/benz-amr-recorder

好多东西都是在项目中才学会的,所以要趁着记忆还清晰,赶紧记下来,如果上述有什么不对的地方,还请指正

转载于:https://juejin.im/post/5cf60f6be51d45595319e2fe

【前端】vue+recorder实现录音功能

重要的事情说三遍:要在https下才能实现! 要在https下才能实现!! 要在https下才能实现!!!
我当时就是忽略了这个点折腾了很久,最后根据这篇文章:Vue.js实战——封装浏览器录音组件_6,实现了录音功能。
因为本地是http,不能看到效果,建议使用【whistle】强大的web调试代理工具来进行代理本地,随时查看修改的效果

因为原文章不能抛出录音失败的错误,所以在此基础修改下

//recorder.js
export default class Recorder {
  constructor(stream, config) {
    //兼容
    window.URL = window.URL || window.webkitURL;
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

    config = config || {};
    config.sampleBits = config.sampleBits || 16;   //采样数位 8, 16
    config.sampleRate = config.sampleRate || 8000; //采样率(1/6 44100)

    this.context = new (window.webkitAudioContext || window.AudioContext)();
    this.audioInput = this.context.createMediaStreamSource(stream);
    this.createScript = this.context.createScriptProcessor || this.context.createJavaScriptNode;
    this.recorder = this.createScript.apply(this.context, [4096, 1, 1]);

    this.audioData = {
      size: 0,          //录音文件长度
      buffer: [],    //录音缓存
      inputSampleRate: this.context.sampleRate,   //输入采样率
      inputSampleBits: 16,     //输入采样数位 8, 16
      outputSampleRate: config.sampleRate,   //输出采样率
      oututSampleBits: config.sampleBits,       //输出采样数位 8, 16
      input: function (data) {
        this.buffer.push(new Float32Array(data));
        this.size += data.length;
      },
      compress: function () { //合并压缩
        //合并
        let data = new Float32Array(this.size);
        let offset = 0;
        for (let i = 0; i < this.buffer.length; i++) {
          data.set(this.buffer[i], offset);
          offset += this.buffer[i].length;
        }
        //压缩
        let compression = parseInt(this.inputSampleRate / this.outputSampleRate);
        let length = data.length / compression;
        let result = new Float32Array(length);
        let index = 0, j = 0;
        while (index < length) {
          result[index] = data[j];
          j += compression;
          index++;
        }
        return result;
      },
      encodeWAV: function () {
        let sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
        let sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
        let bytes = this.compress();
        let dataLength = bytes.length * (sampleBits / 8);
        let buffer = new ArrayBuffer(44 + dataLength);
        let data = new DataView(buffer);

        let channelCount = 1;//单声道
        let offset = 0;

        let writeString = function (str) {
          for (let i = 0; i < str.length; i++) {
            data.setUint8(offset + i, str.charCodeAt(i));
          }
        };

        // 资源交换文件标识符
        writeString('RIFF');
        offset += 4;
        // 下个地址开始到文件尾总字节数,即文件大小-8
        data.setUint32(offset, 36 + dataLength, true);
        offset += 4;
        // WAV文件标志
        writeString('WAVE');
        offset += 4;
        // 波形格式标志
        writeString('fmt ');
        offset += 4;
        // 过滤字节,一般为 0x10 = 16
        data.setUint32(offset, 16, true);
        offset += 4;
        // 格式类别 (PCM形式采样数据)
        data.setUint16(offset, 1, true);
        offset += 2;
        // 通道数
        data.setUint16(offset, channelCount, true);
        offset += 2;
        // 采样率,每秒样本数,表示每个通道的播放速度
        data.setUint32(offset, sampleRate, true);
        offset += 4;
        // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
        data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true);
        offset += 4;
        // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
        data.setUint16(offset, channelCount * (sampleBits / 8), true);
        offset += 2;
        // 每样本数据位数
        data.setUint16(offset, sampleBits, true);
        offset += 2;
        // 数据标识符
        writeString('data');
        offset += 4;
        // 采样数据总数,即数据总大小-44
        data.setUint32(offset, dataLength, true);
        offset += 4;
        // 写入采样数据
        if (sampleBits === 8) {
          for (let i = 0; i < bytes.length; i++ , offset++) {
            let s = Math.max(-1, Math.min(1, bytes[i]));
            let val = s < 0 ? s * 0x8000 : s * 0x7FFF;
            val = parseInt(255 / (65535 / (val + 32768)));
            data.setInt8(offset, val, true);
          }
        } else {
          for (let i = 0; i < bytes.length; i++ , offset += 2) {
            let s = Math.max(-1, Math.min(1, bytes[i]));
            data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
          }
        }
        return new Blob([data], { type: 'audio/wav' });
      }
    };
  }

  //开始录音
  start () {
    this.audioInput.connect(this.recorder);
    this.recorder.connect(this.context.destination);

    //音频采集
    let self = this;
    this.recorder.onaudioprocess = function (e) {
      self.audioData.input(e.inputBuffer.getChannelData(0));
    };
  };

  //停止
  stop () {
    this.recorder.disconnect();
  };

  //获取音频文件
  getBlob () {
    this.stop();
    return this.audioData.encodeWAV();
  };

  //回放
  play (audio) {
    audio.src = window.URL.createObjectURL(this.getBlob());
  };

  //清理缓存的录音数据
  clear (audio) {
    this.audioData.buffer = [];
    this.audioData.size = 0;
    audio.src = ''
  };

  static checkError (e) {
    const { name } = e;
    let errorMsg = ''
    switch (name) {
      case 'AbortError': errorMsg = '录音设备无法被使用'; break;
      case 'NotAllowedError': errorMsg = '用户已禁止网页调用录音设备'; break;
      case 'PermissionDeniedError': errorMsg = '用户已禁止网页调用录音设备'; break;     // 用户拒绝
      case 'NotFoundError': errorMsg = '录音设备未找到'; break;
      case 'DevicesNotFoundError': errorMsg = '录音设备未找到'; break;
      case 'NotReadableError': errorMsg = '录音设备无法使用'; break;
      case 'NotSupportedError': errorMsg = '不支持录音功能'; break;
      case 'MandatoryUnsatisfiedError': errorMsg = '无法发现指定的硬件设备'; break;
      default: errorMsg = '录音调用错误'; break;
    }
    return { error: errorMsg }
  };

  static get (callback, config) {
    if (callback) {
      if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => {
          let rec = new Recorder(stream, config);
          callback(rec);
        }).catch((e) => {
          callback(Recorder.checkError(e));
        })
      } else {
        navigator.getUserMedia({ audio: true, video: false }).then((stream) => {
          let rec = new Recorder(stream, config);
          callback(rec);
        }).catch((e) => {
          // Recorder.checkError(e)
          callback(Recorder.checkError(e));
        })
      }
    }
  };
}

//record-sdk.js
import Recorder from "./recorder";
export default class Record {
  startRecord (param) {
    let self = this;
    try {
      Recorder.get(rec => {
        if (rec.error) return param.error(rec.error);
        self.recorder = rec;
        self.recorder.start();
        param.success("开始录音");
      })
    } catch (e) {
      param.error("开始录音失败" + e);
    }
  }

  stopRecord (param) {
    let self = this;
    try {
      let blobData = self.recorder.getBlob();
      param.success(blobData);
    } catch (e) {
      param.error("结束录音失败" + e);
    }
  }

  play (audio) {
    let self = this;
    try {
      self.recorder.play(audio);
    } catch (e) {
      console.error("录音播放失败" + e);
    }
  }

  clear (audio) {
    let self = this;
    try {
      self.recorder.clear(audio);
    } catch (e) {
      console.error("清空录音失败" + e);
    }
  }
}

//voice.vue
...
  <div class="record">
      <h1>{{tipMsg}}</h1>
      <button @click="onStartVoice">开始</button>
      <button @click="onEndVoice">结束</button>
      <button @click="onPlayAudio">播放</button>
      <div class="record-play"
           v-show="isFinished">
        <h2>Current voice player is:</h2>
        <audio id="audioVoice"
               controls
               autoplay></audio>
      </div>
    </div>
...
<script>
import Record from '@/plugins/recorder/record-sdk';
export default {
  data () {
    return {
      isVoice: false,
      isFinished: false,
      tipMsg: '录音',
      audio: "",
      recorder: new Record()
    }
  },
 methods: {
     // 开始录音
    onStartVoice () {
      this.onStopAudio()
      this.isFinished = false;
      this.recorder.startRecord({
        success: res => {
          this.isVoice = true
        },
        error: e => {
          this.isVoice = false
          this.$toast(e)
        }
      });
    },

    // 结束录音
    onEndVoice () {
      this.isFinished = false;
      this.recorder.stopRecord({
        success: res => {
          this.isVoice = false
          //此处可以获取音频源文件(res),用于上传等操作
          console.log('音频源文件', res)
        },
        error: e => {
          this.isVoice = false
        }
      });
    },

    // 播放录音
    onPlayAudio () {
      this.isVoice = false
      this.isFinished = true;
      this.audio = document.getElementById("audioVoice");
      this.recorder.play(this.audio);
    },

    // 停止播放录音
    onStopAudio () {
      this.recorder.clear(this.audio);
    }
  }
}
</script>

因为项目可能要实现语音录菜功能,便提前看下h5是否可以录音,如果后期真要实现语音录菜功能,,再来更新

作者:北极星丶超帅的
链接:https://www.jianshu.com/p/f5637e838af0
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。