这是一个我开发了一周的php大作业,其中接触到了很多新的知识,编码过程中也力求规范,在界面美化上也下足了功夫,作为前端语言以及php的入门开发很合适,所以把具体的实现思想分享给需要的人。
1) 创建聊天室(模态窗口实现)
输入聊天室名称及有效期,有效期过后,聊天室自动删除。
设置每个聊天室的人数上限。
设置每个用户限加入的聊天室数量。2) 聊天
随机分配昵称,但不允许同名,有昵称随机动画。
随时显示聊天室的剩余时间,js实现。
ajax实现消息从数据库的获取。
消息输入框使用富文本编辑器ueditor。
退出按钮,点击后退出聊天室。
在数据库中一共用到了四个表,用户表、房间表、角色表、以及消息表。其中角色表和消息表的房间id参照房间表,使得删除房间时级联删除角色和消息。但要注意数据库引擎使用支持外键的innodb,对于其他引擎,可以使用触发器。
用户表users
房间表room
用户角色表role
消息表msg
匿名聊天系统支持多个用户登录, 用户登录后进入大厅界面,首页index.html如下
用户随意输入账号,按回车登录,下为聊天室大厅room.php
房间界面分为两个模块,第一个模块是已经加入的聊天室,根据用户账号检索角色表,获得加入的房间id,并在页面中以块(图片廓)的形式显示出来,用户进入时使用之前的身份。第二个模块是对房间表所有房间的查询,并使用count统计每个房间已经加入的人数,如果房间已加入的人数超过6人,房间将不可进入,样式变成灰色点击无反应。
当一个账号加入的房间超过6个时会弹出
右上方有创建房间功能和退出功能,退出即退出当前账号回到主界面。当点击创建房间时,将弹出一个模态窗口,如下
并使用蒙版使背景变灰,在这里输入的房间名不能超过5个字,房间存在时间不能小于1分钟,输入完成且正确将在数据库中插入一条包含销毁时间的数据,这个销毁时间由函数date_add()实现,即把分钟累加在当前时间上,这样做的好处是,在房间过期时,只需要一条delete between 过去某个时间 and当前时间的数据库语句即可完成。
用户可以点击任何一个房间并进入聊天室界面chats.php,如果 此用户是第一次到此房间将动态随机分配一个昵称(保证不重复),然后在上方提示你在本房间的昵称,如下
聊天界面的布局类似于qq中的聊天框,不同的地方在于右上角动态显示房间的剩余时间,当时间到期时,将自动退出并提示房间已销毁。
聊天窗口的消息的获取使用ajax异步无刷新的技术,每秒调用一次函数获取数据库中的消息,无需刷新页面。对于聊天框中溢出的消息,将出现滚动条,这里有一个细节是,每次获取消息会判断滚动条的位置,如果位置不在最下方,说明在看之前的消息,那么新消息来时,滚动条位置不变;否则,说明在等新消息,滚动条滚至最下方,即弹出最新消息。当发送的文字超过最大宽度的60%时,会自动换行。
消息的样式使用气泡包围,并区分是否为本人发送动态改变浮动方向,输入框使用富文本编辑器,精简后只保留发送表情功能。按钮是3D的按钮,使用hover伪类,当用户点击时,将获取输入框中的信息,并置空,利用另一个php文件插入消息到数据库。
用户可以随时退出房间(此处退出是失去角色退出,如果不想失去角色身份可以使用浏览器自带的回退),当用户重新进入此房间时将获得新的身份。退出房间按钮我采用了独特的方式,是点击左下角的门,当触碰门的轮廓时光标改变,对于这个梯形的绘制也完全符合门的形状。
1.当房间加入的人数达到6人时,房间将变得不可点击。
这里使用css的选择器实现,我在打印每个房间时会给title属性赋值为房间已经加入的人数,这个人数由查询房间表和角色表时获得
title="" href="chats.php?room_id=&users_id=">
最后只需在style样式中设置
[title='6'] {
/*变得不可以点击*/
cursor: pointer;
pointer-events: none;
color: gray;
}
即可实现,后面的气泡浮动方向也是同样的方法。
2.模态窗口的实现
在设置页面时,已经绘制好了模态窗口的样式,只是将其display设置为不可见,点击创建房间时,调用js函数更改样式为block可见,遮罩层同理,设置的z-index低一些,代码如下
/*点击弹出按钮*/
function popBox() {
var pBox = document.getElementById("popBox");
var pLayer = document.getElementById("popLayer");
pBox.style.display = "block";
pLayer.style.display = "block";
}
/*点击关闭按钮*/
function closeBox() {
var pBox = document.getElementById("popBox");
var pLayer = document.getElementById("popLayer");
pBox.style.display = "none";
pLayer.style.display = "none";
}
3.判断用户加入的房间是否超过6个
在每次进入chats.php时,获取当前用户创建的角色数量,在这里使用mysql_num_rows函数获得返回的结果条数,如果大于6,则跳转界面,不再执行后续代码。
$role_num=mysql_num_rows(mysql_query($con,"SELECT * FROM role WHERE users_id='$users_id'"));
if($role_num>=6){
echo "";
die();//不再执行后续代码
}
在这里我遇到一个问题,就是如果不加这个die函数,虽然弹出提示返回上一页面,但仍然在数据库中创建了一条插入房间的数据,这就说明window.location.hert和header函数跳转界面后,后续代码仍然执行,解决办法要么加个条件,要么使用die()或exit()函数。
4.如果是第一次加入房间,随机获得昵称并去重同名
在chats.php页面有一个昵称池,里面有预先定义好的325个昵称,在用户点击房间加入聊天室时,首先会判断是否第一次加入房间,如果是则使用$names[rand(0,325)],随机函数随机获取数组中的值。但在此之前需要获得该房间已经使用的昵称,即保证昵称不重复,代码如下
$exist_names=mysql_query($con,"SELECT role_name FROM role where room_id='$room_id'");
if(!mysql_num_rows($exist_names))//房间第一个加入
$role_name=$names[rand(0,325)];
else{//不重名验证
while($exist_name=mysql_fetch_assoc($exist_names))
$data[]=$exist_name['role_name'];
do{
$role_name=$names[rand(0,325)];
}
while(in_array($role_name,$data));//判断数组中是否存在,存在为真
}
mysql_query($con,"INSERT INTO role(role_name,users_id,room_id) VALUES('$role_name','$users_id',$room_id)");
$newUser='true';//新用户标志,后面随机效果的条件
这里面我把数据库查询的昵称转换为一个数组,这样只需要封装好的in_array函数就能判断是否重名。后面的newUser变量作用在后面是否执行获取昵称的随机动画。
5.获取昵称,随机动画的实现
这里的随机动画,其实真就是一个动画,就是展示给用户看的,昵称在页面加载的时候就已经确定好了。主要逻辑就是使用setInterval每隔0.1秒使用innerHTML更改标签的内容,循环20次后停止,使用clearInterval通过句柄暂停,然后延时0.2秒弹出提示窗口。
本来我是想直接在js中使用php的随机函数,但发现无论循环多少次随机值都是一个,这让我感到很诧异,后来突然想起来php代码是预加载的,就是页面还没渲染出来时随机数就已经执行了,对于后来的js来说php随机数函数就是一个确定了的字符串。所以后来改用js中的随机数,但在js与php代码的混合使用中又出现了问题,最后百度知道了要把php数组转化为json数组,js才能调用。这也让我知道了json在网页中扮演什么角色。
随机昵称函数代码如下:
var names = eval(); //转换php数组为json格式
function randRole() {
if ( == true) {
var turn = 20; //循环次数
var intervar = setInterval(function() {
document.getElementById("rolename").innerHTML = names[Math.ceil(Math.random() * 325)];
if (!turn--) {
document.getElementById("rolename").innerHTML = '';
setTimeout('alert("你在本聊天室的昵称为,尽情畅聊吧!!");', 200);
clearInterval(intervar);
}
}, 100);
}
document.getElementById("rolename").innerHTML = '';
}
6.动态显示剩余时间
这里注意时间显示函数的调用必须在页面加载完后,也就是id=“timeleft”定义之后。对于时间计算我在网上找到的现成的函数经过修改使用的,主要逻辑是获取当前房间的销毁时间然后减去当前时间得到的毫秒数换算而来,再通过setTimeout()函数,每秒运行一次时间换算函数,如果剩余秒数小于1则刷新界面,此时便会弹出“房间已销毁”的提示。个人感觉此函数的功能比较浪费系统资源,应该有更好的办法。
function TimeDown(endDateStr) {
var endDate = new Date(endDateStr);
var nowDate = new Date();
var totalSeconds = parseInt((endDate - nowDate) / 1000);
if (totalSeconds < 1) {
location.reload();//刷新界面
}
var days = Math.floor(totalSeconds / (60 * 60 * 24));
var modulo = totalSeconds % (60 * 60 * 24);
var hours = Math.floor(modulo / (60 * 60));
modulo = modulo % (60 * 60);
var minutes = Math.floor(modulo / 60);
var seconds = modulo % 60;
document.getElementById("timeleft").innerHTML = days + "天" + hours + "小时" + minutes + "分钟" + seconds + "秒";
setTimeout(function() {
TimeDown(endDateStr);
}, 1000)
}
7.富文本编辑器的引入
根据官方文档的指示,我需要在想要使用的页面先引入两个js文件,设置好路径。
然后在想插入的位置,实例化一个富文本编辑器
var ue = UE.getEditor('txtmsg');
然后根据id修改富文本编辑器的样式。对于精简功能,根据官方文档的指示修改 ueditor.config.js 里面的 toolbars根据需要我只保留了表情包功能。这里面用到了几个api,有
if (UE.getEditor('txtmsg').hasContents()) { //判断是否有内容
strmsg = UE.getEditor('txtmsg').getContent(); //获取内容
UE.getEditor('txtmsg').setContent(''); //清空输入内容
}
因为要发送表情包,所以我使用getContent获取输入框全部内容,包括html标签,但出现的问题是这个编译器会自动给每一行前后套上一个
标签,导致我输出消息的时候上下都有换行,最后没办法我只好禁用了p标签的功能
P{display: inline;}最后还是有一些bug
8.ajax的使用
在这里ajax的主要作用体现在输出消息上面,他的无刷新是只刷新消息窗口,而不刷新整个界面。关键代码也就几行,
oxmlHttp = new XMLHttpRequest();
oxmlHttp.onreadystatechange = get_chat_msg_result;
oxmlHttp.open("GET", "chat_recv_ajax.php?room_id=", true);
oxmlHttp.send(null);
首先定义一个ajax请求,然后通过open建立ajax连接,send发送,因为是get请求所以发送null就行,如果是post请求send函数中就要规定发送的内容了。后台调用chat_recv_ajax.php,这个过程一共有四个状态,每次状态改变都调用get_chat_msg_result()函数,当状态为4或完成时渲染新的内容
if (oxmlHttp.readyState == 4 || oxmlHttp.readyState == "complete") {
var scrollDiv = document.getElementById("chatmsg");
…}
chat_recv_ajax.php文件中返回的查询结果
while($line=mysql_fetch_assoc($result)){
$msg=$msg."
". ";$line['role_name']." ".$line['msg_time'].
"
".$line['msg_context']."
}
这整个过程是每秒执行一次,当然这个时间可以自己规定,时间太长新消息获取的不及时,太短的话多次建立连接查询数据库浪费资源。
9.滚动条位置的获取以及改变
为了达到好的用户体验,模拟qq的聊天环境,我想到qq中消息来时会自动弹出新消息,当我在看以前的消息时,来新消息则不会跳转。所以通过百度,我学会了获取滚动条的位置,这里用到了三个属性分别是scrollTop 获取滚动条隐藏的高度,clientHeight显示的滚动条高度也就是窗口高度,scrollHeight滚动条的总高度。它们三者之间有一个关系是scrollTop +clientHeight == scrollHeight时说明滚动到最下方,如果前者之和小于后者说明滚动条位置在中间某个位置,所以在ajax渲染新消息的函数下面,添加一个条件
if (scrollDiv.scrollTop + scrollDiv.clientHeight == scrollDiv.scrollHeight)
var waitNewMsg = true;
if (document.getElementById("chatmsg") != null) {
document.getElementById("chatmsg").innerHTML = oxmlHttp.responseText;
oxmlHttp = null;
}
if (waitNewMsg == true)
scrollDiv.scrollTop = scrollDiv.scrollHeight -scrollDiv.clientHeight;
为什么要分开在innerHTML的两边呢?因为我要保存新消息来之前滚动条的位置,新消息来之后scrollHeight高度就变了。如果条件满足,就把滚动条定位至最底下。
10.补充一点,考虑到不同时区人的使用,所以在每个文件的前面手动规定时间为上海的时间,这样插入数据库时的时间都是一致的。
date_default_timezone_set('Asia/shanghai');
1.修改外部引用的css文件后,刷新页面不生效。
这里在排除所有可能的问题后仍然不生效,最后查得资料发现谷歌浏览器中单纯的刷新并不会重新载入修改的.css文件,要使用ctrl+f5刷新。欺负新人系列。
2.房间名的位置会随左右文字的长度改变而改变。
这个问题在随机动画时尤其明显,定义样式时,我把三个字段放置在一个块中,这样左面的设置左浮动,右面的设置有浮动,中间的在父标签中定义text-align:center;一切看起来都很完美,但是左面文字长度改变时,房间名位置也会发生变化。最后没办法我让房间名脱离文档流,单独放在一个块中,一直在想有没有固定位置还不用脱离文档流的办法。
3.发送消息时,气泡中的文字如果是英文和数字超过最大宽度不换行。
不换行的原因浏览器把很长且连续的英文或数字解读为一个单词,导致溢出气泡。最后在样式中添加word-wrap: break-word;问题解决了。
4.边框的阴影也是边框的一部分,必须留出一部分空间显示阴影,当然,可以指定层叠。
5. window.location.hert和header函数跳转界面后,后续代码仍然执行,解决办法要么加个条件,要么使用die()函数和exit()函数。
6. php代码是预加载的,然后是浏览器执行html等代码。
虽然我是个完美主义者,编码时力求规范,但作为新手肯定还有待改善的地方,希望朋友们不吝指点。还有这个博客贴代码时自动把换行识别为段落可怎么整?!!难受啊!
以上作为分享已经足够,如果有需要代码的朋友链接在下,创作不易多多支持,本文未经许可,禁止转发!
https://download.csdn.net/download/qq_42748385/12117020