一个简单的广播队列

记录一些需求开发中的小代码,如果以后有类似的功能需求,可快速复用。

调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import BroadcastQueue from './BroadcastQueue';

// 初始化调用
this.broadcastQueue = new BroadcastQueue((data) => {
this.showHighlightTip(data);
});


showHighlightTip(queueItem) {
// 取出队列 item后的处理逻辑
...
},

// 收到广播的时候,一条条push进队列
this.broadcastQueue.push({
tip,
showBtn: false,
order: 3,
});

// 清除队列
this.broadcastQueue.clear()

BroadcastQueue.js

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 广播队列

export default class BroadcastQueue {
constructor(onData) {
this.onData = onData;
this.queue = [];
this.isRun = false;
}

/**
* 向队列中增加广播
* @param {Object} data
* @param {Object} tip 广播内容
* @param {boolean} showBtn 是否展示围观按钮
* @param {number} order 优先顺序,值较小的优先展示,如果值为-1表示立即展示
*/
push(data) {
console.log('BroadcastQueue().push', JSON.stringify(data));
if (data.order == null) { // eslint-disable-line
// 如果没有优先顺序,默认为低优先级,给一个较大的序号
data.order = 100; // eslint-disable-line
}

if (this.queue.length === 0) {
this.queue.push(data);
} else {
for (let i = 0; i < this.queue.length; i += 1) {
const item = this.queue[i];
if (data.order < item.order) {
this.queue.splice(i, 0, data);
break;
} else if (i === this.queue.length - 1) {
this.queue.push(data);
break;
}
}
}

this.run();
}

run() {
if (this.isRun) {
return;
}
this.isRun = true;
this.next();
}

next() {
if (this.queue.length === 0) {
this.isRun = false;
return;
}

const data = this.queue.shift();
this.onData(data);
setTimeout(() => {
this.next();
}, 10000);
}

// 只清除order不为1的广播
clear() {
this.queue = this.queue.filter(item => item.order === 1);
this.isRun = false;
}
}

其余部分代码:

因为广播展示区域高度是固定28px, 所以当内容高度大于容器高度时,需要一个滚动动画。
smartMarquee.vue

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<template>
<div class="smart-marquee" :style="{height: `${height}px`}">
<div ref="content">
<slot></slot>
</div>
</div>
</template>

<script>
export default {
props: ['height'],

data() {
return {
diffH: 0, // 内容高于容器的高度
offset: 0, // 位移量,单位为px
};
},

mounted() {
this.initScroll();
},

methods: {
initScroll() {
this.diffH = this.$refs.content.clientHeight - this.height;
console.log('diff', this.diffH, 'contentH', this.$refs.content.clientHeight);
if (this.diffH <= 10) {
return; // 内容小于或略大于容器高度,不需要滚动
}

setTimeout(() => {
this.scroll();
}, 500);
},

// 滚动
scroll() {
window.requestAnimationFrame(() => {
if (this.offset < this.diffH) {
this.offset += 0.2;
this.$refs.content.style.transform = `translateY(-${this.offset}px)`;
this.scroll(); // 递归调用,继续滚动
} else { // 已经完成滚动,重新开始
// 过一段时间后再重新开始
setTimeout(() => {
this.offset = 0;
this.$refs.content.style.transform = 'translateY(0)';
this.scroll();
}, 500);
}
});
},
},
};
</script>

<style lang="scss" scoped>
.smart-marquee {
position: relative;
overflow: hidden;
.name {
max-width: 71px;
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
vertical-align: middle;
margin-top: -3px;
}
}
</style>

1
2
3
4
5
6
<smart-marquee :height="28">
<span v-for="(item, index) in data.tips.content.text"
:key="index"
:style="{color: item.color, fontSize: item.size + 'px'}"
>{{utils.base64_utf8_decode(item.text)}}</span>
</smart-marquee>