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,如果是运动中的话,可以根据运动时间以及速度计算出运动的偏移量,再将偏移量加上起始点的位置即可计算出弹幕此时应该渲染的位置。

相关推荐
用户40993225021214 小时前
Composable的命名规矩和参数约定,别再瞎写了
前端·javascript·后端
用户游民14 小时前
Flutter Provider原理以及用法
前端·flutter
Rust研习社14 小时前
告别环境混乱!使用 mise 管理你的开发环境
前端·后端·rust
小小荧14 小时前
Vue Native多分支迭代,Vue跨端原生生态迎来革新
前端·javascript·vue.js
EntyIU14 小时前
uv工程化项目指南
前端·python·uv
WebGirl14 小时前
如何在VS code中添加SKill
前端
梦醒沉醉14 小时前
1、JavaScript入门和语法类型
javascript
_洋14 小时前
Three.js 着色器相关方法总结
开发语言·javascript·着色器
marsh020614 小时前
49 openclaw故障排查:系统异常时的诊断方法
服务器·前端·青少年编程·ai·php·技术美术
Maimai1080814 小时前
前端如何落地 SSE:从实时评论到可复用的实时数据 Hook
前端·javascript·react.js·前端框架·web3·状态模式·webassembly