fly-barrage 前端弹幕库(5):高级弹幕的设计与实现

项目官网地址:fly-barrage.netlify.app/

👑🐋🎉如果感觉项目还不错的话,还请点下 star 🌟🌟🌟。

Gitee:gitee.com/fei_fei27/f...(Gitee 官方推荐项目);

Github:github.com/feiafei27/f...
其他系列文章:

fly-barrage 前端弹幕库(1):项目介绍

fly-barrage 前端弹幕库(2):弹幕内容支持混入渲染图片的设计与实现

fly-barrage 前端弹幕库(3):滚动弹幕的设计与实现

fly-barrage 前端弹幕库(4):顶部、底部弹幕的设计与实现

高级弹幕的计算是最简单的,因为每个高级弹幕和其他弹幕是无交互的,不需要处理相互重叠的问题,只需要根据当前视频的播放时间计算出高级弹幕处于的位置即可。

1:相关类型

typescript 复制代码
/**
 * 用于描述高级弹幕
 */
export default class SeniorBarrage extends BaseBarrage {
  constructor(seniorBarrageOptions: SeniorBarrageOptions, barrageRenderer: BarrageRenderer) {}
}

export type SeniorBarrageOptions = BaseBarrageOptions & {
  // 弹幕的类型
  barrageType: 'senior';
  // 高级弹幕配置
  seniorBarrageConfig: SeniorBarrageConfig;
}

/**
 * 用于描述二位平面中的一点(单位 px)
 */
export type Location = {
  x: number;
  y: number;
}

/**
 * 用于描述二位平面中的一点(面向用户)
 */
export type LocationDefine = Location & {
  // 定义的类型:直接像素 或 canvas 百分比
  type?: 'PIXEL' | 'PERCENT';
  // 在 x、y 定义的基础上进行坐标偏移
  offsetX?: number;
  offsetY?: number;
}

/**
 * 用于描述高级弹幕的运动配置
 */
export type SeniorBarrageConfig = {
  // 起始点
  startLocation: LocationDefine;
  // 结束点
  endLocation: LocationDefine;
  // 生存时间(单位为毫秒)(数据要求:> 0)
  totalDuration: number;
  // 延迟时间(单位为毫秒)(数据要求:>= 0)
  delay: number;
  // 运动时长(单位为毫秒)(数据要求:>= 0)
  motionDuration: number;
}

高级弹幕的运动分为三部分:

  • 运动前(此时位置一定是在 startLocation);
  • 运动时(根据运动时间、运动速度以及 startLocation 可以计算出来);
  • 运动完成(此时的位置 endLocation);

2:高级弹幕关键数据的计算

typescript 复制代码
export default class SeniorBarrage extends BaseBarrage {
  constructor(seniorBarrageOptions: SeniorBarrageOptions, barrageRenderer: BarrageRenderer) {
    this.calcActualLocation();
  }

  /**
   * 计算关键点的实际坐标
   */
  calcActualLocation() {
    const { startLocation, endLocation, motionDuration } = this.seniorBarrageConfig;

    // 计算实际起始点的位置
    // 计算 actualStartLocation
    let actualStartLocationX = (startLocation.type || 'PIXEL') === 'PIXEL' ? startLocation.x : startLocation.x * this.canvasSize.width;
    let actualStartLocationY = (startLocation.type || 'PIXEL') === 'PIXEL' ? startLocation.y : startLocation.y * this.canvasSize.height;
    if (startLocation.offsetX) actualStartLocationX += startLocation.offsetX;
    if (startLocation.offsetY) actualStartLocationY += startLocation.offsetY;
    this.actualStartLocation = {
      x: actualStartLocationX,
      y: actualStartLocationY
    };
    // 计算 actualEndLocation
    let actualEndLocationX = (endLocation.type || 'PIXEL') === 'PIXEL' ? endLocation.x : endLocation.x * this.canvasSize.width;
    let actualEndLocationY = (endLocation.type || 'PIXEL') === 'PIXEL' ? endLocation.y : endLocation.y * this.canvasSize.height;
    if (endLocation.offsetX) actualEndLocationX += endLocation.offsetX;
    if (endLocation.offsetY) actualEndLocationY += endLocation.offsetY;
    this.actualEndLocation = {
      x: actualEndLocationX,
      y: actualEndLocationY
    };

    // 根据实际起始点的位置,计算 vx 和 vy
    this.vx = (this.actualEndLocation.x - this.actualStartLocation.x) / motionDuration;
    this.vy = (this.actualEndLocation.y - this.actualStartLocation.y) / motionDuration;
  }
}

这里根据用户提供的配置计算出起始点以及结束点的实际坐标,并根据 运动路程 / 运动时间 计算出运动速度,这些关键数据是在接下来计算高级弹幕实时位置时需要的。

3:获取当前应该渲染的高级弹幕,并计算渲染位置

typescript 复制代码
/**
 * 获取当前应该渲染的高级弹幕
 * @param time 视频播放时间点
 */
getRenderSeniorBarrages(time: number): SeniorBarrage[] {
  // 获取当前能够渲染的高级弹幕
  const renderSeniorBarrages = this.seniorBarrageInstances.filter(barrage =>
    // 当前时间大于等于弹幕的出现时间 并且 当前时间小于等于弹幕的结束时间
    time >= barrage.time &&
    time <= (barrage.time + barrage.seniorBarrageConfig.totalDuration)
  );

  // 遍历计算高级弹幕的 top 和 left
  renderSeniorBarrages.forEach(barrage => {
    const startPoint = barrage.time;
    const delayEndPoint = startPoint + barrage.seniorBarrageConfig.delay;
    const motionEndPoint = delayEndPoint + barrage.seniorBarrageConfig.motionDuration;

    if (time >= startPoint && time <= delayEndPoint) {
      // delay 时间段内(渲染在开始点即可)
      barrage.left = barrage.actualStartLocation.x;
      barrage.top = barrage.actualStartLocation.y;
    } else if (time >= delayEndPoint && time <= motionEndPoint) {
      // motion 时间段内
      // 当前的运动时长
      const motionTime = time - delayEndPoint;
      barrage.left = barrage.actualStartLocation.x + motionTime * barrage.vx;
      barrage.top = barrage.actualStartLocation.y + motionTime * barrage.vy;
    } else {
      // 运动结束时间段内(渲染在结束点即可)
      barrage.left = barrage.actualEndLocation.x;
      barrage.top = barrage.actualEndLocation.y;
    }
  });

  return renderSeniorBarrages;
}

首先根据弹幕的出现时间和 totalDuration 以及当前的 time 过滤出当前应该渲染的高级弹幕。

然后遍历应该渲染的弹幕,进行渲染位置的计算,在这里,先计算出高级弹幕所处于的运动阶段,如果是运动前的话,弹幕的渲染位置就是上文计算好的 actualStartLocation,然后是运动后的话,弹幕的渲染位置就是上文计算好的 actualEndLocation,如果是运动中的话,可以根据运动时间以及速度计算出运动的偏移量,再将偏移量加上起始点的位置即可计算出弹幕此时应该渲染的位置。

相关推荐
laocooon52385788611 分钟前
HTML CSS 超链
前端·css·html
LUwantAC14 分钟前
CSS(二):美化网页元素
前端·css
素**颜24 分钟前
uniapp 基于xgplayer(西瓜视频) + renderjs开发,实现APP视频播放
javascript·uni-app·音视频
m0_7482510825 分钟前
docker安装nginx,docker部署vue前端,以及docker部署java的jar部署
java·前端·docker
我是ed38 分钟前
# thingjs 基础案例整理
前端
Ashore_44 分钟前
从简单封装到数据响应:Vue如何引领开发新模式❓❗️
前端·vue.js
落魄实习生1 小时前
小米路由器开启SSH,配置阿里云ddns,开启外网访问SSH和WEB管理界面
前端·阿里云·ssh
bug丸1 小时前
v8引擎垃圾回收
前端·javascript·垃圾回收
安全小王子1 小时前
攻防世界web第三题file_include
前端
&活在当下&1 小时前
ref 和 reactive 的用法和区别
前端·javascript·vue.js