mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
679 lines
45 KiB
Plaintext
679 lines
45 KiB
Plaintext
<template>
|
||
<yt-live-chat-renderer
|
||
class="style-scope yt-live-chat-app"
|
||
style="--scrollbar-width:11px;"
|
||
hide-timestamps
|
||
@mousemove="refreshCantScrollStartTime"
|
||
>
|
||
<ticker
|
||
v-model:messages="paidMessages"
|
||
class="style-scope yt-live-chat-renderer"
|
||
:show-gift-name="showGiftName || undefined"
|
||
/>
|
||
<yt-live-chat-item-list-renderer
|
||
class="style-scope yt-live-chat-renderer"
|
||
allow-scroll
|
||
>
|
||
<div
|
||
id="item-scroller"
|
||
ref="scroller"
|
||
class="style-scope yt-live-chat-item-list-renderer animated"
|
||
@scroll="onScroll"
|
||
>
|
||
<div
|
||
id="item-offset"
|
||
ref="itemOffset"
|
||
class="style-scope yt-live-chat-item-list-renderer"
|
||
>
|
||
<div
|
||
id="items"
|
||
ref="items"
|
||
class="style-scope yt-live-chat-item-list-renderer"
|
||
style="overflow: hidden"
|
||
:style="{ transform: `translateY(${Math.floor(scrollPixelsRemaining)}px)` }"
|
||
>
|
||
<template
|
||
v-for="message in messages"
|
||
:key="message.id"
|
||
>
|
||
<text-message
|
||
v-if="message.type === MESSAGE_TYPE_TEXT"
|
||
class="style-scope yt-live-chat-item-list-renderer"
|
||
:time="message.time"
|
||
:avatar-url="message.avatarUrl"
|
||
:author-name="message.authorName"
|
||
:author-type="message.authorType"
|
||
:privilege-type="message.privilegeType"
|
||
:content-parts="getShowContentParts(message)"
|
||
:repeated="message.repeated"
|
||
/>
|
||
<paid-message
|
||
v-else-if="message.type === MESSAGE_TYPE_GIFT"
|
||
class="style-scope yt-live-chat-item-list-renderer"
|
||
:time="message.time"
|
||
:avatar-url="message.avatarUrl"
|
||
:author-name="getShowAuthorName(message)"
|
||
:price="message.price"
|
||
:price-text="message.price <= 0 ? getGiftShowNameAndNum(message) : ''"
|
||
:content="message.price <= 0 ? '' : getGiftShowContent(message, showGiftName)"
|
||
/>
|
||
<membership-item
|
||
v-else-if="message.type === MESSAGE_TYPE_MEMBER"
|
||
class="style-scope yt-live-chat-item-list-renderer"
|
||
:time="message.time"
|
||
:avatar-url="message.avatarUrl"
|
||
:author-name="getShowAuthorName(message)"
|
||
:privilege-type="message.privilegeType"
|
||
:title="message.title"
|
||
/>
|
||
<paid-message
|
||
v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT"
|
||
class="style-scope yt-live-chat-item-list-renderer"
|
||
:time="message.time"
|
||
:avatar-url="message.avatarUrl"
|
||
:author-name="getShowAuthorName(message)"
|
||
:price="message.price"
|
||
:content="getShowContent(message)"
|
||
/>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</yt-live-chat-item-list-renderer>
|
||
</yt-live-chat-renderer>
|
||
</template>
|
||
|
||
<script>
|
||
import _ from 'lodash'
|
||
import Ticker from './Ticker.vue'
|
||
import TextMessage from './TextMessage.vue'
|
||
import MembershipItem from './MembershipItem.vue'
|
||
import PaidMessage from './PaidMessage.vue'
|
||
import * as constants from './constants'
|
||
import { defineComponent } from 'vue'
|
||
import { useDebounceFn } from '@vueuse/core'
|
||
|
||
// 瑕佹坊鍔犵殑娑堟伅绫诲瀷
|
||
const ADD_MESSAGE_TYPES = [
|
||
constants.MESSAGE_TYPE_TEXT,
|
||
constants.MESSAGE_TYPE_GIFT,
|
||
constants.MESSAGE_TYPE_MEMBER,
|
||
constants.MESSAGE_TYPE_SUPER_CHAT,
|
||
]
|
||
// 鍙戦€佹秷鎭椂闂撮棿闅旇寖鍥?
|
||
const MESSAGE_MIN_INTERVAL = 80
|
||
const MESSAGE_MAX_INTERVAL = 1000
|
||
|
||
// 姣忔鍙戦€佹秷鎭悗澧炲姞鐨勫姩鐢绘椂闂达紝瑕佹瘮MESSAGE_MIN_INTERVAL绋嶅井澶т竴鐐癸紝澶皬浜嗗姩鐢讳笉杩炵画锛屽お澶т簡鍙戦€佹秷鎭椂浼氫腑鏂姩鐢?
|
||
// 84 = ceil((1000 / 60) * 5)
|
||
const CHAT_SMOOTH_ANIMATION_TIME_MS = 84
|
||
// 婊氬姩鏉¤窛绂诲簳閮ㄥ皬浜庡灏戝儚绱犲垯璁や负鍦ㄥ簳閮?
|
||
const SCROLLED_TO_BOTTOM_EPSILON = 15
|
||
|
||
export default defineComponent({
|
||
name: 'ChatRenderer',
|
||
components: {
|
||
Ticker,
|
||
TextMessage,
|
||
MembershipItem,
|
||
PaidMessage
|
||
},
|
||
props: {
|
||
maxNumber: {
|
||
type: Number,
|
||
default: 60
|
||
},
|
||
showGiftName: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
customCss: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
},
|
||
data() {
|
||
let customStyleElement = document.createElement('style')
|
||
document.head.appendChild(customStyleElement)
|
||
const setCssDebounce = useDebounceFn(() => {
|
||
customStyleElement.innerHTML = this.customCss ?? ''
|
||
console.log('[blivechat] 宸茶缃嚜瀹氫箟鏍峰紡')
|
||
}, 1000)
|
||
return {
|
||
MESSAGE_TYPE_TEXT: constants.MESSAGE_TYPE_TEXT,
|
||
MESSAGE_TYPE_GIFT: constants.MESSAGE_TYPE_GIFT,
|
||
MESSAGE_TYPE_MEMBER: constants.MESSAGE_TYPE_MEMBER,
|
||
MESSAGE_TYPE_SUPER_CHAT: constants.MESSAGE_TYPE_SUPER_CHAT,
|
||
|
||
messages: [], // 鏄剧ず鐨勬秷鎭?
|
||
paidMessages: [], // 鍥哄畾鍦ㄤ笂鏂圭殑娑堟伅
|
||
|
||
smoothedMessageQueue: [], // 骞虫粦娑堟伅闃熷垪锛岀敱澶栭儴璋冪敤addMessages绛夋柟娉曟坊鍔?
|
||
emitSmoothedMessageTimerId: null, // 娑堣垂骞虫粦娑堟伅闃熷垪鐨勫畾鏃跺櫒ID
|
||
enqueueIntervals: [], // 鏈€杩戣繘闃熷垪鐨勬椂闂撮棿闅旓紝鐢ㄦ潵浼拌涓嬫杩涢槦鍒楃殑鏃堕棿
|
||
lastEnqueueTime: null, // 涓婃杩涢槦鍒楃殑鏃堕棿
|
||
estimatedEnqueueInterval: null, // 浼拌鐨勪笅娆¤繘闃熷垪鏃堕棿闂撮殧
|
||
|
||
messagesBuffer: [], // 鏆傛椂鏈樉绀虹殑娑堟伅锛屽綋涓嶈兘鑷姩婊氬姩鏃朵細绉帇鍦ㄨ繖
|
||
preinsertHeight: 0, // 鎻掑叆鏂版秷鎭箣鍓峣tems鐨勯珮搴?
|
||
isSmoothed: true, // 鏄惁骞虫粦婊氬姩锛屽綋娑堟伅澶揩鏃朵笉骞虫粦婊氬姩
|
||
chatRateMs: 1000, // 鐢ㄦ潵璁$畻娑堟伅閫熷害
|
||
scrollPixelsRemaining: 0, // 骞虫粦婊氬姩鍓╀綑鍍忕礌
|
||
scrollTimeRemainingMs: 0, // 骞虫粦婊氬姩鍓╀綑鏃堕棿
|
||
lastSmoothChatMessageAddMs: null, // 涓婃showNewMessages鏃堕棿
|
||
smoothScrollRafHandle: null, // 骞虫粦婊氬姩requestAnimationFrame鍙ユ焺
|
||
lastSmoothScrollUpdate: null, // 骞虫粦婊氬姩涓婁竴甯ф椂闂?
|
||
|
||
atBottom: true, // 婊氬姩鍒板簳閮紝鐢ㄦ潵鍒ゆ柇鑳藉惁鑷姩婊氬姩
|
||
cantScrollStartTime: null, // 寮€濮嬩笉鑳借嚜鍔ㄦ粴鍔ㄧ殑鏃堕棿锛岀敤鏉ラ槻姝㈠崱浣?
|
||
|
||
customStyleElement,
|
||
|
||
setCssDebounce,
|
||
}
|
||
},
|
||
computed: {
|
||
canScrollToBottom() {
|
||
return this.atBottom/* || this.allowScroll */
|
||
}
|
||
},
|
||
watch: {
|
||
canScrollToBottom(val) {
|
||
this.cantScrollStartTime = val ? null : new Date()
|
||
},
|
||
watchCustomCss: {
|
||
immediate: true,
|
||
handler(val, oldVal) {
|
||
this.setCssDebounce(val)
|
||
}
|
||
}
|
||
},
|
||
mounted() {
|
||
this.scrollToBottom()
|
||
},
|
||
beforeUnmount() {
|
||
if (this.emitSmoothedMessageTimerId) {
|
||
window.clearTimeout(this.emitSmoothedMessageTimerId)
|
||
this.emitSmoothedMessageTimerId = null
|
||
}
|
||
this.clearMessages()
|
||
document.head.removeChild(this.customStyleElement)
|
||
},
|
||
methods: {
|
||
getGiftShowContent(message) {
|
||
return constants.getGiftShowContent(message, this.showGiftName)
|
||
},
|
||
getGiftShowNameAndNum: constants.getGiftShowNameAndNum,
|
||
getShowContent: constants.getShowContent,
|
||
getShowContentParts: constants.getShowContentParts,
|
||
getShowAuthorName: constants.getShowAuthorName,
|
||
|
||
addMessage(message) {
|
||
this.addMessages([message])
|
||
},
|
||
addMessages(messages) {
|
||
this.enqueueMessages(messages)
|
||
},
|
||
setCss(css) {
|
||
this.setCssDebounce(css)
|
||
},
|
||
// 鍚庢倲鍔犺繖涓姛鑳戒簡
|
||
mergeSimilarText(content) {
|
||
content = content.trim().toLowerCase()
|
||
for (let message of this.iterRecentMessages(5)) {
|
||
if (message.type !== constants.MESSAGE_TYPE_TEXT) {
|
||
continue
|
||
}
|
||
|
||
let messageContent = message.content.trim().toLowerCase()
|
||
let longer, shorter
|
||
if (messageContent.length > content.length) {
|
||
longer = messageContent
|
||
shorter = content
|
||
} else {
|
||
longer = content
|
||
shorter = messageContent
|
||
}
|
||
|
||
if (
|
||
longer.indexOf(shorter) !== -1 // 闀跨殑鍖呭惈鐭殑
|
||
&& longer.length - shorter.length < shorter.length // 闀垮害宸緝灏?
|
||
) {
|
||
this.updateMessage(message.id, {
|
||
$add: {
|
||
repeated: 1
|
||
}
|
||
})
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
},
|
||
mergeSimilarGift(authorName, price, _freePrice, giftName, num) {
|
||
for (let message of this.iterRecentMessages(5)) {
|
||
if (
|
||
message.type === constants.MESSAGE_TYPE_GIFT
|
||
&& message.authorName === authorName
|
||
&& message.giftName === giftName
|
||
) {
|
||
this.updateMessage(message.id, {
|
||
$add: {
|
||
price: price,
|
||
// freePrice: freePrice, // 鏆傛椂娌$敤鍒?
|
||
num: num
|
||
}
|
||
})
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
},
|
||
// 浠庢柊鍒拌€佽凯浠um鏉℃秷鎭紝娉ㄦ剰浼氳凯浠moothedMessageQueue锛屼笉浼氳凯浠aidMessages
|
||
*iterRecentMessages(num, onlyCountAddMessages = true) {
|
||
if (num <= 0) {
|
||
return
|
||
}
|
||
for (let arr of this.iterMessageArrs()) {
|
||
for (let i = arr.length - 1; i >= 0 && num > 0; i--) {
|
||
let message = arr[i]
|
||
yield message
|
||
if (!onlyCountAddMessages || this.isAddMessage(message)) {
|
||
num--
|
||
}
|
||
}
|
||
if (num <= 0) {
|
||
break
|
||
}
|
||
}
|
||
},
|
||
// 浠庢柊鍒拌€佽凯浠f秷鎭殑鏁扮粍
|
||
*iterMessageArrs() {
|
||
for (let i = this.smoothedMessageQueue.length - 1; i >= 0; i--) {
|
||
yield this.smoothedMessageQueue[i]
|
||
}
|
||
yield this.messagesBuffer
|
||
yield this.messages
|
||
},
|
||
delMessage(id) {
|
||
this.delMessages([id])
|
||
},
|
||
delMessages(ids) {
|
||
this.enqueueMessages(ids.map(
|
||
id => ({
|
||
type: constants.MESSAGE_TYPE_DEL,
|
||
id
|
||
})
|
||
))
|
||
},
|
||
clearMessages() {
|
||
this.messages = []
|
||
this.paidMessages = []
|
||
this.smoothedMessageQueue = []
|
||
this.messagesBuffer = []
|
||
this.isSmoothed = true
|
||
this.lastSmoothChatMessageAddMs = null
|
||
this.chatRateMs = 1000
|
||
this.lastSmoothScrollUpdate = null
|
||
this.scrollTimeRemainingMs = this.scrollPixelsRemaining = 0
|
||
this.smoothScrollRafHandle = null
|
||
this.preinsertHeight = 0
|
||
this.maybeResizeScrollContainer()
|
||
if (!this.atBottom) {
|
||
this.scrollToBottom()
|
||
}
|
||
},
|
||
updateMessage(id, newValuesObj) {
|
||
this.enqueueMessages([{
|
||
type: constants.MESSAGE_TYPE_UPDATE,
|
||
id,
|
||
newValuesObj
|
||
}])
|
||
},
|
||
|
||
enqueueMessages(messages) {
|
||
// 浼拌杩涢槦鍒楁椂闂撮棿闅?
|
||
if (!this.lastEnqueueTime) {
|
||
this.lastEnqueueTime = new Date()
|
||
} else {
|
||
let curTime = new Date()
|
||
let interval = curTime - this.lastEnqueueTime
|
||
// 鐪熷疄鐨勮繘闃熷垪鏃堕棿闂撮殧妯″紡澶ф鏄繖鏍凤細2500, 300, 300, 300, 2500, 300, ...
|
||
// B绔欐秷鎭湁缂撳啿锛屼細涓€娆″彂澶氭潯娑堟伅銆傝繖閲屾妸娉㈠嘲瑙嗕负鍙戦€佷簡涓€娆$湡瀹炵殑WS娑堟伅锛屾墍浠ヨ杩囨护鎺夐棿闅斿お灏忕殑
|
||
if (interval > 1000 || this.enqueueIntervals.length < 5) {
|
||
this.enqueueIntervals.push(interval)
|
||
if (this.enqueueIntervals.length > 5) {
|
||
this.enqueueIntervals.splice(0, this.enqueueIntervals.length - 5)
|
||
}
|
||
// 杩欒竟浼拌寰楀敖閲忓ぇ锛屽彧瑕佷笉澶棭鎶婃秷鎭紦鍐插彂瀹屽氨鏄钩婊戠殑銆傛湁MESSAGE_MAX_INTERVAL淇濆簳锛屼笉浼氳娑堟伅寤惰繜澶ぇ
|
||
// 鍏跺疄鍙互鐢ㄥ崟璋冮槦鍒楁眰鏈€澶у€硷紝鍋锋噿涓嶅啓浜?
|
||
this.estimatedEnqueueInterval = Math.max(...this.enqueueIntervals)
|
||
}
|
||
// 涓婃鍏ラ槦鏃堕棿杩樻槸瑕佽缃紝鍚﹀垯浼氬お鏃╂妸娑堟伅缂撳啿鍙戝畬锛岀劧鍚庤緝闀挎椂闂存病鏈夋柊娑堟伅
|
||
this.lastEnqueueTime = curTime
|
||
}
|
||
|
||
// 鎶妋essages鍒嗘垚messageGroup锛屾瘡涓粍閲屾渶澶氭湁1涓渶瑕佸钩婊戠殑娑堟伅
|
||
let messageGroup = []
|
||
for (let message of messages) {
|
||
messageGroup.push(message)
|
||
if (this.isAddMessage(message)) {
|
||
this.smoothedMessageQueue.push(messageGroup)
|
||
messageGroup = []
|
||
}
|
||
}
|
||
// 杩樺墿涓嬩笉闇€瑕佸钩婊戠殑娑堟伅
|
||
if (messageGroup.length > 0) {
|
||
if (this.smoothedMessageQueue.length > 0) {
|
||
// 鍜屼笂涓€缁勫悎骞?
|
||
let lastMessageGroup = this.smoothedMessageQueue[this.smoothedMessageQueue.length - 1]
|
||
for (let message of messageGroup) {
|
||
lastMessageGroup.push(message)
|
||
}
|
||
} else {
|
||
// 鑷繁涓€涓粍
|
||
this.smoothedMessageQueue.push(messageGroup)
|
||
}
|
||
}
|
||
|
||
if (!this.emitSmoothedMessageTimerId) {
|
||
this.emitSmoothedMessageTimerId = window.setTimeout(this.emitSmoothedMessages)
|
||
}
|
||
},
|
||
isAddMessage({ type }) {
|
||
return ADD_MESSAGE_TYPES.indexOf(type) !== -1
|
||
},
|
||
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)
|
||
}
|
||
// 璁$畻鍙戦€佺殑娑堟伅鏁帮紝淇濊瘉鍦ㄤ笅娆¤繘闃熷垪涔嬪墠鍙戝畬
|
||
// 涓嬫杩涢槦鍒椾箣鍓嶅簲璇ュ彂澶氬皯鏉℃秷鎭?
|
||
let shouldEmitGroupNum = Math.max(this.smoothedMessageQueue.length, 0)
|
||
// 涓嬫杩涢槦鍒椾箣鍓嶆渶澶氳兘鍙戝灏戞
|
||
let maxCanEmitCount = estimatedNextEnqueueRemainTime / MESSAGE_MIN_INTERVAL
|
||
// 杩欐鍙戝灏戞潯娑堟伅
|
||
let groupNumToEmit
|
||
if (shouldEmitGroupNum < maxCanEmitCount) {
|
||
// 闃熷垪涓秷鎭暟寰堝皯锛屾瘡娆″彂1鏉′篃鑳藉彂瀹?
|
||
groupNumToEmit = 1
|
||
} else {
|
||
// 姣忔鍙?鏉′互涓婏紝淇濊瘉鎸夋渶蹇€熷害鑳藉彂瀹?
|
||
groupNumToEmit = Math.ceil(shouldEmitGroupNum / maxCanEmitCount)
|
||
}
|
||
|
||
// 鍙戞秷鎭?
|
||
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) {
|
||
// 闃熷垪涓秷鎭暟寰堝皯锛岄殢渚垮畾涓猍MESSAGE_MIN_INTERVAL, MESSAGE_MAX_INTERVAL]鐨勬椂闂?
|
||
sleepTime = estimatedNextEnqueueRemainTime / this.smoothedMessageQueue.length
|
||
sleepTime *= 0.5 + Math.random()
|
||
if (sleepTime > MESSAGE_MAX_INTERVAL) {
|
||
sleepTime = MESSAGE_MAX_INTERVAL
|
||
} else if (sleepTime < MESSAGE_MIN_INTERVAL) {
|
||
sleepTime = MESSAGE_MIN_INTERVAL
|
||
}
|
||
} else {
|
||
// 鎸夋渶蹇€熷害鍙?
|
||
sleepTime = MESSAGE_MIN_INTERVAL
|
||
}
|
||
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_GIFT:
|
||
case constants.MESSAGE_TYPE_MEMBER:
|
||
case constants.MESSAGE_TYPE_SUPER_CHAT:
|
||
// 杩欓噷澶勭悊鐨勭被鍨嬭鍜?ADD_MESSAGE_TYPES 涓€鑷?
|
||
this.handleAddMessage(message)
|
||
break
|
||
case constants.MESSAGE_TYPE_DEL:
|
||
this.handleDelMessage(message)
|
||
break
|
||
case constants.MESSAGE_TYPE_UPDATE:
|
||
this.handleUpdateMessage(message)
|
||
break
|
||
}
|
||
}
|
||
|
||
this.maybeResizeScrollContainer()
|
||
this.flushMessagesBuffer()
|
||
this.$nextTick(this.maybeScrollToBottom)
|
||
},
|
||
handleAddMessage(message) {
|
||
// 娣诲姞涓€涓湰鍦版椂闂寸粰Ticker鐢紝闃叉鏈湴鏃堕棿鍜屾湇鍔″櫒鏃堕棿鐩稿樊寰堝ぇ鐨勬儏鍐?
|
||
message.addTime = new Date()
|
||
|
||
if (message.type !== constants.MESSAGE_TYPE_TEXT) {
|
||
this.paidMessages.unshift(_.cloneDeep(message))
|
||
const MAX_PAID_MESSAGE_NUM = 100
|
||
if (this.paidMessages.length > MAX_PAID_MESSAGE_NUM) {
|
||
this.paidMessages.splice(MAX_PAID_MESSAGE_NUM, this.paidMessages.length - MAX_PAID_MESSAGE_NUM)
|
||
}
|
||
}
|
||
|
||
// 涓嶇煡閬揷loneDeep鎷疯礉Vue鐨勫搷搴斿紡瀵硅薄浼氫笉浼氭湁闂锛屼繚闄╄捣瑙佹妸杩欏彞鏀惧湪鍚庨潰
|
||
this.messagesBuffer.push(message)
|
||
},
|
||
handleDelMessage({ id }) {
|
||
let arrs = [this.messages, this.paidMessages, this.messagesBuffer]
|
||
let needResetSmoothScroll = false
|
||
for (let arr of arrs) {
|
||
for (let i = 0; i < arr.length; i++) {
|
||
if (arr[i].id !== id) {
|
||
continue
|
||
}
|
||
arr.splice(i, 1)
|
||
if (arr === this.messages) {
|
||
needResetSmoothScroll = true
|
||
}
|
||
break
|
||
}
|
||
}
|
||
if (needResetSmoothScroll) {
|
||
this.resetSmoothScroll()
|
||
}
|
||
},
|
||
handleUpdateMessage({ id, newValuesObj }) {
|
||
let arrs = [this.messages, this.paidMessages, this.messagesBuffer]
|
||
let needResetSmoothScroll = false
|
||
for (let arr of arrs) {
|
||
for (let message of arr) {
|
||
if (message.id !== id) {
|
||
continue
|
||
}
|
||
this.doUpdateMessage(message, newValuesObj)
|
||
if (arr === this.messages) {
|
||
needResetSmoothScroll = true
|
||
}
|
||
break
|
||
}
|
||
}
|
||
if (needResetSmoothScroll) {
|
||
this.resetSmoothScroll()
|
||
}
|
||
},
|
||
doUpdateMessage(message, newValuesObj) {
|
||
// +=
|
||
let addValuesObj = newValuesObj.$add
|
||
if (addValuesObj !== undefined) {
|
||
for (let name in addValuesObj) {
|
||
message[name] += addValuesObj[name]
|
||
}
|
||
}
|
||
|
||
// =
|
||
for (let name in newValuesObj) {
|
||
if (!name.startsWith('$')) {
|
||
message[name] = newValuesObj[name]
|
||
}
|
||
}
|
||
},
|
||
|
||
async flushMessagesBuffer() {
|
||
if (this.messagesBuffer.length <= 0) {
|
||
return
|
||
}
|
||
if (!this.canScrollToBottomOrTimedOut()) {
|
||
if (this.messagesBuffer.length > this.maxNumber) {
|
||
// 鏈樉绀烘秷鎭暟 > 鏈€澶у彲鏄剧ず鏁帮紝涓㈠純
|
||
this.messagesBuffer.splice(0, this.messagesBuffer.length - this.maxNumber)
|
||
}
|
||
return
|
||
}
|
||
|
||
let removeNum = Math.max(this.messages.length + this.messagesBuffer.length - this.maxNumber, 0)
|
||
if (removeNum > 0) {
|
||
this.messages.splice(0, removeNum)
|
||
// 闃叉鍚屾椂娣诲姞鍜屽垹闄ら」鐩椂鎵€鏈夌殑椤圭洰閲嶆柊娓叉煋 https://github.com/vuejs/vue/issues/6857
|
||
await this.$nextTick()
|
||
}
|
||
|
||
this.preinsertHeight = this.$refs.items.clientHeight
|
||
for (let message of this.messagesBuffer) {
|
||
this.messages.push(message)
|
||
}
|
||
this.messagesBuffer = []
|
||
// 绛塱tems楂樺害鍙樺寲
|
||
await this.$nextTick()
|
||
this.showNewMessages()
|
||
},
|
||
showNewMessages() {
|
||
let hasScrollBar = this.$refs.items.clientHeight > this.$refs.scroller.clientHeight
|
||
this.$refs.itemOffset.style.height = `${this.$refs.items.clientHeight}px`
|
||
if (!this.canScrollToBottomOrTimedOut() || !hasScrollBar) {
|
||
return
|
||
}
|
||
|
||
// 璁$畻鍓╀綑鍍忕礌
|
||
this.scrollPixelsRemaining += this.$refs.items.clientHeight - this.preinsertHeight
|
||
this.scrollToBottom()
|
||
|
||
// 璁$畻鏄惁骞虫粦婊氬姩銆佸墿浣欐椂闂?
|
||
if (!this.lastSmoothChatMessageAddMs) {
|
||
this.lastSmoothChatMessageAddMs = performance.now()
|
||
}
|
||
let interval = performance.now() - this.lastSmoothChatMessageAddMs
|
||
this.chatRateMs = (0.9 * this.chatRateMs) + (0.1 * interval)
|
||
if (this.isSmoothed) {
|
||
if (this.chatRateMs < 400) {
|
||
this.isSmoothed = false
|
||
}
|
||
} else {
|
||
if (this.chatRateMs > 450) {
|
||
this.isSmoothed = true
|
||
}
|
||
}
|
||
this.scrollTimeRemainingMs += this.isSmoothed ? CHAT_SMOOTH_ANIMATION_TIME_MS : 0
|
||
|
||
if (!this.smoothScrollRafHandle) {
|
||
this.smoothScrollRafHandle = window.requestAnimationFrame(this.smoothScroll)
|
||
}
|
||
this.lastSmoothChatMessageAddMs = performance.now()
|
||
},
|
||
smoothScroll(time) {
|
||
if (!this.lastSmoothScrollUpdate) {
|
||
// 绗竴甯?
|
||
this.lastSmoothScrollUpdate = time
|
||
this.smoothScrollRafHandle = window.requestAnimationFrame(this.smoothScroll)
|
||
return
|
||
}
|
||
|
||
let interval = time - this.lastSmoothScrollUpdate
|
||
if (
|
||
this.scrollPixelsRemaining <= 0 || this.scrollPixelsRemaining >= 400 // 宸茬粡婊氬姩鍒板簳閮ㄦ垨鑰呯搴曢儴澶繙鍒欑粨鏉?
|
||
|| interval >= 1000 // 绂讳笂涓€甯ф椂闂村お涔咃紝鍙兘鐢ㄦ埛鍒囨崲鍒板叾浠栫綉椤?
|
||
|| this.scrollTimeRemainingMs <= 0 // 鏃堕棿宸茬粨鏉?
|
||
) {
|
||
this.resetSmoothScroll()
|
||
return
|
||
}
|
||
|
||
let pixelsToScroll = interval / this.scrollTimeRemainingMs * this.scrollPixelsRemaining
|
||
this.scrollPixelsRemaining -= pixelsToScroll
|
||
if (this.scrollPixelsRemaining < 0) {
|
||
this.scrollPixelsRemaining = 0
|
||
}
|
||
this.scrollTimeRemainingMs -= interval
|
||
if (this.scrollTimeRemainingMs < 0) {
|
||
this.scrollTimeRemainingMs = 0
|
||
}
|
||
this.lastSmoothScrollUpdate = time
|
||
this.smoothScrollRafHandle = window.requestAnimationFrame(this.smoothScroll)
|
||
},
|
||
resetSmoothScroll() {
|
||
this.scrollTimeRemainingMs = this.scrollPixelsRemaining = 0
|
||
this.lastSmoothScrollUpdate = null
|
||
if (this.smoothScrollRafHandle) {
|
||
window.cancelAnimationFrame(this.smoothScrollRafHandle)
|
||
this.smoothScrollRafHandle = null
|
||
}
|
||
},
|
||
|
||
maybeResizeScrollContainer() {
|
||
this.$refs.itemOffset.style.height = `${this.$refs.items.clientHeight}px`
|
||
this.$refs.itemOffset.style.minHeight = `${this.$refs.scroller.clientHeight}px`
|
||
this.maybeScrollToBottom()
|
||
},
|
||
maybeScrollToBottom() {
|
||
if (this.canScrollToBottomOrTimedOut()) {
|
||
this.scrollToBottom()
|
||
}
|
||
},
|
||
scrollToBottom() {
|
||
this.$refs.scroller.scrollTop = Math.pow(2, 24)
|
||
this.atBottom = true
|
||
},
|
||
onScroll() {
|
||
this.refreshCantScrollStartTime()
|
||
let scroller = this.$refs.scroller
|
||
this.atBottom = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight < SCROLLED_TO_BOTTOM_EPSILON
|
||
this.flushMessagesBuffer()
|
||
},
|
||
canScrollToBottomOrTimedOut() {
|
||
if (this.canScrollToBottom) {
|
||
return true
|
||
}
|
||
// 闃叉鍦∣BS涓崱浣忥紝瓒呰繃涓€瀹氭椂闂翠篃鍙互鑷姩婊氬姩
|
||
return new Date() - this.cantScrollStartTime >= 5 * 1000
|
||
},
|
||
refreshCantScrollStartTime() {
|
||
// 鏈夐紶鏍囦簨浠舵椂鍒锋柊锛岄槻姝㈢敤鎴风湅寮瑰箷鏃惰嚜鍔ㄦ粴鍔?
|
||
if (this.cantScrollStartTime) {
|
||
this.cantScrollStartTime = new Date()
|
||
}
|
||
}
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style src="@/assets/css/youtube/yt-html.css"></style>
|
||
<style src="@/assets/css/youtube/yt-live-chat-renderer.css"></style>
|
||
<style src="@/assets/css/youtube/yt-live-chat-item-list-renderer.css"></style>
|