Node.js + express + socket 实现在线实时多人聊天室

来源:互联网 发布:邓肯数据 编辑:程序博客网 时间:2024/05/21 09:27

项目目录结构:




前端部分:


登录页面Login部分:


login.html


<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>login</title>    <link rel="stylesheet" href="css/login.css"></head><body>    <div class="login-box flex-box">        <!--登录标题栏-->        <h2 class="sign-title box-width">LOGIN</h2>        <!--头像栏-->        <div class="picture-carousel">            <div class="arrow left-arrow">                <div class="before-arrow"></div>            </div>            <img class="p1 img-setting" src="img/1.png" alt="1.png">            <img class="p2 img-setting" src="img/2.png" alt="2.png">            <img class="p3 img-setting" src="img/3.png" alt="3.png">            <img class="p2 img-setting" src="img/4.png" alt="4.png">            <img class="p1 img-setting" src="img/5.png" alt="5.png">            <div class="arrow right-arrow">                <div class="after-arrow"></div>            </div>        </div>        <!--用户名栏-->        <div class="name-box box-width">            <input type="text" class="user-name box-width" placeholder="Please Type Your Name">        </div>        <!--确认栏-->        <div class="button-box box-width">            <input type="button" class="login-button box-width" value="Login The Chatroom">        </div>        <!--错误信息栏-->        <div class="error-box box-width">            <span class="error-message">Welcome to chatroom!</span>        </div>    </div></body><script src="js/login.js"></script></html>

login.css

* {    padding: 0;    margin: 0;    font-family: "Microsoft Yahei";}html,body {    width: 100%;    height: 100%;    font-family: "Microsoft Yahei";    display: flex;    justify-content: center;    align-items: center;}body {    background: linear-gradient(-135deg, #51D15B, #42A855);    background: -moz-linear-gradient(-135deg, #51D15B, #42A855);    background: -webkit-linear-gradient(-135deg, #51D15B, #42A855);    background: -o-linear-gradient(-135deg, #51D15B, #42A855);}.flex-box {    display: flex;    justify-content: center;    align-items: center;}.box-width {    width: 80%;}/*最外层*/.login-box {    width: 20%;    min-width: 304px;    max-width: 404px;    height: 50%;    min-height: 368px;    max-height: 468px;    flex-direction: column;    box-shadow: 1px 1px 15px #7B8C99;    background: #fff;}/*LOGIN标题*/.sign-title {    color: #42A855;    border: 2px solid #42A855;    border-top: transparent;    border-left: transparent;    border-right: transparent;}/*图片切换*/.picture-carousel {    position: relative;    display: flex;    margin: 10%;}/*图片切换箭头*/.arrow {    z-index: 3;    position: absolute;    font-size: 60px;    height: 100%;    width: 30%;    display: flex;    justify-content: center;    align-items: center;    color: #ffffff;}.arrow:hover {    cursor: pointer;}.left-arrow {    left: 0;}.before-arrow {    width: 0px;    height: 0px;    border-width: 30px;    border-style: solid;    border-color: transparent #51D15B transparent transparent;}.right-arrow {    right: 0;}.after-arrow{     width: 0px;    height: 0px;    border-width: 30px;    border-style: solid;    border-color: transparent  transparent transparent #51D15B; }.picture-carousel img {    width: 80px;    height: 80px;    transition: all 0.2s linear;    -moz-transition: all 0.2s ease-out;    -webkit-transition: all 0.2s ease-out;    -o-transition: all 0.2s ease-out;}.img-setting {    margin: 0px -15px;}.p1 {    transform: scale(0.6);    z-index: 1;}.p1:hover {    transform: scale(0.8);}.p2 {    transform: scale(0.8);    z-index: 2;}.p2:hover {    transform: scale(1);}.p3 {    transform: scale(1);    z-index: 3;}.p3:hover {    transform: scale(1.2);}/*用户名*/.name-box {    display: flex;    justify-content: center;    border: 1px solid #51D15B;}.name-box .user-name {    width: 100%;    text-align: center;    padding: 10px;    outline-color: #42A855;    border: none;    font-size: 16px;}/*登录按钮*/.button-box {    display: flex;    justify-content: center;    margin: 10px 0px 20px;}.button-box .login-button {    width: 100%;    padding: 10px 20px;    outline: none;    border: none;    background: #42A855;    color: white;    font-size: 16px;}/*错误信息*/.error-box {    color: #42A855;    border: 2px solid #42A855;    border-top: transparent;    border-left: transparent;    border-right: transparent;}.error-box span {    visibility: hidden;    color: #d43f3a;    font-size: 14px;}


login.js

//用于存储图片顺序var imgArray = ['1', '2', '3', '4', '5']; //获取箭头var leftArrow = document.getElementsByClassName('left-arrow')[0];var rightArrow = document.getElementsByClassName('right-arrow')[0];//获取用户名var userName = document.getElementsByClassName('user-name')[0];//获取登录按钮var loginButton = document.getElementsByClassName('login-button')[0];// 获取错误信息栏var errorMessage = document.getElementsByClassName('error-message')[0];// 添加左箭头监听事件leftArrow.addEventListener('click', function () {    imgArray.unshift(imgArray[imgArray.length - 1]); //把最后的元素放在第一位    imgArray.pop();    carouselImg();});// 添加右箭头监听事件rightArrow.addEventListener('click', function () {    imgArray.push(imgArray[0]); //把第一个元素放在最后    imgArray.shift();    carouselImg();});// 切换图片function carouselImg() {    for (var count = 0; count < imgArray.length; count++) {        document.getElementsByTagName('img')[count].src = 'img/' + imgArray[count] + '.png';        document.getElementsByTagName('img')[count].alt = imgArray[count] + '.png';    };};// 添加登录按钮监听事件loginButton.addEventListener('click', function () {    if (userName.value === '') {        errorMessage.innerHTML = 'Please Type You Name';        errorMessage.style.visibility = 'visible';    } else if (userName.value.length > 8) {        errorMessage.innerHTML = 'Your Name Cannot Over 8 Words';        errorMessage.style.visibility = 'visible';    } else {        window.location.href =            encodeURI('index.html?selectpicture=' + document.getElementsByClassName('p3')[0].alt +                '&username=' + userName.value);    }});// Enter按键绑定登录事件document.onkeydown = function (event) {    var e = event || window.event;    if (e && e.keyCode == 13) {        loginButton.click();    }};

效果图



聊天页面index部分:


index.html


<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>chat-room</title>    <link rel="stylesheet" href="css/index.css"></head><body>    <div class="chat-box">        <!--聊天框头部-->        <div class="chat-header">            <div class="button-box">                <input type="button" class="log-out" value="LOGOUT">            </div>        </div>        <!--聊天框主体-->        <div class="chat-body">            <!--聊天框左侧-->            <div class="chat-body-left">                <!--聊天框左侧聊天内容-->                <div class="chat-content"></div>                <!--聊天框左侧聊天输入框-->                <div class="chat-edit">                    <input type="text" class="edit-box" placeholder="Please Type You Message" maxlength="15">                     <input type="button" class="edit-button" value="SEND">                </div>            </div>            <!--聊天框右侧-->            <div class="chat-body-right">                <!--聊天框右侧统计人数-->                <div class="online-count">Online:0</div>                <!--聊天框右侧用户名-->                <div class="user-name">user-name</div>                <!--聊天框右侧头像-->                <img class="user-img" />            </div>        </div>    </div></body><script src="js/socket.io.js"></script><script src="js/index.js"></script></html>


index.css


* {    margin: 0;    padding: 0;    font-family: "Mircrosoft Yahei";    /*border: 1px solid black;*/}html,body {    width: 100%;    height: 100%;}/*背景色*/body {    display: flex;    justify-content: center;    align-items: center;    background: linear-gradient(-135deg, #51D15B, #42A855);    background: -moz-linear-gradient(-135deg, #51D15B, #42A855);    background: -webkit-linear-gradient(-135deg, #51D15B, #42A855);    background: -o-linear-gradient(-135deg, #51D15B, #42A855);}/*最外层*/.chat-box {    width: 50%;    max-width: 720px;    min-width: 400px;    height: 80%;    min-height: 530px;    max-height: 530px;    display: flex;    flex-direction: column;    background: #fff;    box-shadow: 1px 1px 15px #333333;}/*头部*/.chat-header {    margin: 5px;    box-shadow: 1px 1px 15px #7B8C99;}.button-box {    display: flex;    justify-content: flex-end;}.log-out {    height: 100%;    font-size: 14px;    font-weight: bold;    padding: 5px 15px;    color: #79C2EA;    background: #fff;    outline: none;    border: none;    border-radius: 15px;    cursor: pointer;}/*主体*/.chat-body {    height: 90%;    display: flex;    flex-direction: row;    justify-content: space-around;    align-items: center;    margin: 5px;    padding: 5px;}/*主体左侧*/.chat-body-left {    height: 100%;    width: 70%;    display: flex;    flex-direction: column;    justify-content: space-around;    margin: 5px;}/*左侧内容*/.chat-content {    margin-bottom: 5px;    height: 100%;    box-shadow: 1px 1px 15px #7B8C99;    overflow: scroll;}/*聊天气泡*/.my-message-box {    display: flex;    justify-content: flex-end;    align-content: center;    margin: 5px;}.other-message-box {    display: flex;    justify-content: flex-start;    align-content: center;    margin: 5px;}.message-content {    display: flex;    justify-content: center;    align-content: center;    background-color: #51D15B;    padding: 5px 10px;    border-radius: 15px;    color: #fff;}.other-message-content {    display: flex;    justify-content: center;    align-content: center;    background-color: #79C2EA;    padding: 5px 10px;    border-radius: 15px;    color: #fff;}.message-content span {    padding: 20px 0px;}.other-message-content span {    padding: 20px 0px;}.message-arrow {    width: 0px;    height: 0px;    border-width: 8px;    border-style: solid;    border-color: transparent transparent transparent #51D15B;    align-self: center;}.other-message-arrow {    width: 0px;    height: 0px;    border-width: 8px;    border-style: solid;    border-color: transparent #79C2EA transparent transparent;    align-self: center;}.user-information {    display: flex;    flex-direction: column;    align-content: flex-end;}.other-user-information {    display: flex;    flex-direction: column;    align-content: flex-end;}.user-chat-img {    width: 50px;    height: 50px;}.user-chat-name {    color: #333333;    font-size: 16px;    text-align: center;}/*聊天输入框*/.chat-edit {    margin-top: 5px;    display: flex;    justify-content: space-between;    align-items: center;    box-shadow: 1px 1px 15px #7B8C99;    overflow: hidden;}/*聊天输入框输入区域*/.edit-box {    width: 80%;    height: 100%;    margin: 5px;    border: none;    outline: none;}/*聊天框输入按钮*/.edit-button {    height: 100%;    padding: 5px 15px;    background: #fff;    color: #79C2EA;    outline: none;    border: none;    border-radius: 15px;    cursor: pointer;    font-size: 14px;    font-weight: bold;}/*主体右侧*/.chat-body-right {    height: 100%;    width: 30%;    display: flex;    flex-direction: column;    justify-content: center;    align-items: center;    margin: 5px;    box-shadow: 1px 1px 15px #7B8C99;}/*右侧内容*/.user-name {    margin: 15px;    font-size: 18px;    font-weight: bold;    color: #79C2EA;}.user-img {    width: 100px;    height: 100px;    margin: 5px;}.online-count {    font-size: 18px;    font-weight: bold;    color: #79C2EA;}/*兼容小屏幕*/@media screen and (max-width: 420px) {    .chat-box {        width: 50%;        max-width: 720px;        min-width: 300px;        height: 80%;        min-height: 530px;        max-height: 530px;    }    .chat-body-left {        height: 100%;        width: 100%;        display: flex;        flex-direction: column;        justify-content: space-around;        margin: 5px;    }    .chat-body-right {        display: none;    }}


index.js


// 获取url里面的内容var url = decodeURI(location.href).split('?')[1].split('&');// 获取聊天内容框var chatContent = document.getElementsByClassName('chat-content')[0];// 获取聊天输入框var editBox = document.getElementsByClassName('edit-box')[0];// 获取聊天输入框发送按钮var editButton = document.getElementsByClassName('edit-button')[0];// 获取用户名栏var userName = document.getElementsByClassName('user-name')[0];// 获取在线人数栏var onlineCount = document.getElementsByClassName('online-count')[0];// 把登录页面的名称放在右侧userName.innerHTML = url[1].split('=')[1];var userImg = document.getElementsByClassName('user-img')[0];// 把登录页面的头像放在右侧userImg.src = 'img/' + url[0].split('=')[1];var logOut = document.getElementsByClassName('log-out')[0];// 发送按钮绑定点击事件editButton.addEventListener('click', sendMessage);// 登出按钮绑定点击事件logOut.addEventListener('click', closePage);// 绑定Enter键和发送事件document.onkeydown = function (event) {    var e = event || window.event;    if (e && e.keyCode === 13) {        if (editBox.value !== '') {            editButton.click();        }    }};// 关闭页面function closePage() {    var userAgent = navigator.userAgent;    if (userAgent.indexOf("Firefox") != -1 || userAgent.indexOf("Chrome") != -1) {        window.location.href = "about:blank";    } else {        window.opener = null;        window.open("", "_self");        window.close();    }}// socket部分var socket = io();// 当接收到消息并且不是本机时生成聊天气泡socket.on('message', function (information) {    if (information.name !== userName.textContent) {        createOtherMessage(information);    }});// 当接收到有人连接进来socket.on('connected', function (onlinecount) {    console.log(onlinecount);    onlineCount.innerHTML = 'Online:' + onlinecount;});// 当接收到有人断开后socket.on('disconnected', function (onlinecount) {    console.log(onlinecount);    onlineCount.innerHTML = 'Online:' + onlinecount;});// 发送本机的消息function sendMessage() {    if (editBox.value != '') {        var myInformation = {            name: userName.textContent,            chatContent: editBox.value,            img: userImg.src        };        socket.emit('message', myInformation);        createMyMessage();        editBox.value = '';    }};// 生成本机的聊天气泡function createMyMessage() {    var myMessageBox = document.createElement('div');    myMessageBox.className = 'my-message-box';    var messageContent = document.createElement('div');    messageContent.className = 'message-content';    var text = document.createElement('span');    text.innerHTML = editBox.value;    messageContent.appendChild(text);    myMessageBox.appendChild(messageContent);    var arrow = document.createElement('div')    arrow.className = 'message-arrow';    myMessageBox.appendChild(arrow);    var userInformation = document.createElement('div');    userInformation.className = 'user-information';    var userChatImg = document.createElement('img');    userChatImg.className = 'user-chat-img';    userChatImg.src = userImg.src;    var userChatName = document.createElement('div');    userChatName.className = 'user-chat-name';    userChatName.innerHTML = userName.textContent;    userInformation.appendChild(userChatImg);    userInformation.appendChild(userChatName);    myMessageBox.appendChild(userInformation);    chatContent.appendChild(myMessageBox);    chatContent.scrollTop = chatContent.scrollHeight;}// 生成其他用户的聊天气泡function createOtherMessage(information) {    var otherMessageBox = document.createElement('div');    otherMessageBox.className = 'other-message-box';    var otherUserInformation = document.createElement('div');    otherUserInformation.className = 'other-user-information';    var userChatImg = document.createElement('img');    userChatImg.className = 'user-chat-img';    userChatImg.src = information.img;    var userChatName = document.createElement('span');    userChatName.className = 'user-chat-name';    userChatName.innerHTML = information.name;    otherUserInformation.appendChild(userChatImg);    otherUserInformation.appendChild(userChatName);    otherMessageBox.appendChild(otherUserInformation);    var otherMessageArrow = document.createElement('div');    otherMessageArrow.className = 'other-message-arrow';    otherMessageBox.appendChild(otherMessageArrow);    var otherMessageContent = document.createElement('div');    otherMessageContent.className = 'other-message-content';    var text = document.createElement('span');    text.innerHTML = information.chatContent;    otherMessageContent.appendChild(text);    otherMessageBox.appendChild(otherMessageContent);    chatContent.appendChild(otherMessageBox);    chatContent.scrollTop = chatContent.scrollHeight;}


效果图




后端部分:


server.js


// 引入必须模块var express = require('express');var app = express();var http = require('http').Server(app);var io = require('socket.io')(http);var path = require('path');// 在线人数统计var onlineCount = 0;app.use(express.static(__dirname));// 路径映射app.get('/login.html', function (request, response) {    response.sendFile('login.html');});// 当有用户连接进来时io.on('connection', function (socket) {    console.log('a user connected');    // 发送给客户端在线人数    io.emit('connected', ++onlineCount);    // 当有用户断开    socket.on('disconnect', function () {        console.log('user disconnected');        // 发送给客户端断在线人数        io.emit('disconnected', --onlineCount);        console.log(onlineCount);    });    // 收到了客户端发来的消息    socket.on('message', function (message) {        // 给客户端发送消息        io.emit('message', message);    });});var server = http.listen(4000, function () {    console.log('Sever is running');});


总结:

后端部分使用了Node.js + express 实现
前端部分的布局使用了CSS3的flex来布局
聊天页面的交互使用socket进行了聊天消息的发送与接收的消息机制,避免了用Ajax有时会丢失聊天消息的情况
兼容性目前测试通过的有Chrome 58、Opera 45、Firefox 53、IE 11


待解决的问题:
用户名重复没有检测,后续打算添加用户注册功能,需要使用数据库

无法查看聊天记录,后续打算使用数据库进行聊天记录存储
代码规范还有待提高
界面设计有待提高

接下来还会更新本项目


github地址:https://github.com/CyanChan/chatroom

在线演示地址:http://119.29.19.27:4000/login.html


原创粉丝点击