您的位置:首页 > 编程语言 > Java开发

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

2017-11-23 14:41 681 查看
当你看到这篇文章的时候,相信对点对点聊天室的后台结构已经了解的差不多了。这里我们将实现前端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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐