本文主要描述了如何在网页端实现一个仿微信的聊天窗口界面及其中涉及到的一些技术难点详解,作者前端也是初学者,请大拿们轻拍。
咱们先来看一下效果,如下图:
这一种聊天对话的布局模式,比PC端QQ的那一种聊天方式更加贴近移动端。
需求设定:
让我们先来过一遍,实现该聊天窗口需要支持的一些功能点:
• 聊天消息结构与布局
聊天消息包括:人物(头像)和消息内容,朋友消息位居左侧,自己消息则位于右侧,方便区分。
• 文本区域的自适应
消息内容能够自适应大小,总是以最合理的区域大小包裹。
• 滚动支持
由于聊天记录太多,大小超过聊天窗口的预设尺寸。
• 底部自动对齐
当有新的消息以后,窗口内容自动对齐到可视窗口的底部。
• Enter键捕获
消息的输入支持及捕获响应Enter键。
这几个功能点中,感觉最难的是文本区域自适应处理。走了许多的弯路。
实现方案:
• 聊天消息结构及布局
基本的html代码结构如下:
<div> <img src="" alt="头像"/> <div>消息内容</div> </div>
注:头像为一个img标签,文本消息内容则是个div,包裹两者的是另一个大div,代表一个完整消息。
对于布局的左偏移及右偏移,则可以借助float:left|right来进行控制,这个还是很基础的。
• 文本区域的自适应
为了能让聊天的文本内容更加美观,最好的办法就是自适应的文本区域(有个max-width,区域最小化)。
最初尝试了textarea标签,因为其属性有row和col,对应字符个数单位可用于设定行数和列数。
不过可惜的是我被现实给打败了,因为textarea对中文字符和英文字符的计算标准不同,中文字符按2个算,英文字符按1个算。因为用户输入的不确定,导致很难采用文本串的长度来设定textarea的行列值。
于是又回到了起点,只能走计算文本像素点px长度的方式来设定大小(等价于限定max-width)。
计算文本的长度,请参考于"JQuery 计算文本的总宽度 Width"。
function GetCurrentStrWidth(text, font) { var currentObj = $('<pre>').hide().appendTo(document.body); $(currentObj).html(text).css('font', font); var width = currentObj.width(); currentObj.remove(); return width; }
注: 巧妙的通过添加/删除<pre>标签,返回<pre>的真实长度,既文本长度。
对于小于预设的max-width,则文本区域div缺省就行了,对于大于预设的max-width值,则文本区域div设定为width=max-width。
var maxWidth = 320; var currentFont = "normal 13px Helvetica, Arial, sans-serif"; msgDiv.style.font = currentFont; var currentWidth = GetCurrentStrWidth(message, currentFont); // *) 设定文本区域的宽度 if (currentWidth <= maxWidth) { msgDiv.style.width = "" + currentWidth + "px"; } else { msgDiv.style.width = "" + maxWidth + "px"; }
当然这边还有一个需要的注意的地方, 就是自动换行.
word-break: normal|break-all|keep-all; 值 描述 normal 使用浏览器默认的换行规则。 break-all 允许在单词内换行。 keep-all 只能在半角空格或连字符处换行。 为了防止太长的英文单词(非常规词)的影响, 最后选用了word-break: break-all. • 滚动支持 滚动支持, 相对简单, 只需聊天对话框在y轴方向设定如下css属性即可: overflow-y : scroll; • 底部自动对齐 这个也是老生常谈的事了, 每一次聊天窗口的内容有更新, 执行如下js代码就行了。 div.scrollTop = div.scrollHeight; 注: 既scrollTop和scrollHeight属性值保持一致就行了。 • Enter键响应捕获 对enter键响应的支持, 添加如下监听事件函数就行了。 document.addEventListener("keydown", function (evt) { if (evt.keyCode == 13) { // TODO } });
后记,原以为实现一个聊天窗口的实例会非常的简单,却在真正的实践过程中磕磕碰碰。前端这一块,真的水很深,事后回想起来,觉得收获不小,当然对于文本的自适应,采用了一个非常复杂的办法。后来想想是不是只要加一个max-width属性就可以了?