前端算法之彈幕設計

你們都說前端寫頁面較多,幾乎用不到算法。本文願從彈幕設計這個場景來描述算法在前端中的應用,咱們先來看下實現效果:前端

圖1.1 彈幕效果git

開場以前咱們先來描述彈幕開發的難度,再集中精力描述算法設計的思路。github

  • 如何保證不一樣字號的彈幕不碰撞
  • 彈幕的位置計算
  • 彈幕的速度控制及動畫實現
  • 彈幕與視頻的同步

如何保證不一樣字號的彈幕不碰撞算法

若是彈幕採用相同的字號,碰撞的問題處理起來比較簡單,只要考慮相鄰彈幕的播放速度和偏移的位置就能夠計算出來。然而使用不一樣字號的彈幕處理起來就麻煩了許多,彈幕的起始位置不能夠線性的增長,好比第一行放了字幕,接下來的字幕能夠按順序從上至下依次放置便可。緩存

彈幕的位置計算動畫

只有設計好彈幕的初始位置,才能夠動態、高效的管理不一樣字號彈幕的碰撞問題。打個比方,咱們經過接口獲取了2秒以內的彈幕數據1000條,每一個字幕的長度、速度、字號都不一樣,怎麼管理這些彈幕,示意圖以下:spa

圖2.1 彈幕管理示意圖設計

這是第一種狀況,按照從上到啊的順序依次擺放之後會有幾個問題:code

  1. 彈幕5、6、七該怎麼計算位置,按top值循環取模+累加嗎?
  2. 當彈幕一或者彈幕三足夠長的時候,如何準時的跳過當前位置計算?
  3. 當前屏幕的彈幕播放結束,如何再計算的時候利用空出來的位置
  4. 空出的位置是否知足當前彈幕的高度
  5. ……

一系列問題就不通通列舉出來了,基於這個背景咱們結合數學建模的思惟方式,找到了彈幕場景類似度很是高的機場運營。咱們能夠把彈幕當作飛機,每一個時間段播放多少彈幕和機場每一個時間段放飛多少飛機一個道理。cdn

首都國際機場一共有3條跑道,兩條4E級跑道、一條4F級跑道,2016年的吞吐量爲9000萬人次。它的運行機制就是全部飛機經過搭臺有順序的共用3條跑道來完成運輸任務的。

同理,咱們也設計了幾個個角色:一個是軌道(跑道)、一個是調度(塔臺)、一個是彈幕(飛機),咱們爲每一個角色設計一個類分爲爲Track、Main、Bullet。

  • 軌道

    軌道這個角色很重要,它能夠解決彈幕位置計算、速度控制、碰撞檢測問題。

    首先,咱們要來初始化軌道。通俗的說咱們要修建幾個跑道呢,咱們不是實物,能夠動態調整軌道的 數量,計算的原則:

    軌道數量 = 播放器有效高度 / 設備基準字號
          * 播放器有效高度:播放器的實際高度減去控制條的高度,由於彈幕不能夠遮擋控制條。
          * 設備基準字號:移動端是10px,pc端是12px;
    複製代碼

    爲啥計算公式是這樣的?由於咱們要支持不一樣字號的彈幕。試想不一樣的字號對物理空間的佔用是不一樣的,然而若是要求軌道的尺寸是動態的,那就帶來很複雜的計算。本文提出「虛擬軌道」的概念,在交通管制中最多見的就是道路合併或者改向。咱們也是採用將相鄰的物理軌道臨時合併爲一條軌道。這樣就能夠輕鬆的解決不一樣字號的軌道佔用問題。原理圖以下:

圖2.2 軌道計算示意圖

其次咱們來回憶下機場的工做流程:

  1. 機長呼叫塔臺,CZ6132請求起飛
    • 目前跑道均被佔用,請等待
      • N時刻後再次執行步驟1
    • 目前跑道 A1 空閒,准許進入
      • 執行步驟3
  2. 塔臺查看跑道使用狀況
  3. 進入跑道,起飛完成
  4. 機長通知塔臺,本次起飛完成,釋放跑道的佔用
  5. 其餘飛機一樣執行上述步驟

按照這個思路,咱們的彈幕工做流程:

  1. 彈幕進入播放器
  2. 軌道根據彈幕的播放速度、尺寸計算是否有合適的軌道提供
    • 沒有
      • 通知彈幕尚無合適軌道提供,請等待;同時,彈幕隊列中的其餘彈幕依次執行步驟1
      • 執行步驟3
  3. 播放器加載彈幕DOM,開始播放,待播放完成
  4. 播放完成通知軌道更新軌道佔用狀況
  5. 其餘彈幕一樣執行上述步驟

圖2.3 軌道可用性計算示意圖

關於軌道的基本原理咱們整理清楚了,固然還有很多細節好比如何和調度通訊、如何和彈幕通訊以及虛擬軌道檢測算法等。有興趣的同窗能夠參考代碼吧。https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/control/makeBullet.js

  • 彈幕 彈幕基本是實現「飛機」的角色,咱們要求它具備自身的屬性和方法。好比調度中心經過id能拿到它全部的基本信息,軌道控制也能夠經過彈幕進行檢查和更新。固然彈幕也必須具有狀態自動更新、移動、播放結束通知、自動銷燬等功能。
  • 調度 調度就是搭臺的化身,承接着軌道、彈幕的控制,也保持着與播放器的步調一致。它的職責以下:
    1. 播放器交互控制
    2. 彈幕隊列控制
    3. 自身狀態更新
    4. 數據格式轉換
    5. 動畫執行 仍是直接用流程圖來描述更直接些:

在彈幕啓動以後,首先要檢查本地是否已有緩存數據,沒有的話直接請求數據並緩存,而後執行數據讀取,首次過濾數據進入彈幕隊列,同時啓動定時器。彈幕隊列的數據會按期請求軌道,檢測隊列裏的彈幕是否能夠進入,一旦確認後軌道作好登記,彈幕就能夠進入播放器開啓動畫播放了。定時器每隔2秒就會再次更新數據進入到彈幕隊列(這塊不一樣的業務能夠定製不一樣的規則)。彈幕播放結束後會通知調度和軌道,調度會在彈幕隊列中移除該彈幕實例,軌道也會移除該彈幕實例的軌道佔用。

全部代碼實現來自帶解析器、能節省流量的西瓜播放器, Github

相關文章
相關標籤/搜索