滑动到列表底部
<div class="chat-dialog-cont">
<ul class="im-chat-ul">
</ul>
</div>
解决办法一:
一开始就从列表底部加载以避免跳动。查了好久,这个描述最贴切
根据上面的描述:
- JS 解法和上面的代码差不多,然后定时去调整 : (
- CSS 解法 是用
flex-direction: column-reverse;
去旋转列表,但是好像效果不好。。。
$('.chat-dialog-cont').animate({ scrollTop: $('ul.im-chat-ul').prop('scrollHeight') }, 'fast');
判断到顶部的方法是这样的:
$('.chat-dialog-cont').scroll(() => {
if (this.hasMoreHistory) {
if ($('.chat-dialog-cont').scrollTop() === 0) {
//this._loadMore();
}
}
});
解决办法二:
我在想,要不旋转180度好了,结果真的可以,每次打开直接从底部开始滑动起:),代码大概是这样
.chat-dialog-cont{
height: 392px;
overflow-x: hidden;
overflow-y: auto;
border-top: 1px solid #D4D4D4;
transform: rotate(180deg);
direction:rtl;
text-align:left;
ul{
overflow: hidden;
transform: rotate(180deg);
}
}
重要的只有三行代码:transform: rotate(180deg);
direction:rtl;
text-align:left;
判断到顶部的方法是这样的:
$('.chat-dialog-cont').scroll(() => {
if (this.hasMoreHistory) {
let cont = $('.chat-dialog-cont');
if (cont.height() + cont.scrollTop() === $('ul.im-chat-ul').prop('scrollHeight')) {
// this._loadMore();
}
}
});
第二种方法完美的避免了加载图片之后的跳动
但是…发现好像鼠标的滚动好像有点吊诡。
然后被人发现这个问题了(逃)。
然后鲁大师出手帮我修了,厚着脸皮偷偷贴下他的代码。
可以这样修改鼠标滚轮的方向
$(document).on('mousewheel DOMMouseScroll', function(e) {
e.preventDefault();
var value = e.originalEvent.wheelDelta || -e.originalEvent.detail;
var delta = Math.max(-1, Math.min(1, value));
var decoration = delta > 0 ? 'Up' : 'down';
$(document).scrollTop($(document).scrollTop() + 50 * delta);
});
但是edge的浏览器对兼容性提出了质疑(在edge上,列表会变白屏),后来发现是 direction:rtl
的缘故。
找了一下方法,我是这样修的:
@supports not (-ms-ime-align: auto) {.chat-dialog-cont{direction:rtl;}}
@supports (-ms-ime-align: auto) {.chat-dialog-cont{-ms-overflow-style:none;}}
处理聊天列表中表情包层的窗口关闭事件
// display dialog
$('.button').click(function(event) {
$('.expression-layer').show();
event.preventDefault();
return false;
});
// handle close dialog events
function(show) {
if (!this.nameClick) this.nameClick = function nameClick(event) { $('.expression-layer').hide(); };
if (show) {
window.document.body.addEventListener('click', this.nameClick, true);
} else {
window.document.body.removeEventListener('click', this.nameClick, true);
}
}
回车输入文字
方法一:
$('textarea.chat-dialog-inp').keyup(function(event) {
if (event.keyCode === 13) {
let content = this.value;
let caret = getCaret(this);
if (event.shiftKey) {
this.value = content.substring(0, caret - 1) + '\n' + content.substring(caret, content.length);
event.stopPropagation();
} else {
this.value = content.substring(0, caret - 1) + content.substring(caret, content.length);
// self.send();
}
}
});
function getCaret(el) {
if (el.selectionStart) {
return el.selectionStart;
} else if (document.selection) {
el.focus();
let r = document.selection.createRange();
if (r == null) {
return 0;
}
let re = el.createTextRange();
let rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
return rc.text.length;
}
return 0;
}
方法二:
$('textarea.chat-dialog-inp').keyup(event => {
// allow sending message when input enter(without shift)
// refer to : https://stackoverflow.com/questions/6014702/how-do-i-detect-shiftenter-and-generate-a-new-line-in-textarea/6015906#6015906
if (event.keyCode === 13 && !event.shiftKey) {
this._sendText();
}
});
推荐方法二。
聊天列表把[汗] 这样的表情修改成图片
下面代码来自 NIM_Web_Demo
/**
* 1. 将 `[汗]` 等默认表情转化为图片
* 2. 处理回车换行的逻辑
*/
item.showText = escape(item.text);
if (/\[[^\]]+\]/.test(item.showText)) {
let emojiItems = item.showText.match(/\[[^\]]+\]/g);
emojiItems.forEach(text => {
let emojiCnt = emojiList.emoji;
if (emojiCnt[text]) {
item.showText = item.showText.replace(text, `<img class="emoji-small" src="${emojiCnt[text].img}">`);
}
});
}
let emojiBaseUrl = 'http://yx-web.nosdn.127.net/webdoc/h5/emoji';
let emojiList = {
'emoji': {
'[大笑]': { file: 'emoji_0.png' }, '[可爱]': { file: 'emoji_01.png' }, '[色]': { file: 'emoji_02.png' }, '[嘘]': { file: 'emoji_03.png' }, '[亲]': { file: 'emoji_04.png' }, '[呆]': { file: 'emoji_05.png' }, '[口水]': { file: 'emoji_06.png' }, '[汗]': { file: 'emoji_145.png' }, '[呲牙]': { file: 'emoji_07.png' }, '[鬼脸]': { file: 'emoji_08.png' }, '[害羞]': { file: 'emoji_09.png' }, '[偷笑]': { file: 'emoji_10.png' }, '[调皮]': { file: 'emoji_11.png' }, '[可怜]': { file: 'emoji_12.png' }, '[敲]': { file: 'emoji_13.png' }, '[惊讶]': { file: 'emoji_14.png' }, '[流感]': { file: 'emoji_15.png' }, '[委屈]': { file: 'emoji_16.png' }, '[流泪]': { file: 'emoji_17.png' }, '[嚎哭]': { file: 'emoji_18.png' }, '[惊恐]': { file: 'emoji_19.png' }, '[怒]': { file: 'emoji_20.png' }, '[酷]': { file: 'emoji_21.png' }, '[不说]': { file: 'emoji_22.png' }, '[鄙视]': { file: 'emoji_23.png' }, '[阿弥陀佛]': { file: 'emoji_24.png' }, '[奸笑]': { file: 'emoji_25.png' }, '[睡着]': { file: 'emoji_26.png' }, '[口罩]': { file: 'emoji_27.png' }, '[努力]': { file: 'emoji_28.png' }, '[抠鼻孔]': { file: 'emoji_29.png' }, '[疑问]': { file: 'emoji_30.png' }, '[怒骂]': { file: 'emoji_31.png' }, '[晕]': { file: 'emoji_32.png' }, '[呕吐]': { file: 'emoji_33.png' }, '[拜一拜]': { file: 'emoji_160.png' }, '[惊喜]': { file: 'emoji_161.png' }, '[流汗]': { file: 'emoji_162.png' }, '[卖萌]': { file: 'emoji_163.png' }, '[默契眨眼]': { file: 'emoji_164.png' }, '[烧香拜佛]': { file: 'emoji_165.png' }, '[晚安]': { file: 'emoji_166.png' }, '[强]': { file: 'emoji_34.png' }, '[弱]': { file: 'emoji_35.png' }, '[OK]': { file: 'emoji_36.png' }, '[拳头]': { file: 'emoji_37.png' }, '[胜利]': { file: 'emoji_38.png' }, '[鼓掌]': { file: 'emoji_39.png' }, '[握手]': { file: 'emoji_200.png' }, '[发怒]': { file: 'emoji_40.png' }, '[骷髅]': { file: 'emoji_41.png' }, '[便便]': { file: 'emoji_42.png' }, '[火]': { file: 'emoji_43.png' }, '[溜]': { file: 'emoji_44.png' }, '[爱心]': { file: 'emoji_45.png' }, '[心碎]': { file: 'emoji_46.png' }, '[钟情]': { file: 'emoji_47.png' }, '[唇]': { file: 'emoji_48.png' }, '[戒指]': { file: 'emoji_49.png' }, '[钻石]': { file: 'emoji_50.png' }, '[太阳]': { file: 'emoji_51.png' }, '[有时晴]': { file: 'emoji_52.png' }, '[多云]': { file: 'emoji_53.png' }, '[雷]': { file: 'emoji_54.png' }, '[雨]': { file: 'emoji_55.png' }, '[雪花]': { file: 'emoji_56.png' }, '[爱人]': { file: 'emoji_57.png' }, '[帽子]': { file: 'emoji_58.png' }, '[皇冠]': { file: 'emoji_59.png' }, '[篮球]': { file: 'emoji_60.png' }, '[足球]': { file: 'emoji_61.png' }, '[垒球]': { file: 'emoji_62.png' }, '[网球]': { file: 'emoji_63.png' }, '[台球]': { file: 'emoji_64.png' }, '[咖啡]': { file: 'emoji_65.png' }, '[啤酒]': { file: 'emoji_66.png' }, '[干杯]': { file: 'emoji_67.png' }, '[柠檬汁]': { file: 'emoji_68.png' }, '[餐具]': { file: 'emoji_69.png' }, '[汉堡]': { file: 'emoji_70.png' }, '[鸡腿]': { file: 'emoji_71.png' }, '[面条]': { file: 'emoji_72.png' }, '[冰淇淋]': { file: 'emoji_73.png' }, '[沙冰]': { file: 'emoji_74.png' }, '[生日蛋糕]': { file: 'emoji_75.png' }, '[蛋糕]': { file: 'emoji_76.png' }, '[糖果]': { file: 'emoji_77.png' }, '[葡萄]': { file: 'emoji_78.png' }, '[西瓜]': { file: 'emoji_79.png' }, '[光碟]': { file: 'emoji_80.png' }, '[手机]': { file: 'emoji_81.png' }, '[电话]': { file: 'emoji_82.png' }, '[电视]': { file: 'emoji_83.png' }, '[声音开启]': { file: 'emoji_84.png' }, '[声音关闭]': { file: 'emoji_85.png' }, '[铃铛]': { file: 'emoji_86.png' }, '[锁头]': { file: 'emoji_87.png' }, '[放大镜]': { file: 'emoji_88.png' }, '[灯泡]': { file: 'emoji_89.png' }, '[锤头]': { file: 'emoji_90.png' }, '[烟]': { file: 'emoji_91.png' }, '[炸弹]': { file: 'emoji_92.png' }, '[枪]': { file: 'emoji_93.png' }, '[刀]': { file: 'emoji_94.png' }, '[药]': { file: 'emoji_95.png' }, '[打针]': { file: 'emoji_96.png' }, '[钱袋]': { file: 'emoji_97.png' }, '[钞票]': { file: 'emoji_98.png' }, '[银行卡]': { file: 'emoji_99.png' }, '[手柄]': { file: 'emoji_100.png' }, '[麻将]': { file: 'emoji_101.png' }, '[调色板]': { file: 'emoji_102.png' }, '[电影]': { file: 'emoji_103.png' }, '[麦克风]': { file: 'emoji_104.png' }, '[耳机]': { file: 'emoji_105.png' }, '[音乐]': { file: 'emoji_106.png' }, '[吉他]': { file: 'emoji_107.png' }, '[火箭]': { file: 'emoji_108.png' }, '[飞机]': { file: 'emoji_109.png' }, '[火车]': { file: 'emoji_110.png' }, '[公交]': { file: 'emoji_111.png' }, '[轿车]': { file: 'emoji_112.png' }, '[出租车]': { file: 'emoji_113.png' }, '[警车]': { file: 'emoji_114.png' }, '[自行车]': { file: 'emoji_115.png' }
}
};
for (let emoji in emojiList) {
let emojiItem = emojiList[emoji];
for (let key in emojiItem) {
let item = emojiItem[key];
item.img = `${emojiBaseUrl}/${emoji}/${item.file}`;
}
}
let encode = function(_map, _content) {
_content = '' + _content;
if (!_map || !_content) {
return _content || '';
}
return _content.replace(_map.r, function($1) {
let _result = _map[!_map.i ? $1.toLowerCase() : $1];
return _result != null ? _result : $1;
});
};
let escape = (function() {
let _reg = /<br\/?>$/;
let _map = {
r: /<|>|&|\r|\n|\s|'|"/g,
'<': '<',
'>': '>',
'&': '&',
' ': ' ',
'"': '"',
'\'': ''',
'\n': '<br/>',
'\r': ''
};
return function(_content) {
_content = encode(_map, _content);
return _content.replace(_reg, '<br/>');
};
})();
其他
深入写代码的时候,发现和自己之前写小程序的时候差不多。把模型抽象好,之后的东西就很好弄了。要么是推送,要么是短链接请求。(短链接浏览器也有spdy/http2 的服用)
一般的模型是:
- 要有个会话列表:会话列表的增删改查
- 要有个聊天列表:聊天列表的增删改查
- 玩了,其他都是细节
这次使用了云信的API,发现他们包装得挺好的,和我之前自己包的模型差不多,IM的消息格式也和之前强哥设定的差不多。推荐使用他们进行接入咯。云信的细节参考他们自己的开源项目:
玩~