JS 的平凡之路--学习人气眼中的效果(中)

本文介绍如何在Canvas中实现一种简单的无重叠弹幕效果,包括绘制圆角矩形、获取文字宽度、划分车道及弹幕状态管理等内容。

这一节简单的模仿一下人气眼中的无重叠弹幕效果,也不卖关子了,下一节模仿头部的标签切换效果

一、简介

  一谈到弹幕相信大家多不陌生,平时看直播,那弹幕可是看的很欢啊。

  人气眼中的弹幕可能数量比较少的原因,是一种不重叠的弹幕。先看一下实现的效果:

无重叠弹幕效果

  当你决定看下文之前,你需要注意几点:

  • 最好这时你已经有了一定的canvas基础;
  • 如果你压根听过canvas,那真是太好了,哇!你应该要入坑了。

二、绘制圆角矩形

  在canvas的API中并没有圆角矩形的绘制方法,所以我们得自己写一个绘制圆角矩形的方法,一说到圆角矩形,对于我们前端可以说是小菜一碟了,border-radius早就用烂了:

矩形的圆角

  由上面这幅图,想必绘制圆角矩形的思路已经有了。这里我们可以通过canvas中的弧线和直线组合成圆角矩形(这里我就不过多的设置各个角的半径了):

// =====================
// canvas 圆角矩形的绘制
// =====================
// @param { Number } x 左上角的x坐标
// @param { Number } y 左上角的y坐标
// @param { Number } w 宽度
// @param { Number } h 高度
// @param { Number } r 圆角半径
// @param { String } bg 背景颜色
Barrage.prototype.drawRoundRect = function(options) {
    let {x,y,w,h,r,bg = "rgb(246,185,206)"} = options;
    const context = this.context;
    context.beginPath();
    context.fillStyle = bg;
    //左上角
    context.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2);
    context.lineTo(x + w - r, y);
    //右上角
    context.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0);
    context.lineTo(x + w, y + h - r);
    //右下角
    context.arc(x + w - r, y + h - r, r, 0, Math.PI / 2);
    context.lineTo(x + r, y + h);
    //左下角
    context.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI);
    context.lineTo(x, y + r);

    context.fill();
}复制代码

  这里需要注意的是beginPath方法,这通常是新手的一个坑:

  • 在Canvas中只有一条路径存在,称之为当前路径;
  • 在当前路径中我们可以添加很多子路径;
  • 而beginPath方法就是清除当前路径中的所有子路径;
  • 如果不适当使用beginPath方法,则会导致你会重复绘制很多路径。

  这里你可以实验一下下面的例子:

    context.strokeStyle = "#fff";
    context.rect(10,10,100,100);
    context.stroke();

    context.beginPath(); //去掉这段代码 会导致重复绘制上面生成的四个子路径
    context.strokeStyle = "#000";
    context.rect(200, 200, 100, 100);
    context.stroke();复制代码

三、获取文字宽度

  很巧的是,在Canvas中提供计算文字宽度的方法,需要注意的是:

  • 计算文字的宽度需要考虑到文字样式的影响。
// ===================
// 计算文字宽度
// ===================
Barrage.prototype.getTextWidth = function(font, text) {
    const context = this.context;
    context.font = font + "px Arial";
    const textWidth = context.measureText(text).width;
    return textWidth;
}复制代码

  很遗憾的是,这里并没有获取高度的属性。。。。

四、绘制每一条标签

  上面我们已经知道了绘制圆角矩形和测量文字的宽度了,这里我们只需要绘制文字即可,而你需要注意的是合理的利用textAlign和textBaseline将文字调整到合适的位置,这里我将文字居中于圆角矩形内部:

    context.textBaseline = "middle";
    context.textAlign = "center";
    context.fillText(text, x + width / 2, y + h / 2);复制代码

五、无重叠弹幕

  对于这一条,我们一步步来:

1、初始化数据的基本信息

  初始化每个数据的字体、位置、速度等基本信息,接下来绘制多需要这些参数,这些数据存放在data数组中。

2、划分车道

  无重叠弹幕和道路上行驶的车辆类似,我们需要划分车道,让它们有序的运作在各自的车道中:

    const pathHeight = 10 + vp * 2 + size,
          pathNumber = Math.floor(h / pathHeight);复制代码
3、弹幕的状态区分

  弹幕我们可以区分为待显示的弹幕、即将显示的弹幕、未完全显示的弹幕、完全显示的弹幕和显示完成的弹幕。这里我们还需要声明activeArray保存未完全显示的弹幕和完全实现的弹幕,nextBarrage保存即将显示的弹幕。

4、待显示弹幕

  我们的弹幕是从左边发射的,所以当一个弹幕完全显示出来,这个“车道”应该就要添加新的弹幕了:

    // 部分代码
    if (item.x + item.w < width && item.status === 0) {
        next.push(item.y);
        item.status = 1;
        break;
    }复制代码
5、添加待显示弹幕

  动画执行的每一帧我们需要将待显示弹幕加入到activeArray数组当中:

    if (next.length > 0 && data.length > 0) {
        const temp = data.shift();
        temp.y = next.shift();
        temp.x = width + 200;
        activeArray.push(temp);
    }复制代码

  这里的200并不是一个随意的数,因为我们在设置速度时,是设置1~1.25的随机数,所以你建立一个方程也就解出两个弹幕之间的最小安全距离了。

6、动画的暂停和恢复

  暂停和恢复的根本就是要保存暂停的绘制状态,这里可以通过Canvas的getImageData和putImageData实现:

// ==============
// 动画暂停与恢复
// ==============
Barrage.prototype.pause = function () {
    const context = this.context;
    if (!this.animationLock) {
        this.drawSurface = context.getImageData(0,0,this.width, this.height);
        this.animationLock = true;
    } else {
        context.putImageData(this.drawSurface, 0, 0);
        this.animationLock = false;
        this.animate();
    }
}复制代码

  到这里,简单的弹幕功能就完成了。

参考书籍: 《HTML5 Canvas核心技术》
源代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值