直播是眼下最为火爆的行业,而弹幕无疑是直播平台中最流行、最重要的功能之一。本文将讲述如何实现兼容 PC 浏览器和移动浏览器的弹幕。
基本功能
并发与队列
一般来说,弹幕数据会通过异步请求或 socket 消息传到前端,这里会存在一个隐患——数据量可能非常大。如果一收到弹幕数据就马上渲染出来,在量大的时候:
显示区域不足以放置这么多的弹幕,弹幕会堆叠在一起;渲染过程会占用大量 CPU 资源,导致页面卡顿。所以在接收和渲染数据之间,要引入队列做缓冲。把收到的弹幕数据都存入数组(即下文代码中的 this._queue),再通过轮询该数组,把弹幕逐条渲染出来:
class Danmaku {
// 省略 N 行代码...
add(data) {
this._queue.push(this._parseData(data));
if (!this._renderTimer) { this._render(); }
}
_render() {
try {
this._renderToDOM();
} finally {
this._renderEnd();
}
}
_renderEnd() {
if (this._queue.length > 0) {
this._renderTimer = setTimeout(() => {
this._render();
}, this._renderInterval);
} else {
this._renderTimer = null;
}
}
// 省略 N 行代码...}
弹幕的滚动
弹幕的滚动本质上是位移动画,从显示区域的右侧移动到左侧。前端实现位移动画有两种方案——DOM 和 canvas。
DOM 方案实现的动画较为流畅,且一些特殊效果(如文字阴影)较容易实现(只要在 CSS 中设置对应的属性即可)。Canvas 方案的动画流畅度要差一些,要做特殊效果也不那么容易,但是它在 CPU 占用上有优势。
本文将以 DOM 方案实现弹幕的滚动,并通过 CSS 的 transition 和 transform 来实现动画,这样可以利用浏览器渲染过程中的「合成层」机制(有兴趣可以查阅这篇文章),提高性能。弹幕滚动的示例代码如下:
var $item = $('.danmaku-item').css({
left: '100%',
'transition-duration': '2s',
'transition-property': 'transform',
'will-change': 'transform'
});
setTimeout(function() {
$item.css(
'transform',
`translateX(${
-$item.width() + container.offsetWidth})`
);
}, 1000);
弹幕的渲染
在 DOM 方案下,每条弹幕对应一个 HTML 元素,把元素的样式都设定好之后,就可以添加到 HTML 文档里面:
class Danmaku {
// 省略 N 行代码...
_renderToDOM() {
const data = this._queue[0];
let node = data.node;
if (!node) {
data.node = node = document.createElement('div');
node.innerText = data.msg;
node.style.position = 'absolute';
node.style.left = '100%';
node.style.whiteSpace = 'nowrap';
node.style.color = data.fontColor;
node.style.fontSize = data.fontSize + 'px';
node.style.willChange = 'transform';
this._container.appendChild(node)