zoukankan      html  css  js  c++  java
  • 弹幕,是怎样练成的?

    • 说起弹幕看过视频的都不会陌生,那满屏充满着飘逸评论的效果,让人如痴如醉,无法自拔

    • 最近也是因为在学习关于 canvas 的知识,所以今天就想和大家分享一个关于弹幕的故事

    • 那么究竟弹幕是怎样炼成的呢? 我们且往下看(look)

    看什么?看效果

    效果图已经呈现给各位了,那么是不是有点小激动呢?是的,感慨万分,思绪宁乱,无语凝噎

    无论以后我们的工作中是否会遇到这样的需求,也请给自己一个增加技能的机会吧!!

    本次弹幕的效果,项目结构如下图所示

    项目整体已经给出,那么我们就撸起袖子加油干吧。

     让弹幕飞 

    上面我们提到了 canvas 的事情,所以呢,这就是制作弹幕了。我们利用 canvas 绘图来实现弹幕的功能

    首先,我们先给出html的结构

    // index.html文件

    <div class="wrap">

    <h1>听妈妈的话 - 周杰伦</h1>

    <div class="main">

           <canvas id="canvas"></canvas>

           <video src="../source/mv.mp4" id="video" controls width="720" height="480"></video>

        </div>

        <div class="content">

           <input type="text" id="text">

            <input type="button" value="发弹幕" id="btn">

           <input type="color" id="color">

           <input type="range" id="range" max="40" min="20">

       </div></div>

         // 引入index.js文件用来实现弹幕功能

        <script src="./index.js"></script>

    结构相对来说没什么高级的内容,主要就是写上了 canvas 标签还有 video 标签,他们才是视频网站弹幕的绝佳拍档

    那么不再卖关子了,赶紧进行主要活动吧

    模拟数据

    // index.js文件

    let data = [

        {value: '周杰伦的听妈妈的话,让我反复循环再循环', time: 5, color: 'red', speed: 1, fontSize: 22},

        {value: '想快快长大,才能保护她', time: 10, color: '#00a1f5', speed: 1, fontSize: 30},

        {value: '听妈妈的话吧,晚点再恋爱吧!爱呢?', time: 15},

    ];

    数据里代表了什么:

    • value:代表弹幕的内容 (必填)

    • time:代表弹幕展现的时间 (必填)

    • color:代表弹幕文字的颜色

    • speed:代表弹幕飘过的速度

    • fontSize:代表弹幕文字的大小

    • opacity:代表弹幕文字的透明度

    除了弹幕的内容和展现的时间外,其他都是可选的,模拟的数据里没有这些参数也没关系的

    获取 dom 元素

    // index.js文件 

    // 模拟数据

    ...省略

    // 获取到所有需要的dom元素

    let doc = document;

    let canvas = doc.getElementById('canvas');

    let video = doc.getElementById('video');

    let $txt = doc.getElementById('text');

    let $btn = doc.getElementById('btn');

    let $color = doc.getElementById('color');

    let $range = doc.getElementById('range');

    Canvas渲染弹幕

    下面我们将用面向对象的方式来实现canvas绘制弹幕的功能,之所以选择用这种方式主要是方便复用和后续添加方法;

    下面我们先来创建一个CanvasBarrage类,主要用做canvas来渲染整个弹幕;

    在实现之前,我们先来调用一下,看看是如何创建实例的。

    // index.js文件

    // 模拟数据

    ...省略

    // 获取到所有需要的dom元素

    ...省略

    // 创建CanvasBarrage类

    class CanvasBarrage {

        // todo

    }

    // 创建CanvasBarrage实例

    let canvasBarrage = new CanvasBarrage(canvas, video, { data });

    实现 CanvasBarrage

    我们在“得到所有的弹幕消息”那里,通过数组的 map 方法返回的还是个数组,不过返回的内容是一个 Barrage类,这是为什么呢?

    还记得之前说过么,用类的好处就是方便扩展,后续再添加方法的话可以直接在该类中添加即可。

    所以我们也不推崇直接map方法里直接返回一个{}这种形式

    // 不推荐 

    this.barrages = this.data.map(item => { item });

    说到这里我们还要先写一下Barrage这个类,不然接下来的console.log(this)会因为找不到Barrage类而报错

    // index.js文件

    ++++++++++++++++++++++

    // 创建Barrage类,用来实例化每一个弹幕元素

    class Barrage {

       constructor(obj, ctx) {        // todo    }

    }

     ++++++++++++++++++++++

    class CanvasBarrage {    ...省略}

    Now,通过上面代码中的console.log(this),我们可以看到,所有挂载到this实例上的属性和原型上的方法都呈现眼前了

    render 一下

    接着上面的 CanvasBarrage 类里 render 方法继续写,我们来把 todo 完成

    todo都做了什么?

    1、清除之前画布所有的绘制,防止绘制重叠的影响

    • this.clear()

    2、渲染真正的弹幕数据 (还未实现)

    • this.renderBarrage()

    3、判断是否继续渲染弹幕

    • this.isPaused为false时表示为播放状态

    4、递归调用render

    • 通过requestAnimationFrame来递归调用render

    • 要比setInterval这样的方式好很多

    渲染整个弹幕 render 方法就完成了,那么要继续写了,应该是刚才未实现的 renderBarrage 方法了

    But,在此之前,我们要先写个别的,它就是Barrage类

    因为还需要它来大显身手一下呢,每一个弹幕的实例都由它来制造

    创建 Barrage 类

    弹幕制造者来了,下面我们就来实现一下这个Barrage类,看它都具备哪些属性和方法,继续todo吧

    todo都做了什么?

    1、从传入的obj中取到必要的value和time

    this.value = obj.value; // 内容 

    this.time = obj.time;   // 时间

    2、初始化弹幕

    • 对每个弹幕所需的参数进行设置,如果obj上没有,就取默认参数

    • 计算每个弹幕的宽度

      • 由于不能直接操纵canvas画布里的元素,所以先创建一个p标签

      • p标签的宽度即为弹幕的宽 -> this.width = p.clientWidt

    • 设置每个弹幕的x和y坐标 (起始位置)

      • 横向x坐标起始位置都是从右边进入,即:画布的宽度

        • this.x = this.context.canvas.width

      • 纵向y坐标起始位置是不固定的,选在画布之内的任意位置出现

        • this.y = this.context.canvas.height * Math.random()

    • 处理弹幕超出画布区域

      • canvas是按照字号基线来展示字体的,如果小于这个字号大小

        • this.y = this.fontSize

      • 如果大于了画布高度 - 字号大小

        • this.y = this.context.canvas.height - this.fontSize

    3、渲染每个弹幕

    • 绘制文本需要设置文本的字体字号、颜色和文本的内容与坐标

    • 字体字号api

      • this.context.ctx.font = ${this.value}px Arial

    • 颜色api

      • this.context.ctx.fillStyle = this.color

    • 内容与坐标api

      • this.context.ctx.fillText(this.value, this.x, this.y)

    以上三步就是整个Barrage类所做的事情了。Barrage这个类都已经敲完了,那么接下来开始真正的渲染步骤吧

    renderBarrage才是主角

    此时我们再添加一个触发弹幕的事件,让弹幕飞起来

    大家一起写到了这里,也是时候展示一下成果了,往下看

    别急,让弹幕再飞一会儿

    渲染弹幕的功能,我们已经完成了,接下来让我们马不停蹄的写下如何发弹幕吧。别犹豫,开撸!!!

    发弹幕

    发弹幕相对来说还是很简单的,获取到 value, time, color, fontSize 之后把他们当作对象传给 CanvasBarrage 的 add方法进行添加就好了

    下面我们再写一下add方法,回到CanvasBarrage类里继续写

    // index.js文件

    class CanvasBarrage {

        constructor() { ...省略}

        render() { ...省略 }

        renderBarrage() { ...省略 }

        clear() { ...省略 }

        +++++++++++++++++++++++++++

        add(obj) {

            // 实际上就是往barrages数组里再添加一项Barrage的实例而已

            this.barrages.push(new Barrage(obj, this));

        }

        +++++++++++++++++++++++++++

    }

    完成,漂亮,看看效果吧

    写到这里我们已经完成了视频网站上的弹幕功能了,可喜可贺

    下面我们再来完善一下视频播放时对弹幕的播放处理吧

    暂停和拖动

    • 暂停就停止渲染弹幕

    // index.js文件

    ...省略

    // 播放

    video.addEventListener('play', () => {

        canvasBarrage.isPaused = false;

        canvasBarrage.render();

    });

    +++++++++++++++++++++++++++++++++++++++

    // 暂停

    video.addEventListener('pause', () => {

        // isPaused设为true表示暂停播放

        canvasBarrage.isPaused = true;

    });

    +++++++++++++++++++++++++++++++++++++++

    • 回放时需要重新渲染该时刻的弹幕

    // index.js文件

    // 暂停

    video.addEventListener('pause', () => {

        canvasBarrage.isPaused = true;

    });

    +++++++++++++++++++++++++++++++++++++++

    // 拖动进度条时触发seeked事件

    video.addEventListener('seeked', () => {

        // 调用CanvasBarrage类的replay方法进行回放,重新渲染弹幕

        canvasBarrage.replay();

    });

    +++++++++++++++++++++++++++++++++++++++

    • 让我们再次回到CanvasBarrage这个类上

    OK,写到这里,所有关于弹幕功能的代码就全部结束了!!

    如果工作中让你开发弹幕功能,你也可以在多敲几遍以上代码之后,得心应手的保证完成任务了。

    敬请期待,下节更精彩

    接下来我们再利用 WebSocket 和 redis 来进行一下较为实战的功能吧。

    作者:chenhongdong

    来源:

    https://juejin.im/post/5be54a286fb9a049ae07641b#comment

  • 相关阅读:
    分析java程序的命令总结jps,jstack
    Tomcat配置详解及tomcat的连接数与线程池
    Go入门练习题
    WebSocket的原理,及如何测试websocket是否连接成功
    day3-每天进步一点基础知识-正则练习题
    day2-每天进步一点基础知识
    day1-每天进步一点
    数组有没有length()这个方法? String有没有length()这个方法?
    List, Set, Map是否继承自Collection接口?
    启动一个线程是用run()还是start()?
  • 原文地址:https://www.cnblogs.com/Highdoudou/p/10113954.html
Copyright © 2011-2022 走看看