Node.js+socket.io即時(shí)通訊+HTML5桌面提醒(notification)(下)

接著上次的文章
Node.js+socket.io即時(shí)通訊+HTML5桌面提醒(notification(上)
寫的下篇:

1.打開node_modules文件,打開socket.io文件,找到socket.io.js,在package.json所在的目錄,新建www文件夾.
2.在www文件夾里,建立自己的 index.html ,css樣式,js文件,以及socket.io.js,文件目錄如下:

Snip20170131_8.png
Snip20170131_9.png

3.文件內(nèi)容為:
index.html:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>hichat</title>
        <link rel="stylesheet" href="styles/main.css">
        <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
        <link rel="icon" href="favicon.ico" type="image/x-icon">
    </head>
    <body>
        <div class="wrapper">
            <div class="banner">
                <h1>HiChat :)</h1>
                <span id="status"></span>
            </div>
            <div id="historyMsg">
            </div>
            <div class="controls" >
                <div class="items">
                    <input id="colorStyle" type="color" placeHolder='#000' title="font color" />
                    <input id="emoji" type="button" value="emoji" title="emoji" />
                    <label for="sendImage" class="imageLable">
                        <input type="button" value="image"  />
                        <input id="sendImage" type="file" value="image"/>
                    </label>
                    <input id="clearBtn" type="button" value="clear" title="clear screen" />
                    <input id="notifBtn" type="button" value="通知" title="Notification">
                </div>
                <textarea id="messageInput" placeHolder="enter to send"></textarea>
                <input id="sendBtn" type="button" value="SEND">
                <div id="emojiWrapper">
                </div>
            </div>
        </div>
        <div id="loginWrapper">
            <p id="info">connecting to server...</p>
            <div id="nickWrapper">
                <input type="text" placeHolder="nickname" id="nicknameInput" />
                <input type="button" value="OK" id="loginBtn" />
            </div>
        </div>
        <script src="/socket.io/socket.io.js"></script>
        <script src="scripts/hichat.js"></script>
    </body>
</html>

main.css

html, body {
    margin: 0;
    background-color: #efefef;
    font-family: sans-serif;
}
.wrapper {
    width: 500px;
    height: 640px;
    padding: 5px;
    margin: 0 auto;
    background-color: #ddd;
}
#loginWrapper {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(5, 5, 5, .6);
    text-align: center;
    color: #fff;
    display: block;
    padding-top: 200px;
}
#nickWrapper {
    display: none;
}
.banner {
    height: 80px;
    width: 100%;
}
.banner p {
    float: left;
    display: inline-block;
}
.controls {
    height: 100px;
    margin: 5px 0px;
    position: relative;
}
#historyMsg {
    height: 400px;
    background-color: #fff;
    overflow: auto;
    padding: 2px;
}
#historyMsg img {
    max-width: 99%;
}
.timespan {
    color: #ddd;
}
.items {
    height: 30px;
}
#colorStyle {
    width: 50px;
    border: none;
    padding: 0;
}
/*custom the file input*/

.imageLable {
    position: relative;
}
#sendImage {
    position: absolute;
    width: 52px;
    left: 0;
    opacity: 0;
    overflow: hidden;
}
/*end custom file input*/

#messageInput {
    width: 440px;
    max-width: 440px;
    height: 90px;
    max-height: 90px;
    float:left;
}
#sendBtn {
    width: 50px;
    height: 96px;
    float: right;
}
#emojiWrapper {
    display: none;
    width: 500px;
    bottom: 105px;
    position: absolute;
    background-color: #aaa;
    box-shadow: 0 0 10px #555;
}
#emojiWrapper img {
    margin: 2px;
    padding: 2px;
    width: 25px;
    height: 25px;
}
#emojiWrapper img:hover {
    background-color: blue;
}
.emoji{
    display: inline;
}
footer {
    text-align: center;
}

main.js

window.onload = function() {
    var hichat = new HiChat();
    hichat.init();
    if(window.Notification){
            if(Notification.permission === 'granted'){
            }else{
                Notification.requestPermission();
            }
        }
};
var HiChat = function() {
    this.socket = null;
};
HiChat.prototype = {
    init: function() {
        var that = this;
        this.socket = io.connect();
        this.socket.on('connect', function() {
            document.getElementById('info').textContent = 'get yourself a nickname :)';
            document.getElementById('nickWrapper').style.display = 'block';
            document.getElementById('nicknameInput').focus();
        });
        this.socket.on('nickExisted', function() {
            document.getElementById('info').textContent = '!nickname is taken, choose another pls';
        });
        this.socket.on('loginSuccess', function() {
            document.title = 'hichat | ' + document.getElementById('nicknameInput').value;
            document.getElementById('loginWrapper').style.display = 'none';
            document.getElementById('messageInput').focus();
        });
        this.socket.on('error', function(err) {
            if (document.getElementById('loginWrapper').style.display == 'none') {
                document.getElementById('status').textContent = '!fail to connect :(';
            } else {
                document.getElementById('info').textContent = '!fail to connect :(';
            }
        });
        this.socket.on('system', function(nickName, userCount, type) {
            var msg = nickName + (type == 'login' ? ' joined' : ' left');
            that._displayNewMsg('system ', msg, 'red');
            document.getElementById('status').textContent = userCount + (userCount > 1 ? ' users' : ' user') + ' online';
        });
        this.socket.on('newMsg', function(user, msg, color) {
            that._displayNewMsg(user, msg, color);
        });
        this.socket.on('newImg', function(user, img, color) {
            that._displayImage(user, img, color);
        });

        this.socket.on('notification',function(user,config){
            that._consoleText(user,config);
        });

        document.getElementById('loginBtn').addEventListener('click', function() {
            var nickName = document.getElementById('nicknameInput').value;
            if (nickName.trim().length != 0) {
                that.socket.emit('login', nickName);
            } else {
                document.getElementById('nicknameInput').focus();
            };
        }, false);
        document.getElementById('nicknameInput').addEventListener('keyup', function(e) {
            if (e.keyCode == 13) {
                var nickName = document.getElementById('nicknameInput').value;
                if (nickName.trim().length != 0) {
                    that.socket.emit('login', nickName);
                };
            };
        }, false);
        document.getElementById('sendBtn').addEventListener('click', function() {
            var messageInput = document.getElementById('messageInput'),
                msg = messageInput.value,
                color = document.getElementById('colorStyle').value;
            messageInput.value = '';
            messageInput.focus();
            if (msg.trim().length != 0) {
                that.socket.emit('postMsg', msg, color);
                that._displayNewMsg('me', msg, color);
                return;
            };
        }, false);
        document.getElementById('messageInput').addEventListener('keyup', function(e) {
            var messageInput = document.getElementById('messageInput'),
                msg = messageInput.value,
                color = document.getElementById('colorStyle').value;
            if (e.keyCode == 13 && msg.trim().length != 0) {
                messageInput.value = '';
                that.socket.emit('postMsg', msg, color);
                that._displayNewMsg('me', msg, color);
            };
        }, false);
        document.getElementById('clearBtn').addEventListener('click', function() {
            document.getElementById('historyMsg').innerHTML = '';
        }, false);
        document.getElementById('sendImage').addEventListener('change', function() {
            if (this.files.length != 0) {
                var file = this.files[0],
                    reader = new FileReader(),
                    color = document.getElementById('colorStyle').value;
                if (!reader) {
                    that._displayNewMsg('system', '!your browser doesn\'t support fileReader', 'red');
                    this.value = '';
                    return;
                };
                reader.onload = function(e) {
                    this.value = '';
                    that.socket.emit('img', e.target.result, color);
                    that._displayImage('me', e.target.result, color);
                };
                reader.readAsDataURL(file);
            };
        }, false);
        this._initialEmoji();
        document.getElementById('emoji').addEventListener('click', function(e) {
            var emojiwrapper = document.getElementById('emojiWrapper');
            emojiwrapper.style.display = 'block';
            e.stopPropagation();
        }, false);
        document.body.addEventListener('click', function(e) {
            var emojiwrapper = document.getElementById('emojiWrapper');
            if (e.target != emojiwrapper) {
                emojiwrapper.style.display = 'none';
            };
        });
document.getElementById('emojiWrapper').addEventListener('click', function(e) {
            var target = e.target;
            if (target.nodeName.toLowerCase() == 'img') {
                var messageInput = document.getElementById('messageInput');
                messageInput.focus();
                messageInput.value = messageInput.value + '[emoji:' + target.title + ']';
            };
        }, false);  document.getElementById('notifBtn').addEventListener('click',function(){
          var config = {
                          body: '我發(fā)起了一個(gè)描述~', //正文內(nèi)容
                          dir: 'auto',//文本顯示方向 auto ,ltr,rtl
                          lang:'en', //文本語言
                          icon: 'http://img4.imgtn.bdimg.com/it/u=196405673,1932175745&fm=23&gp=0.jpg' //圖片的URL,將被用于顯示通知的圖標(biāo)
                      };
          that.socket.emit('noti',config);
          return;
        }),false;
    },
    _initialEmoji: function() {
        var emojiContainer = document.getElementById('emojiWrapper'),
            docFragment = document.createDocumentFragment();
        for (var i = 69; i > 0; i--) {
            var emojiItem = document.createElement('img');
            emojiItem.src = '../content/emoji/' + i + '.gif';
            emojiItem.title = i;
            docFragment.appendChild(emojiItem);
        };
        emojiContainer.appendChild(docFragment);
    },
    _displayNewMsg: function(user, msg, color) {
        var container = document.getElementById('historyMsg'),
            msgToDisplay = document.createElement('p'),
            date = new Date().toTimeString().substr(0, 8),
            //determine whether the msg contains emoji
            msg = this._showEmoji(msg);
        msgToDisplay.style.color = color || '#000';
        msgToDisplay.innerHTML = user + '<span class="timespan">(' + date + '): </span>' + msg;
        container.appendChild(msgToDisplay);
        container.scrollTop = container.scrollHeight;
    },
    _displayImage: function(user, imgData, color) {
        var container = document.getElementById('historyMsg'),
            msgToDisplay = document.createElement('p'),
            date = new Date().toTimeString().substr(0, 8);
        msgToDisplay.style.color = color || '#000';
        msgToDisplay.innerHTML = user + '<span class="timespan">(' + date + '): </span> <br/>' + '<a href="' + imgData + '" target="_blank">![](' + imgData + ')</a>';
        container.appendChild(msgToDisplay);
        container.scrollTop = container.scrollHeight;
    },
    _showEmoji: function(msg) {
        var match, result = msg,
            reg = /\[emoji:\d+\]/g,
            emojiIndex,
            totalEmojiNum = document.getElementById('emojiWrapper').children.length;
        while (match = reg.exec(msg)) {
            emojiIndex = match[0].slice(7, -1);
            if (emojiIndex > totalEmojiNum) {
                result = result.replace(match[0], '[X]');
            } else {
                result = result.replace(match[0], '![](../content/emoji/' + emojiIndex + '.gif)');//todo:fix this in chrome it will cause a new request for the image
            };
        };
        return result;
    },
    _consoleText: function(user,config){
      if(window.Notification){
              if(Notification.permission === 'granted'){
                  var notification = new Notification(user,config);
                  delete notification;
              }else{
                  Notification.requestPermission();
              }
          }
    }
};
  1. socket.io
    我們要求連接的用戶需要首先設(shè)置一個(gè)昵稱搬素,且這個(gè)昵稱還要唯一,也就是不能與別人同名小渊。一是方便用戶區(qū)分麦向,二是為了統(tǒng)計(jì)在線人數(shù)突硝,同時(shí)也方便維護(hù)一個(gè)保存所有用戶昵稱的數(shù)組再悼。

為此在后臺(tái)server.js中,我們創(chuàng)建一個(gè)名叫users的全局?jǐn)?shù)組變量,當(dāng)一個(gè)用戶設(shè)置好昵稱發(fā)送到服務(wù)器的時(shí)候阅签,將昵稱壓入users數(shù)組矿瘦。同時(shí)注意枕面,如果用戶斷線離開了,也要相應(yīng)地從users數(shù)組中移除以保證數(shù)據(jù)的正確性缚去。

在前臺(tái)潮秘,輸入昵稱點(diǎn)擊OK提交后,我們需要發(fā)起一個(gè)設(shè)置昵稱的事件以便服務(wù)器偵聽到易结。將以下代碼添加到之前的init方法中枕荞。

//昵稱設(shè)置的確定按鈕
document.getElementById('loginBtn').addEventListener('click', function() {
    var nickName = document.getElementById('nicknameInput').value;
    //檢查昵稱輸入框是否為空
    if (nickName.trim().length != 0) {
        //不為空,則發(fā)起一個(gè)login事件并將輸入的昵稱發(fā)送到服務(wù)器
        that.socket.emit('login', nickName);
    } else {
        //否則輸入框獲得焦點(diǎn)
        document.getElementById('nicknameInput').focus();
    };
}, false);

服務(wù)器端:

//socket部分
io.on('connection', function(socket) {
    //昵稱設(shè)置
    socket.on('login', function(nickname) {
        if (users.indexOf(nickname) > -1) {
            socket.emit('nickExisted');
        } else {
            socket.userIndex = users.length;
            socket.nickname = nickname;
            users.push(nickname);
            socket.emit('loginSuccess');
            io.sockets.emit('system', nickname); //向所有連接到服務(wù)器的客戶端發(fā)送當(dāng)前登陸用戶的昵稱 
        };
    });
});
在線統(tǒng)計(jì)

這里實(shí)現(xiàn)顯示在線用戶數(shù)及在聊天主界面中以系統(tǒng)身份顯示用戶連接離開等信息搞动。

上面server.js中除了loginSuccess事件躏精,后面還有一句代碼,通過io.sockets.emit 向所有用戶發(fā)送了一個(gè)system事件鹦肿,傳遞了剛登入用戶的昵稱矗烛,所有人接收到這個(gè)事件后,會(huì)在聊天窗口顯示一條系統(tǒng)消息'某某加入了聊天室'箩溃。同時(shí)考慮到在前端我們無法得知用戶是進(jìn)入還是離開瞭吃,所以在這個(gè)system事件里我們多傳遞一個(gè)數(shù)據(jù)來表明用戶是進(jìn)入還是離開碌嘀。

將server.js中l(wèi)ogin事件更改如下:

server.js

socket.on('login', function(nickname) {
     if (users.indexOf(nickname) > -1) {
         socket.emit('nickExisted');
     } else {
         socket.userIndex = users.length;
         socket.nickname = nickname;
         users.push(nickname);
         socket.emit('loginSuccess');
         io.sockets.emit('system', nickname, users.length, 'login');
     };
 });

較之前,多傳遞了一個(gè)login字符串歪架。

同時(shí)再添加一個(gè)用戶離開的事件股冗,這個(gè)可能通過socket.io自帶的disconnect事件完成,當(dāng)一個(gè)用戶斷開連接和蚪,disconnect事件就會(huì)觸發(fā)魁瞪。在這個(gè)事件中,做兩件事情惠呼,一是將用戶從users數(shù)組中刪除导俘,一是發(fā)送一個(gè)system事件通知所有人'某某離開了聊天室'。

將以下代碼添加到server.js中connection的回調(diào)函數(shù)中剔蹋。

server.js

//斷開連接的事件
socket.on('disconnect', function() {
    //將斷開連接的用戶從users中刪除
    users.splice(socket.userIndex, 1);
    //通知除自己以外的所有人
    socket.broadcast.emit('system', socket.nickname, users.length, 'logout');
});
發(fā)送消息

用戶連接以及斷開我們需要顯示系統(tǒng)消息旅薄,用戶還要頻繁的發(fā)送聊天消息,所以可以考慮將消息顯示到頁面這個(gè)功能單獨(dú)寫一個(gè)函數(shù)方便我們調(diào)用泣崩。為此我們向HiChat類中添加一個(gè)_displayNewMsg的方法少梁,它接收要顯示的消息,消息來自誰矫付,以及一個(gè)顏色共三個(gè)參數(shù)凯沪。因?yàn)槲覀兿胂到y(tǒng)消息區(qū)別于普通用戶的消息,所以增加一個(gè)顏色參數(shù)买优。同時(shí)這個(gè)參數(shù)也方便我們之后實(shí)現(xiàn)讓用戶自定義文本顏色做準(zhǔn)備妨马。

將以下代碼添加到的我的HiChat類當(dāng)中。

//向原型添加業(yè)務(wù)方法
HiChat.prototype = {
    init: function() { //此方法初始化程序
        //...
    },
    _displayNewMsg: function(user, msg, color) {
        var container = document.getElementById('historyMsg'),
            msgToDisplay = document.createElement('p'),
            date = new Date().toTimeString().substr(0, 8);
        msgToDisplay.style.color = color || '#000';
        msgToDisplay.innerHTML = user + '<span class="timespan">(' + date + '): </span>' + msg;
        container.appendChild(msgToDisplay);
        container.scrollTop = container.scrollHeight;
    }
};

在_displayNewMsg方法中杀赢,我們還向消息添加了一個(gè)日期烘跺。我們也判斷了該方法在調(diào)用時(shí)有沒有傳遞顏色參數(shù),沒有傳遞顏色的話默認(rèn)使用#000即黑色脂崔。

同時(shí)修改我們?cè)趕ystem事件中顯示系統(tǒng)消息的代碼滤淳,讓它調(diào)用這個(gè)_displayNewMsg方法。

this.socket.on('system', function(nickName, userCount, type) {
    var msg = nickName + (type == 'login' ? ' joined' : ' left');
    //指定系統(tǒng)消息顯示為紅色
    that._displayNewMsg('system ', msg, 'red');
    document.getElementById('status').textContent = userCount + (userCount > 1 ? ' users' : ' user') + ' online';
});

有了這個(gè)顯示消息的方法后砌左,下面就開始實(shí)現(xiàn)用戶之間的聊天功能了脖咐。

做法也很簡(jiǎn)單,如果你掌握了上面所描述的emit發(fā)送事件汇歹,on接收事件屁擅,那么用戶聊天消息的發(fā)送接收也就輕車熟路了。

首先為頁面的發(fā)送按鈕寫一個(gè)click事件處理程序秤朗,我們通過addEventListner來監(jiān)聽這個(gè)click事件煤蹭,當(dāng)用戶點(diǎn)擊發(fā)送的時(shí)候,先檢查輸入框是否為空,如果不為空硝皂,則向服務(wù)器發(fā)送postMsg事件常挚,將用戶輸入的聊天文本發(fā)送到服務(wù)器,由服務(wù)器接收并分發(fā)到除自己外的所有用戶稽物。

將以下代碼添加到main.js的inti方法中奄毡。

document.getElementById('sendBtn').addEventListener('click', function() {
    var messageInput = document.getElementById('messageInput'),
        msg = messageInput.value;
    messageInput.value = '';
    messageInput.focus();
    if (msg.trim().length != 0) {
        that.socket.emit('postMsg', msg); //把消息發(fā)送到服務(wù)器
        that._displayNewMsg('me', msg); //把自己的消息顯示到自己的窗口中
    };
}, false);

在server.js中添加代碼以接收postMsg事件。

io.on('connection', function(socket) {
    //其他代碼贝或。吼过。。

    //接收新消息
    socket.on('postMsg', function(msg) {
        //將消息發(fā)送到除自己外的所有用戶
        socket.broadcast.emit('newMsg', socket.nickname, msg);
    });
});

然后在客戶端接收服務(wù)器發(fā)送的newMsg事件咪奖,并將聊天消息顯示到頁面盗忱。

將以下代碼顯示添加到main.js的init方法中了。

this.socket.on('newMsg', function(user, msg) {
    that._displayNewMsg(user, msg);
});
發(fā)送圖片

圖片不同于文字羊赵,但通過將圖片轉(zhuǎn)化為字符串形式后趟佃,便可以像發(fā)送普通文本消息一樣發(fā)送圖片了,只是在顯示的時(shí)候?qū)⑺€原為圖片昧捷。

在這之前闲昭,我們已經(jīng)將圖片按鈕在頁面放好了,其實(shí)是一個(gè)文件類型的input靡挥,下面只需在它身上做功夫便可序矩。

用戶點(diǎn)擊圖片按鈕后,彈出文件選擇窗口供用戶選擇圖片跋破。之后我們可以在JavaScript代碼中使用FileReader來將圖片讀取為base64格式的字符串形式進(jìn)行發(fā)送簸淀。而base64格式的圖片直接可以指定為圖片的src,這樣就可以將圖片用img標(biāo)簽顯示在頁面了幔烛。

為此我們監(jiān)聽圖片按鈕的change事件啃擦,一但用戶選擇了圖片囊蓝,便顯示到自己的屏幕上同時(shí)讀取為文本發(fā)送到服務(wù)器饿悬。

將以下代碼添加到main.js的init方法中。

document.getElementById('sendImage').addEventListener('change', function() {
    //檢查是否有文件被選中
     if (this.files.length != 0) {
        //獲取文件并用FileReader進(jìn)行讀取
         var file = this.files[0],
             reader = new FileReader();
         if (!reader) {
             that._displayNewMsg('system', '!your browser doesn\'t support fileReader', 'red');
             this.value = '';
             return;
         };
         reader.onload = function(e) {
            //讀取成功聚霜,顯示到頁面并發(fā)送到服務(wù)器
             this.value = '';
             that.socket.emit('img', e.target.result);
             that._displayImage('me', e.target.result);
         };
         reader.readAsDataURL(file);
     };
 }, false);

上面圖片讀取成功后狡恬,調(diào)用_displayNImage方法將圖片顯示在自己的屏幕同時(shí)向服務(wù)器發(fā)送了一個(gè)img事件,在server.js中蝎宇,我們通過這個(gè)事件來接收并分發(fā)圖片到每個(gè)用戶弟劲。同時(shí)也意味著我們還要在前端寫相應(yīng)的代碼來接收。

這個(gè)_displayNImage還沒有實(shí)現(xiàn)姥芥,將會(huì)在下面介紹兔乞。

將以下代碼添加到server.js的socket回調(diào)函數(shù)中。

server.js

//接收用戶發(fā)來的圖片
 socket.on('img', function(imgData) {
    //通過一個(gè)newImg事件分發(fā)到除自己外的每個(gè)用戶
     socket.broadcast.emit('newImg', socket.nickname, imgData);
 });

main.js

this.socket.on('newImg', function(user, img) {
     that._displayImage(user, img);
 });

有個(gè)問題就是如果圖片過大,會(huì)破壞整個(gè)窗口的布局庸追,或者會(huì)出現(xiàn)水平滾動(dòng)條霍骄,所以我們對(duì)圖片進(jìn)行樣式上的設(shè)置讓它最多只能以聊天窗口的99%寬度來顯示,這樣過大的圖片就會(huì)自己縮小了淡溯。

#historyMsg img {
    max-width: 99%;
}

但考慮到縮小后的圖片有可能失真读整,用戶看不清,我們需要提供一個(gè)方法讓用戶可以查看原尺寸大小的圖片咱娶,所以將圖片用一個(gè)鏈接進(jìn)行包裹米间,當(dāng)點(diǎn)擊圖片的時(shí)候我們打開一個(gè)新的窗口頁面,并將圖片按原始大小呈現(xiàn)到這個(gè)新頁面中讓用戶查看膘侮。

所以最后我們實(shí)現(xiàn)的_displayNImage方法應(yīng)該是這樣的屈糊。

將以下代碼添加到main.js的HiChat類中。

_displayImage: function(user, imgData, color) {
    var container = document.getElementById('historyMsg'),
        msgToDisplay = document.createElement('p'),
        date = new Date().toTimeString().substr(0, 8);
    msgToDisplay.style.color = color || '#000';
    msgToDisplay.innerHTML = user + '<span class="timespan">(' + date + '): </span> <br/>' + '<a href="' + imgData + '" target="_blank">![](' + imgData + ')</a>';
    container.appendChild(msgToDisplay);
    container.scrollTop = container.scrollHeight;
}
發(fā)送表情

規(guī)定了一種格式來代表表情琼了,[emoji:xx]另玖,中括號(hào)括起來然后'emoji'加個(gè)冒號(hào),后面跟一個(gè)數(shù)字表伦,這個(gè)數(shù)字表示某個(gè)gif圖片的編號(hào)谦去。程序中,如果我們點(diǎn)擊表情按扭蹦哼,然后呈現(xiàn)所有可用的表情圖片鳄哭,當(dāng)用戶選擇一個(gè)表情后,生成對(duì)應(yīng)的代碼插入到當(dāng)前待發(fā)送的文字消息中纲熏。發(fā)出去后妆丘,每個(gè)人接收到的也是代碼形式的消息,只是在將消息顯示到頁面前局劲,我們將表情代碼提取出來勺拣,獲取圖片編號(hào),然后用相應(yīng)的圖片替換鱼填。

文字顏色

_displayNewMsg方法可以接收一個(gè)color參數(shù)药有,現(xiàn)在要做的就是每次發(fā)送消息到服務(wù)器的時(shí)候,多加一個(gè)color參數(shù)就可以了苹丸,同時(shí)愤惰,在顯示消息時(shí)調(diào)用_displayNewMsg的時(shí)候?qū)⑦@個(gè)color傳遞過去。

下面是修改main.js中消息發(fā)送按鈕代碼的示例:

document.getElementById('sendBtn').addEventListener('click', function() {
    var messageInput = document.getElementById('messageInput'),
        msg = messageInput.value,
        //獲取顏色值
        color = document.getElementById('colorStyle').value;
    messageInput.value = '';
    messageInput.focus();
    if (msg.trim().length != 0) {
        //顯示和發(fā)送時(shí)帶上顏色值參數(shù)
        that.socket.emit('postMsg', msg, color);
        that._displayNewMsg('me', msg, color);
    };
}, false);

同樣socket的綁定事件 newMsg

this.socket.on('newMsg', function(user, msg, color) {
     that._displayNewMsg(user, msg, color);
 });

這只是展示了發(fā)送按鈕的修改赘理,改動(dòng)非常小宦言,只是每次消息發(fā)送時(shí)獲取一下顏色值,同時(shí)emit事件到服務(wù)器的時(shí)候也帶上這個(gè)顏色值商模,這樣前端在顯示時(shí)就可以根據(jù)這個(gè)顏色值為每個(gè)不兩只用戶顯示他們自己設(shè)置的顏色了奠旺。
三.HTML5桌面提醒(Notification)
在聊天中,雖然已經(jīng)有顯示,某位用戶登錄.但如果用戶不在該頁面時(shí),如果A用戶要通知B用戶,例如QQ的抖一抖;
HTML5提供最新的API: Notification
微信用Notification綁定socket,來做即時(shí)通訊,通知用戶.Notification的兼容最新瀏覽器,如圖

2016-07-05_221531.png

同理,server.js:

//new Notification
    socket.on('noti',function(config){
        socket.broadcast.emit('notification',socket.nickname,config);
    });

main.js:

this.socket.on('notification',function(user,config){
            that._consoleText(user,config);
        });

document.getElementById('notifBtn').addEventListener('click',function(){
          var config = {
                          body: '我發(fā)起了一個(gè)描述~', //正文內(nèi)容
                          dir: 'auto',//文本顯示方向 auto ,ltr,rtl
                          lang:'en', //文本語言
                          icon: 'http://img4.imgtn.bdimg.com/it/u=196405673,1932175745&fm=23&gp=0.jpg' //圖片的URL,將被用于顯示通知的圖標(biāo)
                      };
          that.socket.emit('noti',config);
          return;
        }, false);

同樣,需要把_consoleText添加到HiChat類中:

_consoleText: function(user,config){
      if(window.Notification){
              if(Notification.permission === 'granted'){
                  var notification = new Notification(user,config);
                  delete notification;
              }else{
                  Notification.requestPermission();
              }
          }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蜘澜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子响疚,更是在濱河造成了極大的恐慌兼都,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稽寒,死亡現(xiàn)場(chǎng)離奇詭異扮碧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)杏糙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門慎王,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宏侍,你說我怎么就攤上這事赖淤。” “怎么了谅河?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵咱旱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我绷耍,道長(zhǎng)吐限,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任褂始,我火速辦了婚禮诸典,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘崎苗。我一直安慰自己狐粱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布胆数。 她就那樣靜靜地躺著肌蜻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪必尼。 梳的紋絲不亂的頭發(fā)上蒋搜,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音胰伍,去河邊找鬼齿诞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛骂租,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斑司,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼渗饮,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼但汞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起互站,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤私蕾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后胡桃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體踩叭,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年翠胰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了容贝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡之景,死狀恐怖斤富,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锻狗,我是刑警寧澤满力,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站轻纪,受9級(jí)特大地震影響油额,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刻帚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一悔耘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧我擂,春花似錦衬以、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衙吩,卻和暖如春互妓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坤塞。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國打工冯勉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摹芙。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓灼狰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親浮禾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子交胚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 同時(shí)再添加一個(gè)用戶離開的事件份汗,這個(gè)可能通過socket.io自帶的disconnect事件完成,當(dāng)一個(gè)用戶斷開連接...
    Www劉閱讀 540評(píng)論 1 9
  • 源碼&演示在線演示 (heroku服務(wù)器網(wǎng)速略慢且免費(fèi)套餐是小水管蝴簇,建議下載代碼本地運(yùn)行)源碼可訪問項(xiàng)目的GitH...
    Www劉閱讀 786評(píng)論 1 10
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,085評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理杯活,服務(wù)發(fā)現(xiàn),斷路器熬词,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • 從師傅師母簡(jiǎn)單而又親密的舉止間旁钧,我看到了一言一行背后的溫暖與愛意。 看完雨后的殘荷互拾,師傅不無動(dòng)情的說歪今,天氣好的時(shí)候...
    若潯wfsindy閱讀 202評(píng)論 0 0