封装addMessage到ChatRenderer

暂时移除了messagesBuffer
This commit is contained in:
John Smith 2019-11-24 18:48:10 +08:00
parent 9565c52e23
commit 3b2616c278
8 changed files with 118 additions and 152 deletions

View File

@ -7,7 +7,6 @@ export const DEFAULT_CONFIG = {
mergeSimilarDanmaku: true, mergeSimilarDanmaku: true,
showDanmaku: true, showDanmaku: true,
showGift: true, showGift: true,
maxSpeed: 4,
maxNumber: 60, maxNumber: 60,
blockGiftDanmaku: true, blockGiftDanmaku: true,

View File

@ -31,6 +31,7 @@
</template> </template>
<script> <script>
import config from '@/api/config'
import Ticker from './Ticker.vue' import Ticker from './Ticker.vue'
import TextMessage from './TextMessage.vue' import TextMessage from './TextMessage.vue'
import LegacyPaidMessage from './LegacyPaidMessage.vue' import LegacyPaidMessage from './LegacyPaidMessage.vue'
@ -49,9 +50,11 @@ export default {
PaidMessage PaidMessage
}, },
props: { props: {
paidMessages: Array, css: String,
messages: Array, maxNumber: {
css: String type: Number,
default: config.DEFAULT_CONFIG.maxNumber
}
}, },
data() { data() {
let styleElement = document.createElement('style') let styleElement = document.createElement('style')
@ -61,6 +64,8 @@ export default {
MESSAGE_TYPE_MEMBER: constants.MESSAGE_TYPE_MEMBER, MESSAGE_TYPE_MEMBER: constants.MESSAGE_TYPE_MEMBER,
styleElement, styleElement,
messages: [],
paidMessages: [],
canAutoScroll: true canAutoScroll: true
} }
}, },
@ -71,17 +76,72 @@ export default {
beforeDestroy() { beforeDestroy() {
document.head.removeChild(this.styleElement) document.head.removeChild(this.styleElement)
}, },
updated() {
if (this.canAutoScroll) {
this.scrollToBottom()
}
},
watch: { watch: {
css(val) { css(val) {
this.styleElement.innerText = val this.styleElement.innerText = val
} }
}, },
methods: { methods: {
addMessage(message) {
this.addMessages([message])
},
addMessages(messages) {
for (let message of 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) {
for (let i = this.messages.length - 1; i >= 0 && i >= this.messages.length - 5; i--) {
let message = this.messages[i]
let longer, shorter
if (message.content.length > content.length) {
longer = message.content
shorter = content
} else {
longer = content
shorter = message.content
}
if (longer.indexOf(shorter) !== -1 //
&& longer.length - shorter.length < shorter.length //
) {
message.repeated++
return true
}
}
return false
},
delMessage(id) {
for (let arr of [this.messages, this.paidMessages]) {
for (let i = 0; i < arr.length; i++) {
if (arr[i].id === id) {
arr.splice(i, 1)
break
}
}
}
},
clearMessages() {
this.messages = []
this.paidMessages = []
},
scrollToBottom() { scrollToBottom() {
this.$refs.scroller.scrollTop = this.$refs.scroller.scrollHeight this.$refs.scroller.scrollTop = this.$refs.scroller.scrollHeight
}, },

View File

@ -15,7 +15,6 @@ export default {
showGift: 'Show Super Chats', showGift: 'Show Super Chats',
mergeSimilarDanmaku: 'Merge similar messages', mergeSimilarDanmaku: 'Merge similar messages',
minGiftPrice: 'Min price of Super Chats to show (CNY)', minGiftPrice: 'Min price of Super Chats to show (CNY)',
maxSpeed: 'Max speed of messages (/second, 0 for unlimited)',
maxNumber: 'Max number of messages', maxNumber: 'Max number of messages',
block: 'Block', block: 'Block',

View File

@ -15,7 +15,6 @@
showGift: 'スーパーチャットと新メンバーを表示する', showGift: 'スーパーチャットと新メンバーを表示する',
mergeSimilarDanmaku: '同じコメントを合併する', mergeSimilarDanmaku: '同じコメントを合併する',
minGiftPrice: '最低表示スーパーチャット価格CNY', minGiftPrice: '最低表示スーパーチャット価格CNY',
maxSpeed: 'コメントの最大速度(バー/秒、0は無制限',
maxNumber: '最大コメント数', maxNumber: '最大コメント数',
block: 'ブロック', block: 'ブロック',

View File

@ -15,7 +15,6 @@ export default {
showGift: '显示打赏和新舰长', showGift: '显示打赏和新舰长',
mergeSimilarDanmaku: '合并相似弹幕', mergeSimilarDanmaku: '合并相似弹幕',
minGiftPrice: '最低显示打赏价格(元)', minGiftPrice: '最低显示打赏价格(元)',
maxSpeed: '弹幕最大速度(条/秒0为无限制',
maxNumber: '最大弹幕数', maxNumber: '最大弹幕数',
block: '屏蔽', block: '屏蔽',

View File

@ -22,9 +22,6 @@
<el-form-item :label="$t('home.minGiftPrice')"> <el-form-item :label="$t('home.minGiftPrice')">
<el-input v-model.number="form.minGiftPrice" type="number" min="0"></el-input> <el-input v-model.number="form.minGiftPrice" type="number" min="0"></el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('home.maxSpeed')">
<el-input v-model.number="form.maxSpeed" type="number" min="0"></el-input>
</el-form-item>
<el-form-item :label="$t('home.maxNumber')"> <el-form-item :label="$t('home.maxNumber')">
<el-input v-model.number="form.maxNumber" type="number" min="1"></el-input> <el-input v-model.number="form.maxNumber" type="number" min="1"></el-input>
</el-form-item> </el-form-item>

View File

@ -1,5 +1,5 @@
<template> <template>
<chat-renderer :paidMessages="paidMessages" :messages="messages" :css="config.css"></chat-renderer> <chat-renderer ref="renderer" :css="config.css" :maxNumber="config.maxNumber"></chat-renderer>
</template> </template>
<script> <script>
@ -21,23 +21,23 @@ export default {
ChatRenderer ChatRenderer
}, },
data() { data() {
let cfg = {...config.DEFAULT_CONFIG}
cfg.blockKeywords = cfg.blockKeywords.split('\n').filter(val => val)
cfg.blockUsers = cfg.blockUsers.split('\n').filter(val => val)
cfg.maxSpeed = 0
return { return {
config: cfg, config: {...config.DEFAULT_CONFIG},
websocket: null, websocket: null,
retryCount: 0, retryCount: 0,
isDestroying: false, isDestroying: false,
heartbeatTimerId: null, heartbeatTimerId: null,
messagesBufferTimerId: null,
nextId: 0, nextId: 0,
messagesBuffer: [], // }
messages: [], // },
paidMessages: [] computed: {
blockKeywords() {
return this.config.blockKeywords.split('\n').filter(val => val)
},
blockUsers() {
return this.config.blockUsers.split('\n').filter(val => val)
} }
}, },
created() { created() {
@ -47,30 +47,13 @@ export default {
} }
}, },
beforeDestroy() { beforeDestroy() {
if (this.messagesBufferTimerId) {
window.clearInterval(this.messagesBufferTimerId)
}
this.isDestroying = true this.isDestroying = true
this.websocket.close() this.websocket.close()
}, },
watch: {
config(val) {
if (this.messagesBufferTimerId) {
window.clearInterval(this.messagesBufferTimerId)
this.messagesBufferTimerId = null
}
if (val.maxSpeed > 0) {
this.messagesBufferTimerId = window.setInterval(this.handleMessagesBuffer, 1000 / val.maxSpeed)
}
}
},
methods: { methods: {
async updateConfig(configId) { async updateConfig(configId) {
try { try {
let cfg = await config.getRemoteConfig(configId) this.config = await config.getRemoteConfig(configId)
cfg.blockKeywords = cfg.blockKeywords.split('\n').filter(val => val)
cfg.blockUsers = cfg.blockUsers.split('\n').filter(val => val)
this.config = cfg
} catch (e) { } catch (e) {
this.$message.error('获取配置失败:' + e) this.$message.error('获取配置失败:' + e)
} }
@ -182,23 +165,15 @@ export default {
content: data.content content: data.content
} }
break break
case COMMAND_DEL_SUPER_CHAT: { case COMMAND_DEL_SUPER_CHAT:
let arrays = [this.messages, this.messagesBuffer, this.paidMessages]
for (let id of data.ids) { for (let id of data.ids) {
id = `sc_${id}` id = `sc_${id}`
for (let arr of arrays) { this.$refs.renderer.delMessage(id)
for (let i = 0; i < arr.length; i++) {
if (arr[i].id === id) {
arr.splice(i, 1)
}
}
}
} }
break break
} }
}
if (message) { if (message) {
this.addMessageBuffer(message) this.$refs.renderer.addMessage(message)
} }
}, },
filterTextMessage(data) { filterTextMessage(data) {
@ -213,25 +188,15 @@ export default {
} else if (this.config.blockMedalLevel > 0 && data.medalLevel < this.config.blockMedalLevel) { } else if (this.config.blockMedalLevel > 0 && data.medalLevel < this.config.blockMedalLevel) {
return false return false
} }
for (let keyword of this.config.blockKeywords) { return this.filterSuperChatMessage(data)
if (data.content.indexOf(keyword) !== -1) {
return false
}
}
for (let user of this.config.blockUsers) {
if (data.authorName === user) {
return false
}
}
return true
}, },
filterSuperChatMessage(data) { filterSuperChatMessage(data) {
for (let keyword of this.config.blockKeywords) { for (let keyword of this.blockKeywords) {
if (data.content.indexOf(keyword) !== -1) { if (data.content.indexOf(keyword) !== -1) {
return false return false
} }
} }
for (let user of this.config.blockUsers) { for (let user of this.blockUsers) {
if (data.authorName === user) { if (data.authorName === user) {
return false return false
} }
@ -242,60 +207,7 @@ export default {
if (!this.config.mergeSimilarDanmaku) { if (!this.config.mergeSimilarDanmaku) {
return false return false
} }
for (let i = this.messages.length - 1; i >= 0 && i >= this.messages.length - 5; i--) { return this.$refs.renderer.mergeSimilar(content)
let message = this.messages[i]
if (
(message.content.indexOf(content) !== -1 || content.indexOf(message.content) !== -1) //
&& Math.abs(message.content.length - content.length) < Math.min(message.content.length, content.length) //
) {
message.repeated++
return true
}
}
return false
},
addMessageBuffer(message) {
if (this.config.maxSpeed > 0 && message.type === constants.MESSAGE_TYPE_TEXT) {
message.addTime = new Date()
this.messagesBuffer.push(message)
} else {
//
this.addMessageShow(message)
}
},
addMessageShow(message) {
message.addTime = new Date()
this.messages.push(message)
if (message.type !== constants.MESSAGE_TYPE_TEXT) {
this.paidMessages.unshift(message)
}
// https://github.com/vuejs/vue/issues/6857
this.$nextTick(() => {
if (this.messages.length > this.config.maxNumber) {
this.messages.splice(0, this.messages.length - this.config.maxNumber)
}
})
},
handleMessagesBuffer() {
// 3
let i
let curTime = new Date()
for (i = 0; i < this.messagesBuffer.length; i++) {
if (curTime - this.messagesBuffer[i].addTime <= 3 * 1000) {
break
}
}
this.messagesBuffer.splice(0, i)
if (this.messagesBuffer.length <= 0) {
return
}
let message = this.messagesBuffer.shift()
//
if (this.mergeSimilar(message.content)) {
this.handleMessagesBuffer()
return
}
this.addMessageShow(message)
} }
} }
} }

View File

@ -188,9 +188,9 @@
</el-form> </el-form>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<div id="example-container" ref="exampleContainer"> <div ref="exampleContainer" id="example-container">
<div id="fakebody"> <div id="fakebody">
<chat-renderer :paidMessages="examplePaidMessages" :messages="messages" :css="exampleCss" ref="example"></chat-renderer> <chat-renderer ref="renderer" :css="exampleCss"></chat-renderer>
</div> </div>
</div> </div>
</el-col> </el-col>
@ -246,7 +246,8 @@ const EXAMPLE_MESSAGES = [
authorName: 'mob路人', authorName: 'mob路人',
content: '8888888888', content: '8888888888',
repeated: 12 repeated: 12
}, { },
{
...textMessageTemplate, ...textMessageTemplate,
id: nextId++, id: nextId++,
authorName: 'member舰长', authorName: 'member舰长',
@ -254,30 +255,35 @@ const EXAMPLE_MESSAGES = [
content: '草', content: '草',
privilegeType: 3, privilegeType: 3,
repeated: 3 repeated: 3
}, { },
{
...textMessageTemplate, ...textMessageTemplate,
id: nextId++, id: nextId++,
authorName: 'admin房管', authorName: 'admin房管',
authorType: constants.AUTHRO_TYPE_ADMIN, authorType: constants.AUTHRO_TYPE_ADMIN,
content: 'kksk' content: 'kksk'
}, { },
{
...legacyPaidMessageTemplate, ...legacyPaidMessageTemplate,
id: nextId++, id: nextId++,
authorName: '吾乐KANA', authorName: '吾乐KANA',
content: 'Welcome 吾乐KANA!' content: 'Welcome 吾乐KANA!'
}, { },
{
...paidMessageTemplate, ...paidMessageTemplate,
id: nextId++, id: nextId++,
authorName: '无火的残渣', authorName: '无火的残渣',
price: 66600, price: 66600,
content: 'Sent 小电视飞船x100' content: 'Sent 小电视飞船x100'
}, { },
{
...textMessageTemplate, ...textMessageTemplate,
id: nextId++, id: nextId++,
authorName: 'streamer主播', authorName: 'streamer主播',
authorType: constants.AUTHRO_TYPE_OWNER, authorType: constants.AUTHRO_TYPE_OWNER,
content: '感谢石油佬送的小电视' content: '感谢石油佬送的小电视'
}, { },
{
...paidMessageTemplate, ...paidMessageTemplate,
id: nextId++, id: nextId++,
authorName: '夏色祭保護協会会長', authorName: '夏色祭保護協会会長',
@ -285,12 +291,6 @@ const EXAMPLE_MESSAGES = [
content: 'Sent 礼花x1' content: 'Sent 礼花x1'
} }
] ]
let examplePaidMessages = []
for (let message of EXAMPLE_MESSAGES) {
if (message.type !== constants.MESSAGE_TYPE_TEXT) {
examplePaidMessages.push(message)
}
}
export default { export default {
name: 'StyleGenerator', name: 'StyleGenerator',
@ -298,15 +298,14 @@ export default {
ChatRenderer ChatRenderer
}, },
data() { data() {
let config = stylegen.getLocalConfig() let stylegenConfig = stylegen.getLocalConfig()
let result = stylegen.getStyle(config) let result = stylegen.getStyle(stylegenConfig)
return { return {
FONTS: [...fonts.LOCAL_FONTS, ...fonts.NETWORK_FONTS], FONTS: [...fonts.LOCAL_FONTS, ...fonts.NETWORK_FONTS],
form: {...config},
form: {...stylegenConfig},
result, result,
exampleCss: result.replace(/^body\b/gm, '#fakebody'), exampleCss: result.replace(/^body\b/gm, '#fakebody'),
messages: EXAMPLE_MESSAGES,
examplePaidMessages
} }
}, },
computed: { computed: {
@ -314,8 +313,19 @@ export default {
return stylegen.getStyle(this.form) return stylegen.getStyle(this.form)
} }
}, },
watch: {
computedResult: _.debounce(function(val) {
this.result = val
stylegen.setLocalConfig(this.form)
}, 500),
result(val) {
this.exampleCss = val.replace(/^body\b/gm, '#fakebody')
}
},
mounted() { mounted() {
let observer = new MutationObserver(() => this.$refs.example.scrollToBottom()) this.$refs.renderer.addMessages(EXAMPLE_MESSAGES)
let observer = new MutationObserver(() => this.$refs.renderer.scrollToBottom())
observer.observe(this.$refs.exampleContainer, {attributes: true}) observer.observe(this.$refs.exampleContainer, {attributes: true})
}, },
methods: { methods: {
@ -330,8 +340,8 @@ export default {
callback(res) callback(res)
}, },
playAnimation() { playAnimation() {
this.messages = [] this.$refs.renderer.clearMessages()
this.$nextTick(() => this.messages = EXAMPLE_MESSAGES) this.$nextTick(() => this.$refs.renderer.addMessages(EXAMPLE_MESSAGES))
}, },
copyResult() { copyResult() {
this.$refs.result.select() this.$refs.result.select()
@ -340,15 +350,6 @@ export default {
resetConfig() { resetConfig() {
this.form = {...stylegen.DEFAULT_CONFIG} this.form = {...stylegen.DEFAULT_CONFIG}
} }
},
watch: {
computedResult: _.debounce(function(val) {
this.result = val
stylegen.setLocalConfig(this.form)
}, 500),
result(val) {
this.exampleCss = val.replace(/^body\b/gm, '#fakebody')
}
} }
} }
</script> </script>