分析

  • 当收到新消息时,消息层自动滚动
  • 当查看聊天记录时,禁止消息层滚动
  • 键盘弹出/收起 的时候,消息层滚动到最新消息处

使用到vue的技术点

html 结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  <transition-group tag="ul" name="slide" class="barrage-container"
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
>

<li v-for="(item, index) in barrageList" :key="index" class="barrage-item" :class="{self: item.self}">
<!-- head -->
<div v-if="!item.anonymous" class="head" v-head="item.headSrc"></div>
<div v-else class="head" v-head="item.headSrc"></div>

<div class="info">
<p v-if="!item.anonymous" class="name">{{item.nickName}}</p>
<p v-else class="name">{{anonymousNameList[item.id]}}</p>

<p class="message">
{{item.message}}
</p>
</div>
</li>
</transition-group>

js 实现

1、首先要先初始化,获取当前包裹消息的父类container,和显示区域containerParentH的高度,获取containerParentH是为了后面计算消息层滚动到底部需要的距离

1
2
3
4
__init () {
this.container = $('.barrage-container')
this.containerParentH = this.container.parent().height()
}

2、列表过渡钩子函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//-----------
// 这里只用到了进入中的钩子函数
//-----------

// 节点进入前,在这个函数里可以设置进入前的样式,比如随机字体颜色,随机位置等。
beforeEnter (el) {
// el.style.cssText = `color:${this.colorRandom()}`
},


// 此回调函数是可选项的设置
// 与 CSS 结合时使用
// 主要是进入过程的样式,如果是横屏滚动弹幕 可以设置滚动的速度
enter: function (el, done) {
done() // 必须调用,表示进入过程已经完成,才能触发afterEnter函数
},


// 节点进入后
// 需要scrollTop的距离
afterEnter: function (el) {
const insertDomH = el.offsetHeight //获取当前插入节点的高度
this.containerH += insertDomH // 累加插入节点的高度containerH,主要是为了计算消息层滚动到底部
this.scroll(insertDomH) // 消息滚动到底部或不滚动
},

3、获取消息层滚动到底部的距离

1
2
3
4
5
// 获取滚动到底部要滚动的距离
scrollBottomY () {
const Y = this.containerH - this.containerParentH // 当前所有插入节点的高度和 - 显示消息区域的高度
return Y > 0 ? Y : 0 // 当消息层高度没有高出显示区域 滚动距离是0
},

4、消息层滚动到底部

1
2
3
4
5
6
// 滚动到底部
scrollBottom () {
this.scrollTop = this.scrollBottomY() // 获取滚动到底部要滚动的距离 并记录当前滚动的距离
this.container.scrollTop(this.scrollTop) // 滚动到相应位置
return this.scrollTop
},

5、滚动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 滚动

//
// 这快主要是判断当前是否处于查看聊天记录状态,是,禁止消息层自动滚动。反之,滚
// 动。
//
scroll (insertDomH) {
// this.scrollBottomY() - this.getScrollTop() - insertDomH 
// 如果是插入节点前消息已经滚动到底部 得到的值 是 0.
// 判断条件 > 40 主要留个容忍值。
// 即:插入节点前差消息层 40~0 距离滚动到底部。收到新消息也允许消息层自动滚动
if (this.scrollBottomY() - this.getScrollTop() - insertDomH > 40) {
return false
} else {
this.scrollBottom()
}
},

6、这里我使用定时器模拟了接受新消息

1
2
3
4
5
6
7
8
9
10
11
mounted () {
this.$nextTick(() => {
this.__init() // 初始化
// 模拟数据
let index = 0
setInterval(() => {
this.barrageList.push({message: `的煎熬几个好啊和耦合个熊恶化${index}`, self: false, nickName: '爱你所爱', headSrc: '../../static/head/ic_head10@2x.png'})
index++
}, 2000)
})
}



至此,大部分功能已经实现。接下来就是自己发送一条消息情况下的一些交互实现

7、首先实现,真实事件(input: 获取 / 失去焦点 )触发消息层滚动到底部的代码

1
2
3
4
5
// 手动触发滚动底部
scrollBottomHook () {
this.containerParentH = this.container.parent().height() // 重新计算消息层显示区域的高度
this.scrollBottom() // 滚动到底部
}

8、失去/获取 焦点 调用的函数

1
2
3
4
5
6
7
8
// 手动触发弹幕滚动到底部
// 此函数实现的是父类中调用 聊天室组件的 scrollDownHook() 方法
barrageScrollBottom () {
this.barrage ? false : (this.barrage = this.$refs.barrage)
setTimeout(() => {
this.barrage.scrollDownHook()
}, 500) // 加个定时器的原因主要是,键盘弹出 收起 有延迟的原因
}

源码地址