亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

基于Node.js + WebSocket打造即時聊天程序嗨聊

2019-11-19 18:47:10
字體:
來源:轉載
供稿:網友

前端一直是一塊充滿驚喜的土地,不僅是那些富有創造性的頁面,還有那些驚贊的效果及不斷推出的新技術。像node.js這樣的后端開拓者直接將前端人員的能力擴大到了后端。瞬間就有了一統天下的感覺,來往穿梭于前后端之間代碼敲得飛起,從此由前端晉升為'前后端'。

本文將使用Node.js加web socket協議打造一個網頁即時聊天程序,取名為HiChat,中文翻過來就是'嗨聊',聽中文名有點像是專為寂寞單身男女打造的~

其中將會使用到express和socket.io兩個包模塊,下面會有介紹。

源碼

源碼可在頁面最后下載

本地運行方法:

  • 命令行運行npm install
  • 模塊下載成功后,運行node server啟動服務器
  • 打開瀏覽器訪問localhost

下圖為效果預覽:

準備工作

本文示例環境為Windows,Linux也就Node的安裝與命令行稍有區別,程序實現部分基本與平臺無關。

Node相關

  • 你需要在本機安裝Node.js(廢話)
  • 多少需要一點Node.js的基礎知識,如果還未曾了解過Node.js,這里有一篇不錯的入門教程

然后我們就可以開始創建一個簡單的HTTP服務器啦。

類似下面非常簡單的代碼,它創建了一個HTTP服務器并監聽系統的80端口。

//node server example//引入http模塊var http = require('http'),  //創建一個服務器  server = http.createServer(function(req, res) {    res.writeHead(200, {      'Content-Type': 'text/plain'    });    res.write('hello world!');    res.end();  });//監聽80端口server.listen(80);console.log('server started');


將其保存為一個js文件比如server.js,然后從命令行運行node server或者node server.js,服務器便可啟動了,此刻我們可以在瀏覽器地址欄輸入localhost進行訪問,也可以輸入本機IP127.0.0.1,都不用加端口,因為我們服務器監聽的是默認的80端口。當然,如果你機子上面80端口被其他程序占用了,可以選擇其他端口比如8080,這樣訪問的時候需要顯示地加上端口號localhost:8080。 

Express

首先通過npm進行安裝

  • 在我們的項目文件夾下打開命令行(tip: 按住Shift同時右擊,可以在右鍵菜單中找到'從此處打開命令行'選項)
  • 在命令行中輸入 npm install express 回車進行安裝
  • 然后在server.js中通過require('express')將其引入到項目中進行使用

express是node.js中管理路由響應請求的模塊,根據請求的URL返回相應的HTML頁面。這里我們使用一個事先寫好的靜態頁面返回給客戶端,只需使用express指定要返回的頁面的路徑即可。如果不用這個包,我們需要將HTML代碼與后臺JavaScript代碼寫在一起進行請求的響應,不太方便。

//返回一個簡單的HTML內容server = http.createServer(function(req, res) {  res.writeHead(200, {    'Content-Type': 'text/html' //將返回類型由text/plain改為text/html  });  res.write('<h1>hello world!</h1>'); //返回HTML標簽  res.end();});

在存放上一步創建的server.js文件的地方,我們新建一個文件夾名字為www用來存放我們的網頁文件,包括圖片以及前端的js文件等。假設已經在www文件夾下寫好了一個index.html文件(將在下一步介紹,這一步你可以放一個空的HTML文件),則可以通過以下方式使用express將該頁面返回到瀏覽器??梢钥吹捷^最開始,我們的服務器代碼簡潔了不少。

//使用express模塊返回靜態頁面var express = require('express'), //引入express模塊  app = express(),  server = require('http').createServer(app);app.use('/', express.static(__dirname + '/www')); //指定靜態HTML文件的位置server.listen(80);


其中有四個按鈕,分別是設置字體顏色,發送表情,發送圖片和清除記錄,將會在下面介紹其實現

socket.io

Node.js中使用socket的一個包。使用它可以很方便地建立服務器到客戶端的sockets連接,發送事件與接收特定事件。

同樣通過npm進行安裝 npm install socket.io 。安裝后在node_modules文件夾下新生成了一個socket.io文件夾,其中我們可以找到一個socket.io.js文件。將它引入到HTML頁面,這樣我們就可以在前端使用socket.io與服務器進行通信了。

<script src="/socket.io/socket.io.js"></script>

同時服務器端的server.js里跟使用express一樣,也要通過require('socket.io')將其引入到項目中,這樣就可以在服務器端使用socket.io了。

使用socket.io,其前后端句法是一致的,即通過socket.emit()來激發一個事件,通過socket.on()來偵聽和處理對應事件。這兩個事件通過傳遞的參數進行通信。具體工作模式可以看下面這個示例。

比如我們在index.html里面有如下JavaScript代碼(假設你已經在頁面放了一個ID為sendBtn的按鈕):

<script type="text/javascript"> var socket=io.connect(),//與服務器進行連接 button=document.getElementById('sendBtn'); button.onclick=function(){ socket.emit('foo', 'hello');//發送一個名為foo的事件,并且傳遞一個字符串數據‘hello' }</script>

上述代碼首先建立與服務器的連接,然后得到一個socket實例。之后如果頁面上面一個ID為sendBtn的按鈕被點擊的話,我們就通過這個socket實例發起一個名為foo的事件,同時傳遞一個hello字符串信息到服務器。

與此同時,我們需要在服務器端寫相應的代碼來處理這個foo事件并接收傳遞來的數據。

為此,我們在server.js中可以這樣寫:

//服務器及頁面響應部分var express = require('express'),  app = express(),  server = require('http').createServer(app),  io = require('socket.io').listen(server); //引入socket.io模塊并綁定到服務器app.use('/', express.static(__dirname + '/www'));server.listen(80);//socket部分io.on('connection', function(socket) {  //接收并處理客戶端發送的foo事件  socket.on('foo', function(data) {    //將消息輸出到控制臺    console.log(data);  })});

現在Ctrl+C關閉之前啟動的服務器,再次輸入node server啟動服務器運行新代碼查看效果,一切正常的話你會在點擊了頁面的按扭后,在命令行窗口里看到輸出的'hello'字符串。

一如之前所說,socket.io在前后端的句法是一致的,所以相反地,從服務器發送事件到客戶端,在客戶端接收并處理消息也是顯而易見的事件了。這里只是簡單介紹,具體下面會通過發送聊天消息進一步介紹。

基本頁面

有了上面一些基礎的了解,下面可以進入聊天程序功能的開發了。

首先我們構建主頁面。因為是比較大眾化的應用了,界面不用多想,腦海中已經有大致的雛形,它有一個呈現消息的主窗體,還有一個輸入消息的文本框,同時需要一個發送消息的按鈕,這三個是必備的。

另外就是,這里還準備實現以下四個功能,所以界面上還有設置字體顏色,發送表情,發送圖片和清除記錄四個按鈕。

最后的頁面也就是先前截圖展示的那們,而代碼如下:

www/index.html

<!doctype html><html>  <head>    <meta charset="utf-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">    <meta name="author" content="Wayou">    <meta name="description" content="hichat | a simple chat application built with node.js and websocket">    <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" />        </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>

樣式文件 www/styles/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;}#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;}

為了讓項目有一個良好的目錄結構便于管理,這里在www文件夾下又新建了一個styles文件夾存放樣式文件main.css,然后新建一個scripts文件夾存放前端需要使用的js文件比如hichat.js(我們前端所有的js代碼會放在這個文件中),而我們的服務器js文件server.js位置不變還是放在最外層。

同時再新建一個content文件夾用于存放其他資源比如圖片等,其中content文件夾里再建一個emoji文件夾用于存入表情gif圖,后面會用到。最后我們項目的目錄結構應該是這樣的了:

├─node_modules└─www  ├─content  │ └─emoji  ├─scripts  └─styles

此刻打開頁面你看到的是一個淡黑色的遮罩層,而接下來我們要實現的是用戶昵稱的輸入與服務器登入。這個遮罩層用于顯示連接到服務器的狀態信息,而當連接完成之后,會出現一個輸入框用于昵稱輸入。

上面HTML代碼里已經看到,我們將www/scripts/hichat.js文件已經引入到頁面了,下面開始寫一些基本的前端js開始實現連接功能。

定義一個全局變量用于我們整個程序的開發HiChat,同時使用window.onload在頁面準備好之后實例化HiChat,調用其init方法運行我們的程序。

www/scripts/Hichat.js

window.onload = function() {  //實例并初始化我們的hichat程序  var hichat = new HiChat();  hichat.init();};//定義我們的hichat類var HiChat = function() {  this.socket = null;};//向原型添加業務方法HiChat.prototype = {  init: function() {//此方法初始化程序    var that = this;    //建立到服務器的socket連接    this.socket = io.connect();    //監聽socket的connect事件,此事件表示連接已經建立    this.socket.on('connect', function() {      //連接到服務器后,顯示昵稱輸入框      document.getElementById('info').textContent = 'get yourself a nickname :)';      document.getElementById('nickWrapper').style.display = 'block';      document.getElementById('nicknameInput').focus();    });  }};

 上面的代碼定義了整個程序需要使用的類HiChat,之后我們處理消息顯示消息等所有業務邏輯均寫在這個類里面。

首先定義了一個程序的初始化方法,這里面初始化socket,監聽連接事件,一旦連接到服務器,便顯示昵稱輸入框。當用戶輸入昵稱后,便可以在服務器后臺接收到然后進行下一步的處理了。

 設置昵稱

我們要求連接的用戶需要首先設置一個昵稱,且這個昵稱還要唯一,也就是不能與別人同名。一是方便用戶區分,二是為了統計在線人數,同時也方便維護一個保存所有用戶昵稱的數組。

為此在后臺server.js中,我們創建一個名叫users的全局數組變量,當一個用戶設置好昵稱發送到服務器的時候,將昵稱壓入users數組。同時注意,如果用戶斷線離開了,也要相應地從users數組中移除以保證數據的正確性。

在前臺,輸入昵稱點擊OK提交后,我們需要發起一個設置昵稱的事件以便服務器偵聽到。將以下代碼添加到之前的init方法中。

www/scripts/hichat.js

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

 server.js

//服務器及頁面部分var express = require('express'),  app = express(),  server = require('http').createServer(app),  io = require('socket.io').listen(server),  users=[];//保存所有在線用戶的昵稱app.use('/', express.static(__dirname + '/www'));server.listen(80);//socket部分io.on('connection', function(socket) {  //昵稱設置  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); //向所有連接到服務器的客戶端發送當前登陸用戶的昵稱     };  });});

 需要解釋一下的是,在connection事件的回調函數中,socket表示的是當前連接到服務器的那個客戶端。所以代碼socket.emit('foo')則只有自己收得到這個事件,而socket.broadcast.emit('foo')則表示向除自己外的所有人發送該事件,另外,上面代碼中,io表示服務器整個socket連接,所以代碼io.sockets.emit('foo')表示所有人都可以收到該事件。

上面代碼先判斷接收到的昵稱是否已經存在在users中,如果存在,則向自己發送一個nickExisted事件,在前端接收到這個事件后我們顯示一條信息通知用戶。

將下面代碼添加到hichat.js的inti方法中。

www/scripts/hichat.js

this.socket.on('nickExisted', function() {   document.getElementById('info').textContent = '!nickname is taken, choose another pls'; //顯示昵稱被占用的提示 });

如果昵稱沒有被其他用戶占用,則將這個昵稱壓入users數組,同時將其作為一個屬性存到當前socket變量中,并且將這個用戶在數組中的索引(因為是數組最后一個元素,所以索引就是數組的長度users.length)也作為屬性保存到socket中,后面會用到。最后向自己發送一個loginSuccess事件,通知前端登陸成功,前端接收到這個成功消息后將灰色遮罩層移除顯示聊天界面。

將下面代碼添加到hichat.js的inti方法中。

www/scripts/hichat.js

this.socket.on('loginSuccess', function() {   document.title = 'hichat | ' + document.getElementById('nicknameInput').value;   document.getElementById('loginWrapper').style.display = 'none';//隱藏遮罩層顯聊天界面   document.getElementById('messageInput').focus();//讓消息輸入框獲得焦點 });

在線統計

這里實現顯示在線用戶數及在聊天主界面中以系統身份顯示用戶連接離開等信息。

上面server.js中除了loginSuccess事件,后面還有一句代碼,通過io.sockets.emit 向所有用戶發送了一個system事件,傳遞了剛登入用戶的昵稱,所有人接收到這個事件后,會在聊天窗口顯示一條系統消息'某某加入了聊天室'。同時考慮到在前端我們無法得知用戶是進入還是離開,所以在這個system事件里我們多傳遞一個數據來表明用戶是進入還是離開。

將server.js中login事件更改如下:

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');   }; });

較之前,多傳遞了一個login字符串。

同時再添加一個用戶離開的事件,這個可能通過socket.io自帶的disconnect事件完成,當一個用戶斷開連接,disconnect事件就會觸發。在這個事件中,做兩件事情,一是將用戶從users數組中刪除,一是發送一個system事件通知所有人'某某離開了聊天室'。

將以下代碼添加到server.js中connection的回調函數中。

server.js

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

上面代碼通過JavaScript數組的splice方法將當前斷開連接的用戶從users數組中刪除,這里我們看到之前保存的用戶索引被使用了。同時發送和用戶連接時一樣的system事件通知所有人'某某離開了',為了讓前端知道是離開事件,所以發送了一個'logout'字符串。

下面開始前端的實現,也就是接收system事件。

在hichat.js中,將以下代碼添加到init方法中。

www/scripts/hichat.js

this.socket.on('system', function(nickName, userCount, type) {   //判斷用戶是連接還是離開以顯示不同的信息   var msg = nickName + (type == 'login' ? ' joined' : ' left');   var p = document.createElement('p');   p.textContent = msg;   document.getElementById('historyMsg').appendChild(p);   //將在線人數顯示到頁面頂部   document.getElementById('status').textContent = userCount + (userCount > 1 ? ' users' : ' user') + ' online'; });

現在運行程序,打開多個瀏覽器標簽,然后登陸離開,你就可以看到相應的系統提示消息了。

發送消息

用戶連接以及斷開我們需要顯示系統消息,用戶還要頻繁的發送聊天消息,所以可以考慮將消息顯示到頁面這個功能單獨寫一個函數方便我們調用。為此我們向HiChat類中添加一個_displayNewMsg的方法,它接收要顯示的消息,消息來自誰,以及一個顏色共三個參數。因為我們想系統消息區別于普通用戶的消息,所以增加一個顏色參數。同時這個參數也方便我們之后實現讓用戶自定義文本顏色做準備。

將以下代碼添加到的我的HiChat類當中。

www/scripts/hichat.js

//向原型添加業務方法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方法中,我們還向消息添加了一個日期。我們也判斷了該方法在調用時有沒有傳遞顏色參數,沒有傳遞顏色的話默認使用#000即黑色。

同時修改我們在system事件中顯示系統消息的代碼,讓它調用這個_displayNewMsg方法。

www/scripts/hichat.js

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';});

 現在的效果如下:

有了這個顯示消息的方法后,下面就開始實現用戶之間的聊天功能了。

做法也很簡單,如果你掌握了上面所描述的emit發送事件,on接收事件,那么用戶聊天消息的發送接收也就輕車熟路了。

首先為頁面的發送按鈕寫一個click事件處理程序,我們通過addEventListner來監聽這個click事件,當用戶點擊發送的時候,先檢查輸入框是否為空,如果不為空,則向服務器發送postMsg事件,將用戶輸入的聊天文本發送到服務器,由服務器接收并分發到除自己外的所有用戶。

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

www/scripts/hichat.js

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); //把消息發送到服務器    that._displayNewMsg('me', msg); //把自己的消息顯示到自己的窗口中  };}, false);

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

server.js

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

然后在客戶端接收服務器發送的newMsg事件,并將聊天消息顯示到頁面。

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

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

運行程序,現在可以發送聊天消息了。

 發送圖片

上面已經實現了基本的聊天功能了,進一步,如果我們還想讓用戶可以發送圖片,那程序便更加完美了。

圖片不同于文字,但通過將圖片轉化為字符串形式后,便可以像發送普通文本消息一樣發送圖片了,只是在顯示的時候將它還原為圖片。

在這之前,我們已經將圖片按鈕在頁面放好了,其實是一個文件類型的input,下面只需在它身上做功夫便可。

用戶點擊圖片按鈕后,彈出文件選擇窗口供用戶選擇圖片。之后我們可以在JavaScript代碼中使用FileReader來將圖片讀取為base64格式的字符串形式進行發送。而base64格式的圖片直接可以指定為圖片的src,這樣就可以將圖片用img標簽顯示在頁面了。

為此我們監聽圖片按鈕的change事件,一但用戶選擇了圖片,便顯示到自己的屏幕上同時讀取為文本發送到服務器。

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

www/scripts/hichat.js

document.getElementById('sendImage').addEventListener('change', function() {  //檢查是否有文件被選中   if (this.files.length != 0) {    //獲取文件并用FileReader進行讀取     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) {      //讀取成功,顯示到頁面并發送到服務器       this.value = '';       that.socket.emit('img', e.target.result);       that._displayImage('me', e.target.result);     };     reader.readAsDataURL(file);   }; }, false);

 上面圖片讀取成功后,調用_displayNImage方法將圖片顯示在自己的屏幕同時向服務器發送了一個img事件,在server.js中,我們通過這個事件來接收并分發圖片到每個用戶。同時也意味著我們還要在前端寫相應的代碼來接收。

這個_displayNImage還沒有實現,將會在下面介紹。

將以下代碼添加到server.js的socket回調函數中。

server.js

//接收用戶發來的圖片 socket.on('img', function(imgData) {  //通過一個newImg事件分發到除自己外的每個用戶   socket.broadcast.emit('newImg', socket.nickname, imgData); }); 同時向hichat.js的init方法添加以下代碼以接收顯示圖片。 this.socket.on('newImg', function(user, img) {   that._displayImage(user, img); });

有個問題就是如果圖片過大,會破壞整個窗口的布局,或者會出現水平滾動條,所以我們對圖片進行樣式上的設置讓它最多只能以聊天窗口的99%寬度來顯示,這樣過大的圖片就會自己縮小了。

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

但考慮到縮小后的圖片有可能失真,用戶看不清,我們需要提供一個方法讓用戶可以查看原尺寸大小的圖片,所以將圖片用一個鏈接進行包裹,當點擊圖片的時候我們打開一個新的窗口頁面,并將圖片按原始大小呈現到這個新頁面中讓用戶查看。

所以最后我們實現的_displayNImage方法應該是這樣的。

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

www/scripts/hichat.js

_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"><img src="' + imgData + '"/></a>';  container.appendChild(msgToDisplay);  container.scrollTop = container.scrollHeight;}

再次啟動服務器打開程序,我們可以發送圖片了。

發送表情

文字總是很難表達出說話時的面部表情的,于是表情就誕生了。

前面已經介紹過如何發送圖片了,嚴格來說,表情也是圖片,但它有特殊之處,因為表情可以穿插在文字中一并發送,所以就不能像處理圖片那樣來處理表情了。

根據以往的經驗,其他聊天程序是把表情轉為符號,比如我想發笑臉,并且規定':)'這個符號代碼笑臉表情,然后數據傳輸過程中其實轉輸的是一個冒號加右括號的組合,當每個客戶端接收到消息后,從文字當中將這些表情符號提取出來,再用gif圖片替換,這樣呈現到頁面我們就 看到了表情加文字的混排了。

你好,王尼瑪[emoji:23]------>你好,王尼瑪

上面形象地展示了我們程序中表情的使用,可以看出我規定了一種格式來代表表情,[emoji:xx],中括號括起來然后'emoji'加個冒號,后面跟一個數字,這個數字表示某個gif圖片的編號。程序中,如果我們點擊表情按扭,然后呈現所有可用的表情圖片,當用戶選擇一個表情后,生成對應的代碼插入到當前待發送的文字消息中。發出去后,每個人接收到的也是代碼形式的消息,只是在將消息顯示到頁面前,我們將表情代碼提取出來,獲取圖片編號,然后用相應的圖片替換。

首先得將所有可用的表情圖片顯示到一個小窗口,這個窗口會在點擊了表情按鈕后顯示如下圖,在HTML代碼中已經添加好了這個窗口了,下面只需實現代碼部分。

我們使用兔斯基作為我們聊天程序的表情包??梢钥吹剑泻芏鄰坓if圖,如果手動編寫的話,要花一些功夫,不斷地寫<img src='xx.gif'/>,所以考慮將這個工作交給代碼來自動完成,寫一個方法來初始化所有表情。

為此將以下代碼添加到HiChat類中,并在init方法中調用這個方法。

www/scripts/hichat.js

_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);}

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

www/scripts/hichat.js

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';   }; });

上面向頁面添加了兩個單擊事件,一是表情按鈕單擊顯示表情窗口,二是點擊頁面其他地方關閉表情窗口。

現在要做的就是,具體到某個表情被選中后,需要獲取被選中的表情,然后轉換為相應的表情代碼插入到消息框中。

為此我們再寫一個這些圖片的click事件處理程序。將以下代碼添加到hichat.js的inti方法中。

www/scripts/hichat.js

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);

現在表情選中后,消息輸入框中可以得到相應的代碼了。

之后的發送也普通消息發送沒區別,因為之前已經實現了文本消息的發送了,所以這里不用再實現什么,只是需要更改一下之前我們用來顯示消息的代碼,首先判斷消息文本中是否含有表情符號,如果有,則轉換為圖片,最后再顯示到頁面。

為此我們寫一個方法接收文本消息為參數,用正則搜索其中的表情符號,將其替換為img標簽,最后返回處理好的文本消息。

將以下代碼添加到HiChat類中。

www/scripts/hichat.js

_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], '<img class="emoji" src="../content/emoji/' + emojiIndex + '.gif" />');    };  };  return result;}

 現在去修改之前我們顯示消息的_displayNewMsg方法,讓它在顯示消息之前調用這個_showEmoji方法。

_displayNewMsg: function(user, msg, color) {   var container = document.getElementById('historyMsg'),     msgToDisplay = document.createElement('p'),     date = new Date().toTimeString().substr(0, 8),     //將消息中的表情轉換為圖片     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; }

下面是實現后的效果:

 主要功能已經完成得差不多了,為了讓程序更加人性與美觀,可以加入一個修改文字顏色的功能,以及鍵盤快捷鍵操作的支持,這也是一般聊天程序都有的功能,回車即可以發送消息。

文字顏色

萬幸,HTML5新增了一個專門用于顏色選取的input標簽,并且Chrome對它的支持非常之贊,直接彈出系統的顏色拾取窗口。

 

IE及FF中均是一個普通的文本框,不過不影響使用,只是用戶只能通過輸入具體的顏色值來進行顏色設置,沒有Chrome里面那么方便也直觀。

之前我們的_displayNewMsg方法可以接收一個color參數,現在要做的就是每次發送消息到服務器的時候,多加一個color參數就可以了,同時,在顯示消息時調用_displayNewMsg的時候將這個color傳遞過去。

下面是修改hichat.js中消息發送按鈕代碼的示例:

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);  };}, false);

 同時修改hichat.js中接收消息的代碼,讓它接收顏色值

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

這只是展示了發送按鈕的修改,改動非常小,只是每次消息發送時獲取一下顏色值,同時emit事件到服務器的時候也帶上這個顏色值,這樣前端在顯示時就可以根據這個顏色值為每個不兩只用戶顯示他們自己設置的顏色了。剩下的就是按相同的做法把發送圖片時也加上顏色,這里省略。

最后效果:

 按鍵操作

將以下代碼添加到hichat.js的inti方法中,這樣在輸入昵稱后,按回車鍵就可以登陸,進入聊天界面后,回車鍵可以發送消息。

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('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);

部署上線

最后一步,當然就是將我們的辛勤結晶部署到實際的站點。這應該是最激動人心也是如釋重負的一刻。但在這之前,讓我們先添加一個node.js程序通用的package.json文件,該文件里面可以指定我們的程序使用了哪些模塊,這樣別人在獲取到代碼后,只需通過npm install命令就可以自己下載安裝程序中需要的模塊了,而不用我們把模塊隨源碼一起發布。

添加package.json文件

將以下代碼保存為package.json保存到跟server.js相同的位置。

{  "name": "hichat",  "description": "a realtime chat web application",  "version": "0.4.0",  "main": "server.js",  "dependencies": {    "express": "3.4.x",    "socket.io": "0.9.x"  },  "engines": {    "node": "0.10.x",    "npm": "1.2.x"  }} 

云服務選擇與部署

首先我們得選擇一個支持Node.js同時又支持web socket協議的云服務器。因為只是用于測試,空間內存限制什么的都無所謂,只要免費就行。Node.js在GitHub的Wiki頁面上列出了眾多支持Node.js環境的云服務器,選來選去滿足條件的只有heroku。

如果你之前到heroku部署過相關Node程序的話,一定知道其麻煩之處,并且出錯了非常不容易調試。不過當我在寫這篇博客的時候,我發現了一個利器codeship,將它與你的github綁定之后,你每次提交了新的代碼它會自動部署到heroku上面。什么都不用做!

代碼更新,環境設置,編譯部署,全部自動搞定,并且提供了詳細的log信息及各步驟的狀態信息。使用方法也是很簡單,注冊后按提示,兩三步搞定,鑒于本文已經夠長了,應該創紀錄了,這里就不多說了。

 

已知問題

部署測試后,發現一些本地未出現的問題,主要有以下幾點:

  • 首次連接過慢,有時會失敗出現503錯誤,這個查了下heroku文檔,官方表示程序首次接入時受資源限制確實會很慢的,這就是用免費套餐注定被鄙視的結果,不過用于線上測試這點還是能夠忍受的
  • 發送表情時,Chrome會向服務器重新請求已經下載到客戶端的gif圖片,而IE和FF都無此問題,導致在Chrome里表情會有延遲,進而出現聊天主信息窗口滾動也不及時的現象
  • 用戶未活動一定時間后會與服務器失連,socket自動斷開,不知道是socket.io內部機制還是又是heroku搗鬼

總結展望

經過上面一番折騰,一個基本的聊天程序便打造完畢??梢酝晟频牡胤竭€有許多,比如利用CSS3的動畫,完全可以制作出窗口抖動功能的。聽起來很不錯是吧。同時利用HTML5的Audio API,要實現類似微信的語音消息也不是不可能的,夠震撼吧。甚至還有Geolocaiton API我們就可以聯想到實現同城功能,利用Webcam可以打造出視頻對聊,但這方面WebRTC已經做得很出色了。

PS:做程序員之前有兩個想法,一是寫個播放器,一是寫個聊天程序,現在圓滿了。

源碼下載:demo

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产91对白在线播放| 日韩福利在线播放| 国产精品一区二区久久国产| 欧美精品在线免费观看| 7777精品久久久久久| 欧美激情视频在线| 亚洲自拍另类欧美丝袜| 欧美性猛交xxxxx免费看| 日韩高清av一区二区三区| 91av中文字幕| 午夜精品一区二区三区在线播放| 日韩精品视频观看| 亚洲一区二区少妇| 国产成人综合一区二区三区| 欧美专区国产专区| 国产精品久久久久久久电影| 亚洲第一色中文字幕| 亚洲影院在线看| 91精品国产自产在线观看永久| 日韩有码在线视频| 亚洲国产天堂久久综合| 日韩专区在线播放| 国产日韩精品综合网站| 一本一本久久a久久精品牛牛影视| www.日韩不卡电影av| 国产丝袜一区二区三区| 色偷偷亚洲男人天堂| 日韩美女在线看| 欧美激情亚洲自拍| 国产精品日韩欧美| 欧美成人午夜视频| 最新国产精品拍自在线播放| 成人国产精品一区| 久久成人18免费网站| 国产精品久久电影观看| 国模视频一区二区三区| 26uuu久久噜噜噜噜| 亚洲精品理论电影| 国产精品1区2区在线观看| 国产精品免费视频久久久| 久久伊人精品一区二区三区| 国产精品狠色婷| 精品视频中文字幕| 亚洲男人天堂视频| 国产高清在线不卡| 97精品久久久中文字幕免费| 欧美激情亚洲精品| 亚洲国产精品yw在线观看| 欧美日韩性视频| 欧美日韩国产丝袜美女| 日本一区二三区好的精华液| 国产91色在线|免| 国产精品视频最多的网站| 欧美一级淫片videoshd| 亚洲精品自产拍| 亚洲人成77777在线观看网| 日韩一区二区三区xxxx| 国产精品入口免费视频一| 国产精品第3页| 91中文字幕在线观看| 91九色视频在线| 久久精品国产v日韩v亚洲| 欧美精品久久久久久久久久| 原创国产精品91| 精品美女国产在线| 久久免费视频在线观看| 亚洲精品456在线播放狼人| 亚洲www在线观看| 777777777亚洲妇女| 欧美大奶子在线| 美女av一区二区| 国产精品久久一| 亚洲高清免费观看高清完整版| 欧美精品精品精品精品免费| 国产精品自在线| 一本色道久久88综合日韩精品| 午夜精品在线视频| 全色精品综合影院| 午夜精品美女自拍福到在线| 国产精品自拍小视频| 久久久久久久久久亚洲| 97成人精品区在线播放| 91日韩在线视频| 国产91精品黑色丝袜高跟鞋| 国产精品美女www| 日韩精品中文字幕视频在线| 亚洲国产99精品国自产| 国模gogo一区二区大胆私拍| 亚洲精品v欧美精品v日韩精品| 久久国产精品亚洲| 亚洲人成电影网| 精品国产乱码久久久久久天美| 伊人久久综合97精品| 性色av一区二区三区在线观看| 久久精品色欧美aⅴ一区二区| 久久久精品日本| 亚洲aa在线观看| 久久五月天色综合| 久久久av一区| 日韩精品欧美激情| 欧美视频中文字幕在线| 色中色综合影院手机版在线观看| 亚洲欧美激情四射在线日| 欧美激情精品久久久久久| 精品国产户外野外| 国产成人av在线| 欧美一区三区三区高中清蜜桃| 色婷婷综合久久久久中文字幕1| 最近的2019中文字幕免费一页| 亚洲欧美激情四射在线日| 亚洲第一网站男人都懂| 亚洲欧美日韩国产中文专区| 中文字幕精品国产| 日韩亚洲欧美中文在线| 亚洲欧美日韩一区二区在线| 国产精品成人品| 国产亚洲美女久久| 最近2019中文字幕大全第二页| 亚洲午夜av电影| 日本久久精品视频| 欧美电影免费看| 国产噜噜噜噜噜久久久久久久久| 欧美性黄网官网| 国产精品成人免费电影| 欧美激情二区三区| 国产精品美乳在线观看| 中文字幕久久精品| 91在线视频免费| 久久欧美在线电影| 欧美xxxwww| 国产欧美精品一区二区| 2019中文字幕全在线观看| 国产一区二区三区18| 亚洲一区二区三区久久| 国产精品白丝jk喷水视频一区| 欧美精品一区二区三区国产精品| 欧美日韩在线观看视频| 久久久国产精彩视频美女艺术照福利| 国产精品电影在线观看| 久热99视频在线观看| 国产精品自产拍在线观看中文| 在线a欧美视频| 欧洲亚洲妇女av| 亚洲成人黄色网址| 91久久综合亚洲鲁鲁五月天| 成人精品久久久| 久久久免费精品视频| 亚洲aa中文字幕| 久久久久久久久久久免费| 69影院欧美专区视频| 深夜福利日韩在线看| 91中文在线观看| 国产在线98福利播放视频| 国产69精品久久久久9| 欧美激情免费视频| 中文字幕亚洲欧美一区二区三区| 日韩av在线高清| 亚洲黄在线观看| 美女精品久久久| 亚洲欧美综合区自拍另类| 国产精品福利网站| 欧美精品日韩三级| 国产91在线播放精品91| 欧美性猛xxx|