Canvas + WebSocket + Redis 实现一个视频弹幕

在这里插入图片描述


阅读原文


页面布局

首先,我们需要实现页面布局,在根目录创建 index.html 布局中我们需要有一个 video 多媒体标签引入我们的本地视频,添加输入弹幕的输入框、确认发送的按钮、颜色选择器、字体大小滑动条,创建一个 style.css 来调整页面布局的样式,这里我们顺便创建一个 index.js 文件用于后续实现我们的核心逻辑,先引入到页面当中。

HTML 布局代码如下:

<!-- 文件:index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
    <title>视频弹幕</title>
</head>
<body>
    <div id="cantainer">
        <h2>Canvas + WebSocket + Redis 实现视频弹幕</h2>
        <div id="content">
            <canvas id="canvas"></canvas>
            <video id="video" src="./barrage.mp4" controls></video>
        </div>
        <!-- 输入弹幕内容 -->
        <input type="text" id="text">
        <!-- 添加弹幕按钮 -->
        <button id="add">发送</button>
        <!-- 选择文字颜色 -->
        <input type="color" id="color">
        <!-- 调整字体大小 -->
        <input type="range" max="40" min="20" id="range">
    </div>
    <script src="./index.js"></script>
</body>
</html>

CSS 样式代码如下:

/* 文件:style.css */
#cantainer {
   
    text-align: center;
}
#content {
   
    width: 640px;
    margin: 0 auto;
    position: relative;
}
#canvas {
   
    position: absolute;
}
video {
   
    width: 640px;
    height: 360px;
}
input {
   
    vertical-align: middle;
}

布局效果如下图:

在这里插入图片描述


定义接口,构造假数据

我们弹幕中的弹幕数据正常情况下应该是通过与后台数据交互请求回来,所以我们需要先定义数据接口,并构造假数据来实现前端逻辑。

数据字段定义:

  • value:表示弹幕的内容(必填)
  • time:表示弹幕出现的时间(必填)
  • speed:表示弹幕移动的速度(选填)
  • color:表示弹幕文字的颜色(选填)
  • fontSize:表示弹幕的字体大小(选填)
  • opacity:表示弹幕文字的透明度(选填)

上面的 valuetime 是必填参数,其他的选填参数可以在前端设置默认值。

前端定义的假数据如下:

// 文件:index.js
let data = [
    {
   
        value: "这是第一条弹幕",
        speed: 2,
        time: 0,
        color: "red",
        fontSize: 20
    },
    {
   
        value: "这是第二条弹幕",
        time: 1
    }
];

实现前端弹幕的逻辑

我们希望是把弹幕封装成一个功能,只要有需要的地方就可以使用,从而实现复用,那么不同的地方使用这个功能通常的方式是 new 一个实例,传入当前使用该功能对应的参数,我们也使用这种方式来实现,所以我们需要封装一个统一的构造函数或者类,参数为当前的 canvas 元素、video 元素和一个 options 对象,options 里面的 data 属性为我们的弹幕数据,之所以不直接传入 data 是为了后续参数的扩展,严格遵循开放封闭原则,这里我们就统一使用 ES6 的 class 类来实现。

1、创建弹幕功能的类及基本参数处理

布局时需要注意 Canvas 的默认宽为 300px,高为 150px,我们要保证 Canvas 完全覆盖整个视频,需要让 Canvas 与 video 宽高相等。
因为我们不确定每一个使用该功能的视频的宽高都是一样的,所以 Canvas 画布的宽高并没有通过 CSS 来设置,而是通过 JS 在类创建实例初始化属性的时候动态设置。

// 文件:index.js
class CanvasBarrage {
   
    constructor(canvas, video, options = {
   }) {
   
        // 如果没有传入 canvas 或者 video 直接跳出
        if (!canvas || !video) return;
        this.canvas = canvas; // 当前的 canvas 元素
        this.video = video; // 当前的 video 元素

        // 设置 canvas 与 video 等高
        this.canvas.width = video.clientWidth;
        this.canvas.height = video.clientHeight;

        // 默认暂停播放,表示不渲染弹幕
        this.isPaused = true;

        // 没传参数的默认值
        let defaultOptions = {
   
            fontSize: 20,
            color: "gold",
            speed: 2,
            opacity: 0.3,
            data: []
        };

        // 对象的合并,将默认参数对象的属性和传入对象的属性统一放到当前实例上
        Object.assign(this, defaultOptions, options);
    }
}

应该挂在实例上的属性除了有当前的 canvas 元素、video 元素、弹幕数据的默认选项以及弹幕数据之外,还应该有一个代表当前是否渲染弹幕的参数,因为视频暂停的时候,弹幕也是暂停的,所以没有重新渲染,因为是否暂停与弹幕是否渲染的状态是一致的,所以我们这里就用 isPaused 参数来代表当前是否暂停或重新渲染弹幕,值类型为布尔值。

2、创建构造每一条弹幕的类

我们知道,后台返回给我们的弹幕数据是一个数组,这个数组里的每一个弹幕都是一个对象,而对象上有着这条弹幕的信息,如果我们需要在每一个弹幕对象上再加一些新的信息或者在每一个弹幕对象的处理时用到了当前弹幕功能类 CanvasBarrage 实例的一些属性值,取值显然是不太方便的,这样为了后续方便扩展,遵循开放封闭原则,我们把每一个弹幕的对象转变成同一个类的实例,所以我们创建一个名为 Barrage 的类,让我们每一条弹幕的对象进入这个类里面走一遭,挂上一些扩展的属性。

// 文件:index.js
class Barrage {
   
    constructor(item, ctx) {
   
        this.value = item.value; // 弹幕的内容
        this.time = item.time; // 弹幕出现的时间
        this.item = item; // 每一个弹幕的数据对象
        this.ctx = ctx; // 弹幕功能类的执行上下文
    }
}

在我们的 CanvasBarrage 类上有一个存储弹幕数据的数组 data,此时我们需要给 CanvasBarrage 增加一个属性用来存放 “加工” 后的每条弹幕对应的实例。

// 文件:index.js
class CanvasBarrage {
   
    constructor(canvas, video, options = {
   }) {
   
        // 如果没有传入 canvas 或者 video 直接跳出
        if (!canvas || !video) return;
        this.canvas = canvas; // 当前的 canvas 元素
        this.video = video; // 当前的 video 元素

        // 设置 canvas 与 video 等高
        this.canvas.width = video.clientWidth;
        this.canvas.height = video.clientHeight;

        // 默认暂停播放,表示不渲染弹幕
        this.isPaused = true;

        // 没传参数的默认值
        let defaultOptions = {
   
            fontSize: 20,
            color: "gold",
            speed: 2,
            opacity: 0.3,
            data: []
        };

        // 对象的合并,将默认参数对象的属性和传入对象的属性统一放到当前实例上
        Object.assign(this, defaultOptions, options);

        // ********** 以下为新增代码 **********
        // 存放所有弹幕实例,Barrage 是创造每一条弹幕的实例的类
        this.barrages = this.data.map(item => new Barrage(item, this));
        // ********** 以上为新增代码 **********
    }
}

其实通过上面操作以后,我们相当于把 data 里面的每一条弹幕对象转换成了一个 Barrage 类的一个实例,把当前的上下文 this 传入后可以随时在每一个弹幕实例上获取 CanvasBarrage 类实例的属性,也方便我们后续扩展方法,遵循这种开放封闭原则的方式开发,意义是不言而喻的。

3、在 CanvasBarrage 类实现渲染所有弹幕的 render 方法

CanvasBarragerender 方法是在创建弹幕功能实例的时候应该渲染 Canvas 所以应该在 CanvasBarrage 中调用,在 render 内部,每一次渲染之前都应该先将 Canvas 画布清空,所以需要给当前的 CanvasBarrage 类新增一个属性用于存储 Canvas 画布的内容。

// 文件:index.js
class CanvasBarrage {
   
    constructor(canvas, video, options = {
   }) {
   
        // 如果没有传入 canvas 或者 video 直接跳出
        if (!canvas || !video) return;
        this.canvas = canvas; // 当前的 canvas 元素
        this.video = video; // 当前的 video 元素

        // 设置 canvas 与 video 等高
        this.canvas.width = video.clientWidth;
        this.canvas.height = video.clientHeight;

        // 默认暂停播放,表示不渲染弹幕
        this.isPaused = 
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值