版本記錄
| 版本號(hào) | 時(shí)間 |
|---|---|
| V1.0 | 2017.07.23 |
前言
在我們做直播等視頻類(lèi)app的時(shí)候,總是有顯示和發(fā)送彈幕的要求,彈幕可以方便用戶(hù)進(jìn)行溝通和互動(dòng),增加了娛樂(lè)性,增強(qiáng)app等客戶(hù)端的用戶(hù)粘度,下面我們就說(shuō)一下彈幕顯示方面的問(wèn)題和相關(guān)框架BarrageRenderer。先給出
github地址。
1. BarrageRenderer彈幕框架
框架基本介紹
??當(dāng)然,如果是技術(shù)大牛團(tuán)隊(duì)或者有特殊需求的團(tuán)隊(duì),可以自己寫(xiě)一個(gè)定制的彈幕顯示框架,但是如果對(duì)于一般的小公司來(lái)說(shuō),技術(shù)實(shí)力不是很雄厚,時(shí)間緊迫的話(huà),建議還是選擇彈幕框架,本篇就介紹一個(gè)框架BarrageRenderer,也是我們項(xiàng)目中用到的框架。
下面我們看一下框架的star數(shù)目和作者。

??可以看見(jiàn),star數(shù)目不是很多,但是也是可以的了,畢竟無(wú)法和AFNetworking這種非常流行的框架相比,每一個(gè)app都要有網(wǎng)絡(luò)申請(qǐng),但是不是每個(gè)app都會(huì)有彈幕的,這和行業(yè)限制也有一定的關(guān)系。
下面我們?cè)诳匆幌驴蚣艿淖髡摺?/p>

可見(jiàn),這里他比較牛逼的框架,也就BarrageRenderer這個(gè)了,但是很值得我們學(xué)習(xí)了。
下面我們看一下框架有那些代碼,如下圖。

可以看見(jiàn),代碼組成并不少。
框架深入
前面介紹了框架的基本情況,下面我們說(shuō)一下框架深入等方面。
1. 框架發(fā)起原因
??這里要引用作者自己的話(huà)了。彈幕實(shí)質(zhì)是多個(gè)精靈的時(shí)間上的渲染方式。 PC/Web上已經(jīng)有很成熟的解決方案了; Android上比較有名的是BiliBili開(kāi)源的DanmakuFlameMaster, 但是開(kāi)源社區(qū)尚沒(méi)有比較好的iOS彈幕渲染器.覺(jué)得在二次元文化逐漸滲透的今天,視頻彈幕已經(jīng)是很重要的一種情緒表達(dá)方式了。
2. 特點(diǎn)
- 提供過(guò)場(chǎng)彈幕(4種方向)與懸浮彈幕(2種方向)支持,支持圖片彈幕與文字彈幕。
- 提供圖文彈幕接口
attributedText,可按照demo中的指示生成圖文混排彈幕。 - 彈幕字體可定義: 顏色、邊框、圓角、背景、字體等皆可定制。
- 自動(dòng)軌道搜尋算法,新發(fā)彈幕會(huì)根據(jù)相同方向的同種彈幕獲取最佳運(yùn)動(dòng)軌道。
- 支持延時(shí)彈幕,為反復(fù)播放彈幕提供可能,支持與外界的時(shí)間同步。
- 獨(dú)立的動(dòng)畫(huà)時(shí)間系統(tǒng), 可以統(tǒng)一調(diào)整動(dòng)畫(huà)速度。
- 特制的動(dòng)畫(huà)引擎,播放彈幕更流暢,可承接持續(xù)的10條/s的彈幕流速。
- 豐富的擴(kuò)展接口,實(shí)現(xiàn)了父類(lèi)的接口就可以自定義彈幕動(dòng)畫(huà)。
- 概念較清晰,可以為任意UIView綁定彈幕,當(dāng)然彈幕內(nèi)容需要?jiǎng)?chuàng)建控件輸入。
- 因?yàn)樽髡哂浶员容^差,所以在很多緊要處添加了注釋?zhuān)斫獯a更容易。
3. 彈幕效果演示
下面我們就看一下彈幕效果。


4. 使用方式
- 下載版本庫(kù),進(jìn)入
BarrageRendererDemo目錄。 運(yùn)行pod update拉取相關(guān)庫(kù),即可以運(yùn)行BarrageRendererDemo.xcworkspace。 - 也可以在您工程的podfile中添加一條引用:
pod 'BarrageRenderer','1.9.1'并在工程目錄下的命令行中運(yùn)行 pod update,(CocoaPods 版本 0.39)。 - 或者嘗試使用 2.1.0 版本,此版本使用更方便,在部分特殊情況下的性能也有所提升。
或者將代碼下載下來(lái), 將BarrageRenderer/目錄添加到您的工程當(dāng)中 - 在需要使用彈幕渲染功能的地方
#import<BarrageRenderer/BarrageRenderer.h>
創(chuàng)建BarrageRenderer,添加BarrageRenderer.view,執(zhí)行start方法, 通過(guò)receive方法輸入彈幕描述符descriptor, 即可以顯示彈幕,詳見(jiàn)demo。 - demo的基本功能演示了:如何在view上增加一條彈幕,如何啟動(dòng)、停止、暫停、恢復(fù)彈幕播放,如何減速?gòu)椖坏倪\(yùn)動(dòng)速度。
- demo的高級(jí)功能演示了:如何使用自定義方式添加圖文混排彈幕,如何支持錄播中在固定時(shí)間點(diǎn)顯示固定彈幕的邏輯。
5. 基本使用
一般只需要在- (void)viewDidLoad里創(chuàng)建一個(gè) BarrageRenderer對(duì)象,并將其view add 到你想要添加彈幕動(dòng)畫(huà)的 view 上,配置就結(jié)束了。當(dāng)你想要添加一條彈幕到屏幕上的時(shí)候,你只需要?jiǎng)?chuàng)建一個(gè)彈幕描述符 BarrageDescriptor, 為其指定彈幕Sprite 的類(lèi)名,然后通過(guò) params 設(shè)置一些屬性, 調(diào)用 BarrageRenderer 的 receive 方法即可成功將彈幕顯示在屏幕上。彈幕支持的屬性可參照 BarrageSpriteProtocol.h文件 以及在 BarrageSprite 族的屬性。
6. 動(dòng)態(tài)移除彈幕
2.1.0 新增特性
在某些情況下,你可能需要從屏幕中動(dòng)態(tài)地移除彈幕。2.1.0版本為此提供了一個(gè)默認(rèn)的彈幕標(biāo)識(shí)符 params[@"identifier"]以及 一個(gè)移除彈幕的方法- (void)removeSpriteWithIdentifier:(NSString *)identifier;。 舉例而言,你可以在用戶(hù)點(diǎn)擊彈幕的時(shí)候,移除彈幕,代碼如下:
_weak BarrageRenderer *render = _renderer;
descriptor.params[@"clickAction"] = ^(NSDictionary *params){
[render removeSpriteWithIdentifier:params[@"identifier"]];
};
7. 更新彈幕視圖
2.1.0 新增特性
有時(shí)候,你想要為你的彈幕精靈 view 添加動(dòng)畫(huà)。當(dāng)然,你可以使用 animation 或者 NSTimer。由于 BarrageRenderer 整體由 CADisplayLink 驅(qū)動(dòng),你可以借用 BarrageRenderer的時(shí)鐘,來(lái)更新你的精靈 view 。這樣做的好處在于,當(dāng)你通過(guò) BarrageRenderer 暫停彈幕時(shí),你的 彈幕精靈 view 也將暫停。為此,你可以在自定義彈幕精靈 view 的時(shí)候,實(shí)現(xiàn)協(xié)議方法- (void)updateWithTime:(NSTimeInterval)time來(lái)依據(jù)時(shí)間更新你的 view 。連貫起來(lái)就成了動(dòng)畫(huà)。你可以參考 demo 中 AvatarBarrageView 類(lèi)的實(shí)現(xiàn)。
需要注意的是,- (void)updateWithTime:(NSTimeInterval)time中不要放置過(guò)多的計(jì)算邏輯。在大量彈幕下,這樣有可能造成動(dòng)畫(huà)的卡頓。
8. 設(shè)置靠邊位置
1.9.0 版本支持為過(guò)場(chǎng)彈幕與懸浮彈幕設(shè)置"靠邊"屬性。對(duì)于過(guò)場(chǎng)彈幕,可設(shè)置side(BarrageWalkSide)屬性;對(duì)于懸浮彈幕,可設(shè)置side(BarrageFloatSide)屬性。代碼表現(xiàn)為:
descriptor.params[@"side"] = @(BarrageWalkSideRight);
// 過(guò)場(chǎng)彈幕中,靠右側(cè)行駛
descriptor.params[@"side"] = @(BarrageFloatSideLeft);
// 懸浮彈幕中,靠屏幕左側(cè)堆疊
9. 設(shè)置隱入隱出
1.8.0 版本新增屬性,僅對(duì)懸浮彈幕有效,設(shè)置如下:
descriptor.params[@"fadeInTime"] = @(1); // 隱入時(shí)間
descriptor.params[@"fadeOutTime"] = @(1); // 隱出時(shí)間
10. 圖文混排彈幕
最簡(jiǎn)單的彈幕只是文本, 但有時(shí)候你可能需要添加emoji表情或者圖片上去。emoji表情是UTF字符集原生支持的,對(duì)待他和其他的文本字符沒(méi)有區(qū)別;對(duì)于圖片,你有兩種方式可以添加圖片彈幕, 一種是使用 attributedText 設(shè)置屬性文本,一種是自定義view。自定義 view 可以參考 BarrageWalkImageTextSprite。 需要注意的是,如果 - (UIView *)bindingView方法返回的是你自定義的 view,你需要覆蓋你自定義 view 的- (CGSize)sizeThatFits方法,返回正確的 view 大小。
在V2版本中,bindingView 方法被廢除,你需要通過(guò) descriptor.params[@"viewClassName"]指明 sprite所要關(guān)聯(lián)的 view 類(lèi)。
11. 直接在 Sprite 子類(lèi)中布局元素
你可能在方法 - (UIView *)bindingView 中創(chuàng)建了許多視圖元素,而并非返回一個(gè)自定義 view,因此,這時(shí)候你并不方便自定義 view 的 - (CGSize)sizeThatFits方法,為此你可以選擇覆蓋 BarrageSprite 的 size 屬性的- (CGSize)size 方法,在此方法中返回你的彈幕 view 的大小。當(dāng)然,在 - (UIView *)bindingView里你要設(shè)置各個(gè)子 view 的位置,以及處理一些可變大小元素比如 UILabel 的布局問(wèn)題。
在V2 版本中,bindingView 方法被廢除,相關(guān)的子 view 布局則寫(xiě)在sprite關(guān)聯(lián)的 view 類(lèi)中。
12. 外部設(shè)置彈幕元素的大小
你也可以在創(chuàng)建彈幕描述符的時(shí)候強(qiáng)制指定彈幕元素的大小。通過(guò)設(shè)置:
BarrageDescriptor.params[@"mandatorySize"]
設(shè)置此屬性之后,你自定義的彈幕 view 的- (CGSize)sizeThatFits 將不再起作用,但是覆蓋的 - (CGSize)size方法仍然是有效的,因?yàn)樗膬?yōu)先級(jí)比較高。
13. 如何調(diào)節(jié)軌道數(shù)量
繼承自 BarrageFloatSprite與BarrageWalkSprite的彈幕都有trackNumber 屬性,你可以用它來(lái)設(shè)置彈幕軌道數(shù)量。在宏 STRIP_NUM 中規(guī)定了最大的軌道數(shù)量。需要注意的是,BarrageRenderer 中的軌道概念比其他一些彈幕庫(kù)的軌道概念更復(fù)雜,用它可以比較精確地進(jìn)行沖突檢測(cè)。當(dāng)你的彈幕 view 擁有不同大小的時(shí)候,你會(huì)意識(shí)到他的威力。
如果你只是希望簡(jiǎn)單地調(diào)節(jié)一下軌道數(shù)量,你只需為 trackNumber屬性設(shè)置一個(gè)值即可。比如10,20... 并注意不要超過(guò) STRIP_NUM 的值。
14. 如何綁定視頻播放時(shí)間,即支持快進(jìn)快退
這其實(shí)是非直播類(lèi)視頻彈幕的剛需。由于涉及到彈幕存儲(chǔ),所以有些內(nèi)容并不是單獨(dú) BarrageRenderer可以解決的。BarrageRenderer 支持將彈幕綁定到視頻的時(shí)間點(diǎn)上。實(shí)現(xiàn)策略一般有如下幾步:
- 在視頻初始化的時(shí)候,批量添加彈幕
- 設(shè)置
BarrageRenderer的redisplay屬性為YES, 指定其delegate。 - 對(duì)于1條被添加的
BarrageDescriptor, 為其指定delay, delay 是此條彈幕對(duì)應(yīng)的視頻時(shí)間點(diǎn) - 實(shí)現(xiàn)
BarrageRendererDelegate協(xié)議方法, 在- (NSTimeInterval)timeForBarrageRenderer:(BarrageRenderer *)renderer;方法中返回當(dāng)前的視頻時(shí)間點(diǎn). 當(dāng)你的視頻播放、快進(jìn)或者快退時(shí),這個(gè)時(shí)間也會(huì)有變。
15. 如何控制彈幕顯示的區(qū)域
新版已經(jīng)支持配置彈幕的顯示區(qū)域。當(dāng)你把 BarrageRenderer.view 添加到你的業(yè)務(wù) view 上之后,默認(rèn)情況下,彈幕的 view 會(huì)適應(yīng)你的業(yè)務(wù) view,你可以通過(guò) BarrageRenderer 的canvasMargin 屬性來(lái)設(shè)置彈幕顯示區(qū)域相對(duì)于你業(yè)務(wù) view 的大小。如: _renderer.canvasMargin = UIEdgeInsetsMake(10, 10, 10, 10);
16. 修改彈幕的初始位置
原生的BarrageSprite子類(lèi)不支持自定義彈幕位置。如果需要,你需要自定義你自己的 BarrageSprite。你可以繼承 BarrageWalkTextSprite ,然后覆蓋 - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites根據(jù)屏幕上已有的同類(lèi)彈幕信息返回你的彈幕的初始位置。
17. 如何設(shè)置彈幕速率與文本長(zhǎng)度正相關(guān)
一些彈幕組件的速度會(huì)與文本長(zhǎng)度成正比,這在 BarrageRenderer 中實(shí)現(xiàn)起來(lái)也十分容易。在創(chuàng)建彈幕描述符BarrageDescriptor的時(shí)候,根據(jù)文本長(zhǎng)度設(shè)置 BarrageSprite 的速度值即可。
18. 為彈幕添加點(diǎn)擊操作
BarrageRenderer默認(rèn)關(guān)閉了交互行為的,但如果需要,你可以啟用,只需兩步:
- BarrageRenderer.view.userInteractionEnabled = YES;
- 為 descriptor.params[@"clickAction"] 添加參數(shù)
在2.0.1 版本,clickAction指定的 block 增加了參數(shù),以支持在點(diǎn)擊階段拿到彈幕的相關(guān)信息,比如彈幕消息的 id 。
19. 如何使事件透?jìng)鞯降讓?業(yè)務(wù))view
開(kāi)啟 BarrageRenderer.view.userInteractionEnabled 之后,所有的事件都會(huì)被 BarrageRenderer 攔截掉而到不了你的業(yè)務(wù) view,這時(shí)候你如果在你的業(yè)務(wù) View 上添加一個(gè) Button,而 BarrageRenerer.view 又在 Button 之上的話(huà),那么點(diǎn)擊這個(gè) Button 是無(wú)效的。你可以設(shè)置只攔截彈幕上的事件,而將 BarrageRenderer.view 上的事件透?jìng)鳌Mㄟ^(guò)設(shè)置屬性:
BarrageRenderer.masked = NO; // 默認(rèn)為YES
20. 如何對(duì)彈幕進(jìn)行限流
通過(guò) - (NSInteger)spritesNumberWithName:(NSString *)spriteName; 方法可以獲取屏幕上當(dāng)前的彈幕數(shù)量,你可以在調(diào)用BarrageRenderer 的receive方法之前,獲取屏幕上的彈幕數(shù)量,然后根據(jù)一定的規(guī)則決定要不要添加這條彈幕。
21. 為彈幕添加背景圖片
框架原生的 BarrageSprite族并不支持添加背景圖片。如果業(yè)務(wù)需要,可以通過(guò)繼承 BarrageSprite 的方式添加。
22. 提升動(dòng)畫(huà)性能
彈幕一般呈現(xiàn)在視頻之上,而視頻解碼會(huì)消耗大量的 CPU,當(dāng)可用 CPU 不足時(shí),彈幕動(dòng)畫(huà)會(huì)出現(xiàn)卡頓。為使彈幕流暢,你可以將 trackNumber 調(diào)低一些。另外可以對(duì)屏幕上的彈幕數(shù)量進(jìn)行限流。
實(shí)測(cè)中,如果多個(gè)彈幕的delay時(shí)間相同(或相距在1/60s之內(nèi)),可能使這些彈幕同時(shí)進(jìn)入屏幕,進(jìn)而導(dǎo)致瞬間卡頓。真實(shí)直播彈幕環(huán)境下,這種情況出現(xiàn)的比較少。針對(duì)性能較好的iPhone,可以設(shè)置BarrageRenderer 的平滑系數(shù) smoothness ,以?xún)?yōu)化此問(wèn)題。此參數(shù)從V2開(kāi)始支持。
23. V2 重構(gòu)
自 2.0版本起, 對(duì) sprite及 dispatcher進(jìn)行了較大幅度的調(diào)整。主要有如下幾點(diǎn):
- 分離 sprite 更新邏輯與彈幕視圖,方便兩者組合復(fù)用。
- 針對(duì)前版本
layout不方便使用的問(wèn)題做了優(yōu)化。 - 為視圖添加復(fù)用機(jī)制(不過(guò)實(shí)測(cè)中并沒(méi)有太大性能提升)。
- 增加平滑度參數(shù),優(yōu)化一些特殊情況下的性能。
如果你在使用V1系列時(shí),沒(méi)有創(chuàng)建自己的sprite 子類(lèi),那么你可以在不改動(dòng)業(yè)務(wù)代碼的時(shí)候,升級(jí)到V2版本; 否則,你需要改動(dòng)你的 sprite 子類(lèi),當(dāng)然,改動(dòng)不會(huì)太大。
雖然我對(duì)V2版本做了測(cè)試,但是無(wú)法涵蓋所有情況。 如果你的應(yīng)用難以承擔(dān)較高風(fēng)險(xiǎn),那么你也可以保持使用 V1 系列,等到 V2 版本相對(duì)穩(wěn)定時(shí)再行遷移,V1 不會(huì)再添加新的 feature,但對(duì)于顯著的 bug 我還是會(huì)提供修復(fù),如果你剛剛接入 V2, 那么建議你嘗試使用 V2。
V2 在創(chuàng)建自定義彈幕的時(shí)候,涉及到兩部分:
- 繼承對(duì)應(yīng)的
BarrageRenderer子類(lèi),你也可以直接使用默認(rèn)的BarrageWalkSprite或BarrageFloatSprite,涉及到修改對(duì)應(yīng)的 view 時(shí),在創(chuàng)建 descriptor 的時(shí)候增加一條如下的代碼。
descriptor.params[@"viewClassName"] = @"UILabel";
- 將原來(lái)寫(xiě)在
sprite子類(lèi)bindingView中的布局代碼遷出到獨(dú)立的view中,為此類(lèi)實(shí)現(xiàn)BarrageViewProtocol協(xié)議中的方法;一般可以為 view 類(lèi)添加相應(yīng)的擴(kuò)展。比如UILabel+BarrageView.h。如此,你的 sprite 不必再關(guān)心布局的細(xì)節(jié),只需要處理好時(shí)間邏輯。
24. load 方法的語(yǔ)義變化
在v2.1.0 及之后的版本,load語(yǔ)義有所調(diào)整。之前,load 方法所觸發(fā)的 receive 調(diào)用, 會(huì)調(diào)整 descriptor 的 delay 參數(shù)。而之后的版本,不再整 descriptor 的 delay 參數(shù)。所以對(duì)于播放彈幕 前/過(guò)程中 從網(wǎng)絡(luò)加載的批量彈幕(delay屬性是具體不變的),推薦使用 load 方法。
后記
未完,待續(xù)~~~
