mirror of
https://github.com/xfgryujk/blivechat.git
synced 2024-12-27 13:20:29 +08:00
添加平滑消息队列
This commit is contained in:
parent
3b2616c278
commit
98184e6732
2
blivedm
2
blivedm
@ -1 +1 @@
|
|||||||
Subproject commit efc7f3b3f8175ad05135b9819f86ed6fd561c674
|
Subproject commit 87651f044c5605fff08d0caffa012b4f89259353
|
@ -20,6 +20,7 @@ export const GUARD_LEVEL_TO_TEXT = [
|
|||||||
export const MESSAGE_TYPE_TEXT = 0
|
export const MESSAGE_TYPE_TEXT = 0
|
||||||
export const MESSAGE_TYPE_MEMBER = 1
|
export const MESSAGE_TYPE_MEMBER = 1
|
||||||
export const MESSAGE_TYPE_SUPER_CHAT = 2
|
export const MESSAGE_TYPE_SUPER_CHAT = 2
|
||||||
|
export const MESSAGE_TYPE_DEL = 3
|
||||||
|
|
||||||
// 美元 -> 人民币 汇率
|
// 美元 -> 人民币 汇率
|
||||||
const EXCHANGE_RATE = 7
|
const EXCHANGE_RATE = 7
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
:avatarUrl="message.avatarUrl" :title="message.title" :content="message.content"
|
:avatarUrl="message.avatarUrl" :title="message.title" :content="message.content"
|
||||||
:time="message.time"
|
:time="message.time"
|
||||||
></legacy-paid-message>
|
></legacy-paid-message>
|
||||||
<paid-message :key="message.id" v-else
|
<paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT"
|
||||||
class="style-scope yt-live-chat-item-list-renderer"
|
class="style-scope yt-live-chat-item-list-renderer"
|
||||||
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName"
|
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName"
|
||||||
:time="message.time" :content="message.content"
|
:time="message.time" :content="message.content"
|
||||||
@ -62,11 +62,24 @@ export default {
|
|||||||
return {
|
return {
|
||||||
MESSAGE_TYPE_TEXT: constants.MESSAGE_TYPE_TEXT,
|
MESSAGE_TYPE_TEXT: constants.MESSAGE_TYPE_TEXT,
|
||||||
MESSAGE_TYPE_MEMBER: constants.MESSAGE_TYPE_MEMBER,
|
MESSAGE_TYPE_MEMBER: constants.MESSAGE_TYPE_MEMBER,
|
||||||
|
MESSAGE_TYPE_SUPER_CHAT: constants.MESSAGE_TYPE_SUPER_CHAT,
|
||||||
|
|
||||||
styleElement,
|
styleElement,
|
||||||
messages: [],
|
messages: [], // 显示的消息
|
||||||
paidMessages: [],
|
paidMessages: [], // 固定在上方的消息
|
||||||
canAutoScroll: true
|
|
||||||
|
smoothedMessageQueue: [], // 平滑消息队列,由外部调用addMessages等方法添加
|
||||||
|
emitSmoothedMessageTimerId: null, // 消费平滑消息队列的定时器ID
|
||||||
|
enqueueIntervals: [], // 最近进队列的时间间隔,用来估计下次进队列的时间
|
||||||
|
lastEnqueueTime: null, // 上次进队列的时间
|
||||||
|
estimatedEnqueueInterval: null, // 估计的下次进队列时间间隔
|
||||||
|
|
||||||
|
atBottom: true // 滚动到底部,用来判断能否自动滚动
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
canScrollToBottom() {
|
||||||
|
return this.atBottom/* || this.allowScroll*/
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -75,6 +88,10 @@ export default {
|
|||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
document.head.removeChild(this.styleElement)
|
document.head.removeChild(this.styleElement)
|
||||||
|
if (this.emitSmoothedMessageTimerId) {
|
||||||
|
window.clearTimeout(this.emitSmoothedMessageTimerId)
|
||||||
|
this.emitSmoothedMessageTimerId = null
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
css(val) {
|
css(val) {
|
||||||
@ -86,27 +103,7 @@ export default {
|
|||||||
this.addMessages([message])
|
this.addMessages([message])
|
||||||
},
|
},
|
||||||
addMessages(messages) {
|
addMessages(messages) {
|
||||||
for (let message of messages) {
|
this.enqueueMessages(messages)
|
||||||
message = {
|
|
||||||
...message,
|
|
||||||
addTime: new Date() // 添加一个本地时间给Ticker用,防止本地时间和服务器时间相差很大的情况
|
|
||||||
}
|
|
||||||
this.messages.push(message)
|
|
||||||
if (message.type !== constants.MESSAGE_TYPE_TEXT) {
|
|
||||||
this.paidMessages.push(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.messages.length > this.maxNumber) {
|
|
||||||
// 防止同时添加和删除项目时所有的项目重新渲染 https://github.com/vuejs/vue/issues/6857
|
|
||||||
this.$nextTick(() => this.messages.splice(0, this.messages.length - this.maxNumber))
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.canAutoScroll) {
|
|
||||||
this.scrollToBottom()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
mergeSimilar(content) {
|
mergeSimilar(content) {
|
||||||
for (let i = this.messages.length - 1; i >= 0 && i >= this.messages.length - 5; i--) {
|
for (let i = this.messages.length - 1; i >= 0 && i >= this.messages.length - 5; i--) {
|
||||||
@ -129,6 +126,141 @@ export default {
|
|||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
delMessage(id) {
|
delMessage(id) {
|
||||||
|
this.delMessages([id])
|
||||||
|
},
|
||||||
|
delMessages(ids) {
|
||||||
|
this.enqueueMessages(ids.map(id => {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
type: constants.MESSAGE_TYPE_DEL
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
clearMessages() {
|
||||||
|
this.messages = []
|
||||||
|
this.paidMessages = []
|
||||||
|
},
|
||||||
|
|
||||||
|
enqueueMessages(messages) {
|
||||||
|
if (this.lastEnqueueTime) {
|
||||||
|
let interval = new Date() - this.lastEnqueueTime
|
||||||
|
if (interval > 0) {
|
||||||
|
this.enqueueIntervals.push(interval)
|
||||||
|
if (this.enqueueIntervals.length > 5) {
|
||||||
|
this.enqueueIntervals.splice(0, this.enqueueIntervals.length - 5)
|
||||||
|
}
|
||||||
|
this.estimatedEnqueueInterval = Math.max(...this.enqueueIntervals)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.lastEnqueueTime = new Date()
|
||||||
|
|
||||||
|
// 只有要显示的消息需要平滑
|
||||||
|
let messageGroup = []
|
||||||
|
for (let message of messages) {
|
||||||
|
messageGroup.push(message)
|
||||||
|
if (message.type !== constants.MESSAGE_TYPE_DEL) {
|
||||||
|
this.smoothedMessageQueue.push(messageGroup)
|
||||||
|
messageGroup = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (messageGroup.length > 0) {
|
||||||
|
this.smoothedMessageQueue.push(messageGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.emitSmoothedMessageTimerId) {
|
||||||
|
this.emitSmoothedMessageTimerId = window.setTimeout(this.emitSmoothedMessages)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emitSmoothedMessages() {
|
||||||
|
this.emitSmoothedMessageTimerId = null
|
||||||
|
if (this.smoothedMessageQueue.length <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 估计的下次进队列剩余时间
|
||||||
|
let estimatedNextEnqueueRemainTime = 10 * 1000
|
||||||
|
if (this.estimatedEnqueueInterval) {
|
||||||
|
estimatedNextEnqueueRemainTime = Math.max(this.lastEnqueueTime - new Date() + this.estimatedEnqueueInterval, 1)
|
||||||
|
}
|
||||||
|
// 最快80ms/条,计算发送的消息数,保证在下次进队列之前消费完队列
|
||||||
|
let groupNumToEmit
|
||||||
|
if (this.smoothedMessageQueue.length < estimatedNextEnqueueRemainTime / 80) {
|
||||||
|
// 队列中消息数很少,每次发1条也能发完
|
||||||
|
groupNumToEmit = 1
|
||||||
|
} else {
|
||||||
|
// 每次发1条以上,保证按最快速度能发完
|
||||||
|
groupNumToEmit = Math.ceil(this.smoothedMessageQueue.length / (estimatedNextEnqueueRemainTime / 80))
|
||||||
|
}
|
||||||
|
|
||||||
|
let messageGroups = this.smoothedMessageQueue.splice(0, groupNumToEmit)
|
||||||
|
let mergedGroup = []
|
||||||
|
for (let messageGroup of messageGroups) {
|
||||||
|
for (let message of messageGroup) {
|
||||||
|
mergedGroup.push(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.handleMessageGroup(mergedGroup)
|
||||||
|
|
||||||
|
if (this.smoothedMessageQueue.length <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let sleepTime
|
||||||
|
if (groupNumToEmit == 1) {
|
||||||
|
// 队列中消息数很少,随便定个80-1000ms的时间
|
||||||
|
sleepTime = estimatedNextEnqueueRemainTime / this.smoothedMessageQueue.length
|
||||||
|
sleepTime *= 0.5 + Math.random()
|
||||||
|
if (sleepTime > 1000) {
|
||||||
|
sleepTime = 1000
|
||||||
|
} else if (sleepTime < 80) {
|
||||||
|
sleepTime = 80
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 按最快速度发
|
||||||
|
sleepTime = 80
|
||||||
|
}
|
||||||
|
this.emitSmoothedMessageTimerId = window.setTimeout(this.emitSmoothedMessages, sleepTime)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleMessageGroup(messageGroup) {
|
||||||
|
if (messageGroup.length <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let message of messageGroup) {
|
||||||
|
switch (message.type) {
|
||||||
|
case constants.MESSAGE_TYPE_TEXT:
|
||||||
|
case constants.MESSAGE_TYPE_MEMBER:
|
||||||
|
case constants.MESSAGE_TYPE_SUPER_CHAT:
|
||||||
|
this.handleAddMessage(message)
|
||||||
|
break
|
||||||
|
case constants.MESSAGE_TYPE_DEL:
|
||||||
|
this.handleDelMessage(message.id)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.messages.length > this.maxNumber) {
|
||||||
|
// 防止同时添加和删除项目时所有的项目重新渲染 https://github.com/vuejs/vue/issues/6857
|
||||||
|
this.$nextTick(() => this.messages.splice(0, this.messages.length - this.maxNumber))
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.canScrollToBottom) {
|
||||||
|
this.scrollToBottom()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleAddMessage(message) {
|
||||||
|
message = {
|
||||||
|
...message,
|
||||||
|
addTime: new Date() // 添加一个本地时间给Ticker用,防止本地时间和服务器时间相差很大的情况
|
||||||
|
}
|
||||||
|
this.messages.push(message)
|
||||||
|
if (message.type !== constants.MESSAGE_TYPE_TEXT) {
|
||||||
|
this.paidMessages.push(message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDelMessage(message) {
|
||||||
|
let id = message.id
|
||||||
for (let arr of [this.messages, this.paidMessages]) {
|
for (let arr of [this.messages, this.paidMessages]) {
|
||||||
for (let i = 0; i < arr.length; i++) {
|
for (let i = 0; i < arr.length; i++) {
|
||||||
if (arr[i].id === id) {
|
if (arr[i].id === id) {
|
||||||
@ -138,16 +270,20 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearMessages() {
|
|
||||||
this.messages = []
|
maybeScrollToBottom() {
|
||||||
this.paidMessages = []
|
if (this.canScrollToBottom) {
|
||||||
|
this.scrollToBottom()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
this.$refs.scroller.scrollTop = this.$refs.scroller.scrollHeight
|
this.$refs.scroller.scrollTop = this.$refs.scroller.scrollHeight
|
||||||
|
this.atBottom = true
|
||||||
},
|
},
|
||||||
onScroll() {
|
onScroll() {
|
||||||
let scroller = this.$refs.scroller
|
let scroller = this.$refs.scroller
|
||||||
this.canAutoScroll = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight < SCROLLED_TO_BOTTOM_EPSILON
|
this.atBottom = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight < SCROLLED_TO_BOTTOM_EPSILON
|
||||||
|
// this.flushActiveItems()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user