基于Struts2和hibernate的WebSocket聊天室的实现教程六:界面原型及通信请求

当你看到这篇文章的时候,相信对点对点聊天室的后台结构已经了解的差不多了。这里我们将实现前端QQ的聊天界面效果,以及如何使用ajax和webSocket进行通信请求。
Intellij项目下载:
http://download.csdn.net/download/csu_passer/10125210

效果演示

  • 打开界面,请求输入用户名(登录凭证)
    这里写图片描述
  • 登录界面:这里我们登录了两个用户(google浏览器和firefox浏览器),可以看到有系统消息提示和在线列表展示
    这里写图片描述
  • 未连接用户时发送消息,将消息转发给自己
    这里写图片描述
  • 点击在线列表中的用户名,连接用户
    这里写图片描述

  • 用户下线通知
    这里写图片描述

  • 用户下线之后再发送消息
    这里写图片描述

  • 后台日志记录
    这里写图片描述

至于为什么采用gif动态图展示效果而不是用网址自行演示,主要是因为我的服务器是腾讯云的Linux服务器。项目已经上传到服务器上去了,但是在测试的时候websocket连接请求一直被拒绝。可能是因为我的服务器还没有开启这部分协议吧—-后来又有各种事情也就搁置了。

前端界面分析

前端界面初看起来布局十分简单,但是这里面也是蕴含了部分有趣的知识的。为了便于复用和兼容,我们尽量使用css来实现以前需要用Js才能实现的效果。
总的css代码量不是很多,也就300多行。
先看一下Html布局:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>仿QQ在线聊天室</title>
    <link href="resource/css/QQ.css" rel="stylesheet" type="text/css"/><!--导入css样式-->
    <script type="text/javascript" src="resource/js/jquery.js"></script><!--导入js文件-->
    <script type="text/javascript" src="resource/js/socket.js"></script>
</head>
<body>

    <div class="loading">
       加载中... <img src="resource/images/icon/loading.gif" alt="loading">
    </div><!--页面加载动画-->

    <main class="flex hide"><!--html5实义标签-->
        <div class="flex personal-info">
            个人信息--样式待定
        </div>
        <div class="flex chat-room">
            <div class="flex chat">
               <div class="flex records-area">
                   <div class="flex left-center to-who">
                       p2p聊天室欢迎你!
                   </div>
                   <div class="records"><!--聊天记录区域-->

                   </div>
               </div>
                <div class="flex text-area">
                    <!--绝对定位选择气泡--文本---表情-->
                    <div class="flex helps">
                        <div class="text-tips">
                            字体工具栏
                        </div>
                        <div class="flex center-center texts" title="字体选择工具栏">

                        </div>
                        <div class="flex center-center expressions" title="选择表情">
                            <div class="expressions-list">
                                <!--表情,支持动态加载,可从数据库导入-->
                                <img class="expression" src="resource/images/expression/angry.png" data-name="/sq" title="/生气"/>
                                <img class="expression" src="resource/images/expression/smile.png" data-name="/wx" title="/微笑"/>
                                <img class="expression" src="resource/images/expression/anguished.png" data-name="/jy" title="/惊讶"/>
                                <img class="expression" src="resource/images/expression/blush.png" data-name="/ka" title="/可爱"/>
                                <img class="expression" src="resource/images/expression/confused.png" data-name="/yw" title="/疑问"/>
                                <img class="expression" src="resource/images/expression/grin.png" data-name="/lzx" title="/咧嘴笑"/>
                                <img class="expression" src="resource/images/expression/grinning.png" data-name="/dx" title="/大笑"/>
                                <img class="expression" src="resource/images/expression/heart_eyes.png" data-name="/se" title="/色"/>
                                <img class="expression" src="resource/images/expression/kissing_heart.png" data-name="/an" title="/爱你"/>
                                <img class="expression" src="resource/images/expression/mask.png" data-name="/gm" title="/感冒"/>
                                <img class="expression" src="resource/images/expression/pensive.png" data-name="/sx" title="/伤心"/>
                                <img class="expression" src="resource/images/expression/relaxed.png" data-name="/fs" title="/放松"/>
                                <img class="expression" src="resource/images/expression/sunglasses.png" data-name="/ku" title="/酷"/>
                                <img class="expression" src="resource/images/expression/wink.png" data-name="/zy" title="/眨眼"/>
                                <img class="expression" src="resource/images/expression/yum.png" data-name="/kx" title="/开心"/>
                            </div>
                        </div>
                    </div>
                    <!--组合键-->
                    <!--聊天文本输入区域-->
                    <div class="text-input" id="text-input" contenteditable="true" onkeypress="if (event.keyCode==13){ sendText();return false;}" title="聊天区域"></div>
                    <div class="flex button">
                          <button>关闭</button>
                          <button onclick="sendText()">发送</button>
                    </div>
                </div>
            </div>

            <div class="flex online-users"><!--在线列表-->
                <p class="tips" title="点击用户即可连接聊天">在线列表</p>
                <div class="flex online-users-list">


                </div>
            </div>
        </div>
    </main>
    <!--发送消息和接收消息 背景声音提示-->
    <audio id="receive">
        <source  src="resource/audio/receive.wav" type="audio/wav">
    </audio>
    <audio id="send">
        <source  src="resource/audio/send.wav" type="audio/wav">
    </audio>

     <!--聊天记录展示-->
     <div class="history">
         <div class="history-loading">
             加载中... <img src="resource/images/icon/loading.gif" alt="loading">
         </div>
     </div>

</body>

<script type="text/javascript">

    var host = window.location.host;
    var socket = null;
    var me="";
    var self;

    $(document).ready(function() {
        me = prompt("请输入您的名称:", "");
        while (me == "" || me == null) {
            me = prompt("请输入您的名称:", "");
        }
        //登录
        $.ajax({
            type : "GET",  //提交方式
            url : "user/login?username="+me,//测试路径
            async:false,
            beforeSend: function () {
                console.log("准备发送登录请求...")
            },
            complete: function () {
                $(".loading").remove();
                $("main").removeClass("hide");
            },
            success : function(result) {//返回数据根据结果进行相应的处理
                console.log(result);
                if (result.code==200){
                    //请求ws显示在线列表
                    var url = "ws://"+host+"/chatRoom/null";
                    loadWS(url);
                }
            },
            //异常处理
            error:function (XMLHttpRequest, textStatus, errorThrown) {
                console.log(XMLHttpRequest+"---"+textStatus+"---"+errorThrown)
            }
        });


    });

    function loadWS(url) {
        socket = getSocket(url);
        bindMethod(socket);
        socket.onmessage=function (e) {
            var data= $.parseJSON(e.data);
            console.log(data);
            //判断是在线列表还是聊天记录
            if(data.content==null){
                $(".online-users-list").empty();
                $.each(data,function (index, item) {
                    if (item.name==me){
                        self=item;//将对象保存
                    }
                    displayOnlineUsers(item)
                })
            }else if (data.sender.avatar=="000"){
                displaySystemRecords(data.content,data.time);
            }else{
                loadBgMusic("receive");
                addRecordsFromUser(data.sender,data.content);
            }
        };
    }



   $(".texts").click(function () {
        if ($(".text-tips").css("display")=="none"){
            $(".text-tips").css("display","block");
        }else{
            $(".text-tips").css("display","none");
        }
    });

    /**
     * 发送消息
     */
    function sendText() {
       var msg = $(".text-input").html();
        console.log(msg);
       if (!isNull(msg)&&socket!=null) {
            addSelfChatRecords(self,msg);
           //发送消息
           loadBgMusic("send");
           socket.send(msg);
       }
        $(".text-input").html("");//清空输入框
    }

    /**
     * 显示自己发送的消息
     * @param msg 消息
     * @param user 发送者
     */
    function addSelfChatRecords(user,msg) {
        var self="<div class='row'>"+
        "<div class='flex right'>"+
        "<div class='bubble'>"+msg+"</div>"+
        "<img class='avatar' src='"+user.avatar+"' alt='头像'>"+
        "</div>"+
        "</div>";
        $(".records").append($(self));
        $(".records").scrollTop($(".records").scrollHeight)
    }

    /**
     * 显示对方发送过来的消息
     * @param user
     * @param chats
     */
    function addRecordsFromUser(user, chats) {
        var msg="<div class='row'>"+
                "<div class='flex left'>"+
                "<img class='avatar' src='"+user.avatar+"' alt='头像'>"+
                "<div class='bubble'>"+chats+"</div>"+
                "</div>"+
                "</div>";
        $(".records").append($(msg));
        $(".records").scrollTop($(".records").scrollHeight)
    }

    /**
     * 显示在线列表---并且绑定连接函数
    */
    function displayOnlineUsers(user) {
        var str = "<div class='flex left-center users'> "+
                "<img class='avatar' src='"+user.avatar+"' alt='头像'>"+
                "<a href='#!' class='user-link' data-name='"+user.name+"'>"+user.name+"</a>"+
                "</div>";
        $(".online-users-list").append($(str));


        $(".user-link").bind('click',function () {
            var name = $(this).attr("data-name");
            Log("关闭当前连接,连接target:"+name);
            socket.close();
            var url = "ws://"+host+"/chatRoom/"+name;
            $(".to-who").text(name);
            loadWS(url);
    })
}


    /**
     * 加载系统消息
     */
    function displaySystemRecords(content,time) {
        var str =  "<p class='sys-records'>系统消息:"+content+"  "+(time.month+1)+"月"+time.date+"号  "+time.hours+":"+time.minutes+":"+time.seconds+"</p>";
        $(".records").append($(str));
        $(".records").scrollTop($(".records").scrollHeight)
    }


    /**
     * 加载背景音乐
     * @param id
     */
    function loadBgMusic(id) {
        var audio = document.getElementById(id);
        audio.play();
    }

    /**
     * 获取插入光标的位置,在光标处添加表情
     */
    $(".expression").click(function () {
        if (!$("#text-input").is(":focus")){
            $("#text-input").focus();//如果文本编辑器没有获取焦点则自动获取
        }
        var emoj = "<img class='expression' src='"+$(this).attr('src')+"' data-name='"+$(this).attr('data-name')+"' title='"+$(this).attr('title')+"'/>";

        var editor = document.getElementById("text-input");
        var cursorPosition=-1;
        if(document.selection && document.selection.createRange){//IE浏览器
            var range = document.selection.createRange();
            range.moveStart("character",-editor.innerText.length);
            range.pasteHTML( emoj ) ;
        }else{//非IE
            cursorPosition= editor.selectionStart;
//
//            var str = editor.innerText.substr(0,cursorPosition-1);
//            var str1=editor.innerText.substr(cursorPosition);
            document.execCommand( 'InsertHtml' , '' , emoj );//会插入到哪里
        }
    });

    /**
     * 判断字符串是否全由空格组成
     * @param str
     * @returns {boolean}
     */
    function isNull( str ){
        if ( str == ""||str==null ) return true;
        var regu = "^[ ]+$";
        var re = new RegExp(regu);
        return re.test(str);
    }

</script>

</html>

css布局实现

  • 定义了几个常用的全局属性和css类,比如flex
@charset "UTF-8";
*{
    margin:0;
    padding:0;
    font-family: "Arial","Microsoft YaHei","黑体","宋体",sans-serif;
    transition: all 0.5s;
    box-sizing: border-box;
}
::selection{
    background: #000000;
    color: #ffffff;
}
input{
    background: transparent;
    padding:5px;
}
*:focus{
    outline: none;/*消除谷歌浏览器控件选中时的边框*/
}
/*以下修改标签的默认样式*/
a{
    text-decoration: none;
    color: #000;
}
a:hover{
    color: #fc311e;
}

button{
    border:1px solid #eee;
    padding:5px 10px;
    color: #000;
    background: #ffffff;
}
button:hover{
    background-color: #3d9cd4;
    color: #ffffff;
}
.flex{
    display: flex;
    flex-wrap: wrap;
}/*定义flex布局*/
/*下面定义几种对齐方式*/
.center-center{
    justify-content: center;
    align-items: center;
    align-content: center;
}

.left-center{
    justify-content: flex-start;
    align-items: center;
    align-content: center;
}
/*圆形头像样式*/
img.avatar{
    width:50px;
    height: 50px;
    border-radius:50%;
    object-fit: cover;/*图片覆盖填充*/
}

剩余的CSS:


main{
    width:70%;
    min-height:600px;
    /*padding:20px;*/
    margin:20px auto;
    border:1px solid rgba(0,0,0,.15);
    box-shadow: 0 0 10px 2px rgba(0,0,0,.2);
    background: #fcfcfc;
}

.hide{
    display: none !important;
}

.loading{
    width:100px;
    height:50px;
    margin:100px auto;
}

.personal-info{
    padding:20px;
    width:20%;
    height:inherit;
    background-color: #8fd499;
}

.chat-room{
    width:80%;
    height:inherit;
}

.chat{
    width:80%;
    flex-direction: column;
}
.chat .records-area{
    width:100%;
    height:80%; 
    flex:0 0 auto;
    flex-direction: column;
    align-items: flex-start;
    border-bottom: 1px solid #e8e8e8;
}
.chat .records-area .to-who{
    width:100%;
    flex:0 0 auto;
    height:80px;
    padding:20px;
    border-bottom: 1px solid #eeeeee;
}
.chat .records-area .records{
    width:100%;
    flex:1 0 auto;
    max-height:390px;
    overflow-y: auto;
    /*background-color: #d4582d;*/
}
.chat .records-area .records .sys-records{
    padding:0 10px;
    color: #7f7f7f;
    font-size:14px;
}
.chat  .text-area{
    width:100%;
    padding: 5px 0;
    flex:1 0 auto;
    flex-direction: column;

    position: relative;
}
.chat  .text-area .helps{
    height:25px;
    padding:0 10px;
    flex:0 0 auto;

    position: relative;
}
.chat  .text-area .helps>div{
    width:24px;
    margin-right: 5px;
    background: center no-repeat;
    background-size: contain;
}
.chat  .text-area .helps>div:not(:first-child):hover{
    background-color: rgba(0,0,0,.1);
}
/*字体选择工具栏*/
.chat  .text-area .helps .text-tips{
    position: absolute;
    top:-120%;
    left:0;
    display: none;
    width:100%;
    height:30px;
    line-height:30px;
}

.chat  .text-area .helps .texts{
    background-image: url("../images/expression/font.png");
}

.chat  .text-area .helps .expressions{
    background-image: url("../images/expression/expression.png");
    position: relative;
}
.chat  .text-area .helps .expressions:hover .expressions-list{
    display: flex;
}
.chat  .text-area .helps .expressions .expressions-list:hover{
    display: flex;
}
.chat  .text-area .helps .expressions .expressions-list{
    display: none;
    position: absolute;
    left:0;
    width:200px;
    height:100px;
    background: #deffeb;
    border:1px solid #eeeeee;
    transform: translateY(-60%);

    overflow-y: auto;

    flex-wrap: wrap;
    justify-content: flex-start;
}

img.expression{
    display: inline;
    width:30px;
    height:30px;
    border-radius:50%;
    object-fit: contain;
    background-color: rgba(0,0,0,.1);
}


.chat  .text-area .text-input{
    flex:1 0 auto;
    border:none;
    font-size:14px;
    padding:0 10px;
    overflow-y: auto;

    border-radius: 4px;
    /*background-color: hsla(0,0%,71%,.1);*/
    background-color:transparent;
    resize: none;
    display: inline-block;
    vertical-align: top;
    outline-style: none;
}

.chat  .text-area .button{
    height: 30px;
    padding:0 10px;
    flex:0 0 auto;
    justify-content: flex-end;
}
.chat  .text-area .button button{
    width:80px;
    background-color: #8fd499;
    margin-left:5px;
}
.online-users{
    width:20%;
    height:inherit;
    flex-direction: column;
    background-color: #d4c38d
}
.online-users .tips{
    flex: 0 0 auto;
    padding:20px;
    height:50px;
    border-bottom: 1px solid #eeeeee;
    cursor: default;
}
.online-users  .online-users-list{
    width:100%;
    padding-top:10px;
    flex: 1 0 auto;

    max-height:500px;
    flex-direction: column;

    overflow-y: auto;
}
.online-users  .online-users-list .users{
    width:100%;
    padding:0 10px;
    margin-bottom: 20px;
    height:50px;
    text-overflow:ellipsis;
    white-space:nowrap;
    overflow:hidden;
}

.online-users  .online-users-list .users img{
    margin-right:10px;
}
.online-users  .online-users-list .users a{
    width:70px;
}

/*聊天气泡*/
.row{
    width:100%;
    min-height:60px;
    padding:10px;
}
.row>div{
    height:inherit;
}
.left{
    justify-content: flex-start;
}
.right{
    justify-content: flex-end;
}
/*气泡*/
.bubble{
    max-width:40%;
    font-size: 14px;

    border-radius:8px;
    padding:0 10px;
    margin:0 12px;
    background: #7ac2ff;
    border:1px solid #7ac2ff;

    line-height: 14px;

    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
    align-items: center;
    /*align-content: center;*/

    position: relative;
}

.right .bubble:after{
    content: '';
    position: absolute;
    right:0;
    top:12%;
    transform: translateX(100%) translateY(0%);
    width:0;
    height:0;
    border: 8px solid transparent;
    /*float: right;*/
    /*transform: translateX(60%) translateY(-20%);*/
    border-left-color: inherit;
}
.left .bubble:before{
    content: '';
    width:0;
    height:0;
    border: 8px solid transparent;

    position: absolute;
    left:0;
    top:10%;
    transform: translateX(-100%) translateY(0%);
    border-right-color: inherit;
}

js解释

  • 在页面加载的时候询问登录名称 向后台发送登录请求
    这里写图片描述

  • 请求成功后 开始与后台建立websocket连接 此时会在后台进行httpSession和websocket session获取
    这里写图片描述

  • 建立ws连接以后 接受来自后台的json数据 并且判断是在线列表数据还是聊天数据

这里写图片描述

  • 聊天数据也要判断是不是系统消息
    这里写图片描述
  • 绑定发送消息的方法
    这里写图片描述
  • 然后在聊天记录框中显示自己的消息 、系统消息、对方发过来的消息
    这里写图片描述
  • 加载背景音乐的方法也很简单 直接调用系统自带的play方法
    这里写图片描述
  • 有难度的是如何在光标处插入表情
    这里写图片描述
    IE用
    这里写图片描述
    非IE
    这里写图片描述
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值