Code Monkey home page Code Monkey logo

barragerenderer's Introduction

BarrageRenderer

TravisCi

一个 iOS 上的开源弹幕渲染库.

发起原因

弹幕实质是多个精灵的时间上的渲染方式. PC/Web上已经有很成熟的解决方案了; Android上比较有名的是BiliBili开源的DanmakuFlameMaster, 但是开源社区尚没有比较好的iOS弹幕渲染器.觉得在二次元文化逐渐渗透的今天,视频弹幕已经是很重要的一种情绪表达方式了.没必要重复造轮子,所以我把自己写的一份弹幕渲染引擎开源了.还有一些需要后续完善的地方,但基本功能已经有了.祝大家玩得开心.

Features

  • 提供过场弹幕(4种方向)与悬浮弹幕(2种方向)支持; 支持图片弹幕与文字弹幕.
  • 提供图文弹幕接口attributedText, 可按照demo中的指示生成图文混排弹幕.
  • 弹幕字体可定义: 颜色,边框,圆角,背景,字体等皆可定制.
  • 自动轨道搜寻算法,新发弹幕会根据相同方向的同种弹幕获取最佳运动轨道.
  • 支持延时弹幕,为反复播放弹幕提供可能;支持与外界的时间同步.
  • 独立的动画时间系统, 可以统一调整动画速度.
  • 特制的动画引擎,播放弹幕更流畅,可承接持续的10条/s的弹幕流速.
  • 丰富的扩展接口, 实现了父类的接口就可以自定义弹幕动画.
  • 概念较清晰,可以为任意UIView绑定弹幕,当然弹幕内容需要创建控件输入.
  • 因为作者记性比较差,所以在很多紧要处添加了注释,理解代码更容易.
  • 效果动画如下图所示:

效果动画

效果动画

视频演示地址: http://v.youku.com/v_show/id_XMTI5NDM4ODk3Ng==.html

使用方式

  1. 下载版本库,进入BarrageRendererDemo目录. 运行pod update拉取相关库, 即可以运行BarrageRendererDemo.xcworkspace
  2. 也可以在您工程的podfile中添加一条引用: pod 'BarrageRenderer', '1.9.1' 并在工程目录下的命令行中运行 pod update, (CocoaPods 版本 0.39)
  3. 或者尝试使用 2.1.0 版本,此版本使用更方便,在部分特殊情况下的性能也有所提升.
  4. 或者将代码下载下来, 将BarrageRenderer/目录添加到您的工程当中
  5. 在需要使用弹幕渲染功能的地方 #import<BarrageRenderer/BarrageRenderer.h>
  6. 创建BarrageRenderer,添加BarrageRenderer.view, 执行start方法, 通过receive方法输入弹幕描述符descriptor, 即可以显示弹幕. 详见demo.
  7. demo的基本功能演示了: 如何在view上增加一条弹幕, 如何启动、停止、暂停、恢复弹幕播放, 如何减速弹幕的运动速度.
  8. demo的高级功能演示了: 如何使用自定义方式添加图文混排弹幕,如何支持录播中在固定时间点显示固定弹幕的逻辑.
  9. 相关的一篇博文

使用细节

基本使用

一般的,你只需要在 - (void)viewDidLoad 里创建一个 BarrageRenderer 对象,并将其 view add 到你想要添加弹幕动画的 view 上,配置就结束了。

当你想要添加一条弹幕到屏幕上的时候,你只需要创建一个弹幕描述符 BarrageDescriptor, 为其指定弹幕 Sprite 的类名,然后通过 params 设置一些属性, 调用 BarrageRenderer 的 receive 方法即可成功将弹幕显示在屏幕上.

弹幕支持的属性可参照 BarrageSpriteProtocol.h 文件. 以及在 BarrageSprite 族的属性

动态移除弹幕

2.1.0 新增特性。

在某些情况下,你可能需要从屏幕中动态地移除弹幕。2.1.0版本为此提供了一个默认的弹幕标识符 params[@"identifier"] 以及 一个移除弹幕的方法 - (void)removeSpriteWithIdentifier:(NSString *)identifier;。 举例而言,你可以在用户点击弹幕的时候,移除弹幕,代码如下:

    __weak BarrageRenderer *render = _renderer;
    descriptor.params[@"clickAction"] = ^(NSDictionary *params){
        [render removeSpriteWithIdentifier:params[@"identifier"]];
    };

更新弹幕视图

2.1.0 新增特性。

有时候,你想要为你的弹幕精灵 view 添加动画。当然,你可以使用 animation 或者 NSTimer. 由于 BarrageRenderer 整体由 CADisplayLink 驱动,你可以借用 BarrageRenderer 的时钟,来更新你的精灵 view 。这样做的好处在于,当你通过 BarrageRenderer 暂停弹幕时,你的 弹幕精灵 view 也将暂停。为此,你可以在自定义弹幕精灵 view 的时候,实现协议方法 - (void)updateWithTime:(NSTimeInterval)time来依据时间更新你的 view 。连贯起来就成了动画。你可以参考 demo 中 AvatarBarrageView 类的实现。

需要注意的是,- (void)updateWithTime:(NSTimeInterval)time 中不要放置过多的计算逻辑。在大量弹幕下,这样有可能造成动画的卡顿。

设置靠边位置

1.9.0 版本支持为过场弹幕与悬浮弹幕设置"靠边"属性。对于过场弹幕,可设置side(BarrageWalkSide)属性;对于悬浮弹幕,可设置side(BarrageFloatSide)属性。代码表现为:

descriptor.params[@"side"] = @(BarrageWalkSideRight); // 过场弹幕中,靠右侧行驶
descriptor.params[@"side"] = @(BarrageFloatSideLeft); // 悬浮弹幕中,靠屏幕左侧堆叠

具体,可参考代码注释,以及demo中的使用范例。

设置隐入隐出

1.8.0 版本新增属性,仅对悬浮弹幕有效,设置如下:

descriptor.params[@"fadeInTime"] = @(1);  // 隐入时间
descriptor.params[@"fadeOutTime"] = @(1); // 隐出时间

图文混排弹幕

最简单的弹幕只是文本, 但有时候你可能需要添加emoji表情或者图片上去。emoji表情是UTF字符集原生支持的,对待他和其他的文本字符没有区别;对于图片,你有两种方式可以添加图片弹幕, 一种是使用 attributedText 设置属性文本,一种是自定义 view. 自定义 view 可以参考 BarrageWalkImageTextSprite。 需要注意的是,如果 - (UIView *)bindingView 方法返回的是你自定义的 view,你需要覆盖你自定义 view 的 - (CGSize)sizeThatFits 方法,返回正确的 view 大小。

在 V2 版本中,bindingView 方法被废除,你需要通过 descriptor.params[@"viewClassName"] 指明 sprite 所要关联的 view 类。

直接在 Sprite 子类中布局元素

你可能在方法 - (UIView *)bindingView 中创建了许多视图元素,而并非返回一个自定义 view,因此,这时候你并不方便自定义 view 的 - (CGSize)sizeThatFits 方法,为此你可以选择覆盖 BarrageSprite 的 size 属性的 - (CGSize)size 方法,在此方法中返回你的弹幕 view 的大小。当然,在 - (UIView *)bindingView 里你要设置各个子 view 的位置,以及处理一些可变大小元素比如 UILabel 的布局问题。

在 V2 版本中,bindingView 方法被废除,相关的子 view 布局则写在 sprite 关联的 view 类中。

外部设置弹幕元素的大小

你也可以在创建弹幕描述符的时候强制指定弹幕元素的大小。通过设置:

BarrageDescriptor.params[@"mandatorySize"]

设置此属性之后,你自定义的弹幕 view 的 - (CGSize)sizeThatFits 将不再起作用,但是覆盖的 - (CGSize)size 方法仍然是有效的,因为它的优先级比较高。

如何调节轨道数量

继承自 BarrageFloatSprite 与 BarrageWalkSprite 的弹幕都有 trackNumber 属性,你可以用它来设置弹幕轨道数量。在宏 STRIP_NUM 中规定了最大的轨道数量。需要注意的是,BarrageRenderer 中的轨道概念比其他一些弹幕库的轨道概念更复杂,用它可以比较精确地进行冲突检测。当你的弹幕 view 拥有不同大小的时候,你会意识到他的威力。

如果你只是希望简单地调节一下轨道数量,你只需为 trackNumber 属性设置一个值即可。比如10,20... 并注意不要超过 STRIP_NUM 的值。

如何绑定视频播放时间,即支持快进快退

这其实是非直播类视频弹幕的刚需。由于涉及到弹幕存储,所以有些内容并不是单独 BarrageRenderer 可以解决的。BarrageRenderer 支持将弹幕绑定到视频的时间点上。实现策略一般有如下几步:

  1. 在视频初始化的时候,批量添加弹幕
  2. 设置 BarrageRenderer 的 redisplay 属性为 YES, 指定其 delegate.
  3. 对于1条被添加的 BarrageDescriptor, 为其指定 delay,delay 是此条弹幕对应的视频时间点(一般从服务器端获得);在 2.1.0 版本之后,请尽量调用load方法。如果此条被添加的弹幕 BarrageDescriptor 来自某方实时发送的(因而服务端接口并未给出对应视频时间点),可调用 receive 方法。
  4. 实现 BarrageRendererDelegate 协议方法, 在 - (NSTimeInterval)timeForBarrageRenderer:(BarrageRenderer *)renderer; 方法中返回当前的视频时间点. 当你的视频播放、快进或者快退时,这个时间也会有变。

在 Demo 的 AdvancedBarrageController 中演示了这一流程,可以参照。

如何控制弹幕显示的区域

新版已经支持配置弹幕的显示区域。当你把 BarrageRenderer.view 添加到你的业务 view 上之后,默认情况下,弹幕的 view 会适应你的业务 view,你可以通过 BarrageRenderer 的 canvasMargin 属性来设置弹幕显示区域相对于你业务 view 的大小。如: _renderer.canvasMargin = UIEdgeInsetsMake(10, 10, 10, 10);

修改弹幕的初始位置

原生的 BarrageSprite 子类不支持自定义弹幕位置。如果需要,你需要自定义你自己的 BarrageSprite。你可以继承 BarrageWalkTextSprite ,然后覆盖 - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites根据屏幕上已有的同类弹幕信息返回你的弹幕的初始位置。另外需要注意的是,如果你是继承的 BarrageWalkSprite ,你还需要在此方法中计算出终点位置 CGPoint _destination;的值。

如何设置弹幕速率与文本长度正相关

一些弹幕组件的速度会与文本长度成正比,这在 BarrageRenderer 中实现起来也十分容易。在创建弹幕描述符 BarrageDescriptor 的时候,根据文本长度设置 BarrageSprite 的速度值即可。

限制过场弹幕只显示屏幕上方5行

虽然 BarrageWalkSprite 弹幕设有 trackNumber 属性,但是 trackNumber 的本质,并不很适合某些业务下,固定屏幕弹幕行数的需求。若非要如此,比如需要限制过场弹幕只显示屏幕上方5行,可继承 BarrageWalkTextSprite ,重写- (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites方法,如下:

- (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites
{
    CGRect newRect = rect;
    newRect.size.height = 100; // 这里你根据你的文本大小,为固定的行数估出一个合适的高度来
    self.trackNumber = 5; // 这里是你需要限定的弹幕行数,这里设置之后,descriptor 的 trackNumber 参数自然就失效了
    return [super originInBounds:newRect withSprites:sprites];
}

如此,就生成了满足上述需求的弹幕形式。

为弹幕添加点击操作

BarrageRenderer 默认关闭了交互行为的,但如果需要,你可以启用,只需两步:

  1. BarrageRenderer.view.userInteractionEnabled = YES;
  2. 为 descriptor.params[@"clickAction"] 添加参数

在 2.0.1 版本,clickAction 指定的 block 增加了参数,以支持在点击阶段拿到弹幕的相关信息,比如弹幕消息的 id 。

如何使事件透传到底层(业务)view

开启 BarrageRenderer.view.userInteractionEnabled 之后,所有的事件都会被 BarrageRenderer 拦截掉而到不了你的业务 view,这时候你如果在你的业务 View 上添加一个 Button,而 BarrageRenerer.view 又在 Button 之上的话,那么点击这个 Button 是无效的。你可以设置只拦截弹幕上的事件,而将 BarrageRenderer.view 上的事件透传。通过设置属性:

  • BarrageRenderer.masked = NO; // 默认为YES

如何对弹幕进行限流

通过 - (NSInteger)spritesNumberWithName:(NSString *)spriteName; 方法可以获取屏幕上当前的弹幕数量,你可以在调用 BarrageRenderer 的 receive 方法之前,获取屏幕上的弹幕数量,然后根据一定的规则决定要不要添加这条弹幕。缩小过场弹幕的速度幅度,也有助于降低弹幕的重叠几率。当然,最合理的方式,还是服务端将大量的弹幕过滤到一个合适的范围之中。

为弹幕添加背景图片

框架原生的 BarrageSprite 族并不支持添加背景图片。如果业务需要,可以通过继承 BarrageSprite 的方式添加。

提升动画性能

弹幕一般呈现在视频之上,而视频解码会消耗大量的 CPU,当可用 CPU 不足时,弹幕动画会出现卡顿。为使弹幕流畅,你可以将 trackNumber 调低一些。另外可以对屏幕上的弹幕数量进行限流。

实测中,如果多个弹幕的delay时间相同(或相距在1/60s之内),可能使这些弹幕同时进入屏幕,进而导致瞬间卡顿。真实直播弹幕环境下,这种情况出现的比较少。针对性能较好的iPhone,可以设置 BarrageRenderer 的平滑系数 smoothness ,以优化此问题。此参数从 V2 开始支持。

V2 重构

自 2.0 版本起, 对 sprite 及 dispatcher 进行了较大幅度的调整。主要有如下几点:

  1. 分离 sprite 更新逻辑与弹幕视图,方便两者组合复用
  2. 针对前版本 layout 不方便使用的问题做了优化
  3. 为视图添加复用机制(不过实测中并没有太大性能提升)
  4. 增加平滑度参数,优化一些特殊情况下的性能。

如果你在使用 V1 系列时,没有创建自己的 sprite 子类,那么你可以在不改动业务代码的时候,升级到 V2 版本; 否则,你需要改动你的 sprite 子类,当然,改动不会太大。

虽然我对V2版本做了测试,但是无法涵盖所有情况。 如果你的应用难以承担较高风险,那么你也可以保持使用 V1 系列,等到 V2 版本相对稳定时再行迁移, V1 不会再添加新的 feature, 但对于显著的 bug 我还是会提供修复; 如果你刚刚接入 V2, 那么建议你尝试使用 V2。

V2 在创建自定义弹幕的时候,涉及到两部分:

1.继承对应的 BarrageRenderer 子类,你也可以直接使用默认的 BarrageWalkSprite 或 BarrageFloatSprite,涉及到修改对应的 view 时,在创建 descriptor 的时候增加一条如下的代码:

descriptor.params[@"viewClassName"] = @"UILabel";

2.将原来写在 sprite 子类 bindingView 中的布局代码迁出到独立的view中,为此类实现 BarrageViewProtocol 协议中的方法;一般可以为 view 类添加相应的扩展。比如 UILabel+BarrageView.h。如此,你的 sprite 不必再关心布局的细节,只需要处理好时间逻辑。

更详细的使用,你可以参考 BarrageRenderer 中提供的 sprite 默认实现或者 demo。

load 方法的语义变化

在 v2.1.0 及之后的版本, load 语义有所调整。之前,load 方法所触发的 receive 调用, 会调整 descriptor 的 delay 参数; 而之后的版本,不再整 descriptor 的 delay 参数。所以对于播放弹幕 前/过程中 从网络加载的批量弹幕(delay属性是具体不变的),推荐使用 load 方法。

支持与联系

  • 欢迎在GitHub上提出相关的issue;
  • 欢迎加入qq群讨论:325298378(回复不一定及时).

barragerenderer's People

Contributors

cornerant avatar evianzhow avatar parsifalc avatar unash avatar wongzigii avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

barragerenderer's Issues

循环播放

是否有循环播放的 feature?如果有的话怎么设置?

Engine

/// 冒泡法排序,值越大越往后
- (void)stackSpirits
{
    NSMutableArray * spirits = [NSMutableArray arrayWithArray:_spirits];
    NSInteger num = spirits.count;
    for (NSInteger i = 0; i < num - 1; i++) { //TODO: 这里如果num 换成 spirits.count, 会产生诡异的死循环
        for (NSInteger j = i+1; j < num; j++) {
            BarrageSpirit * spiritA = [spirits objectAtIndex:i];
            BarrageSpirit * spiritB = [spirits objectAtIndex:j];
            if (spiritA.z_index > spiritB.z_index) {
                [spirits exchangeObjectAtIndex:i withObjectAtIndex:j];
            }
        }
    }
    _spirits = [spirits copy];
}
  1. 排序不要自己去写,_spirits = [_spirits sortedArrayWith...]
  2. 产生死循环因为 sprits.countNSUInteger 无符号整型

swift 3.0 会报错

start之后会报 -[_SwiftTypePreservingNSNumber length]: unrecognized selector sent to instance 0x60000043db80的错误,看了半天不太好定位具体的位置。。。。

弹幕精灵重叠问题

你好,当弹幕的展示区域较小,或者弹幕精灵的速度随机,或者某个时间段弹幕精灵的数量较多,都有一定概率出现某些弹幕精灵重叠在一起的情况,尽管可以通过spritesNumberWithName来限制精灵数量,但重叠的情况仍然会出现,求解决方案

issue when Build

download project but can't run.这个下载下来就跑不了啊。需要什么特别设置?

Clock

/// 更新逻辑时间系统
- (void)updateTime
{
    NSDate * currentDate = [NSDate date];
    self.time += [currentDate timeIntervalSinceDate:_previousDate] * self.speed;
    _previousDate = currentDate;
}

这里每帧都会运行,构造 NSDate 开销可能比较大,只记时间戳可能好点

seek之后暂停,时钟系统乱了

外部播放器seek 改变_predictedTime的值通过代理方法timeForBarrageRenderer: 传递到弹幕库,接着执行pause方法,时间好像乱了。

关于弹幕颜色的问题

在设置弹幕的text时,如果text中不包含中文(测试过字母和数字混合),那么设置其颜色不起作用,显示为蓝色,且带下划线.只有当弹幕含有中文时,设置其颜色才起作用.text为NSTaggedPointerString类型,请问这种情况应该怎么解决?

视频快进快退弹幕处理

如果播放视频到15s,快退5s,这时是10s的时候。之前10s-15s播放过的弹幕不会再播放吗,需要再等到视频15s的时候才能继续播弹幕?
如果要实现10s-15s的时候播放快退前播放过的弹幕,应该怎么处理这块逻辑?

怎么设置轨道顺序?

现在默认第一轨道在顶部,第一条弹幕就是从最上面出来,有设置让第一个弹幕从最下面出来的方法吗?

崩溃问题

初始化的时候崩溃了。本人的需求是需要经常的添加,删除弹幕。(经常init,removeFromSuperView)

初始化代码如下:

private var render: BarrageRenderer! //弹幕

render = BarrageRenderer()
render.redisplay = true
renderBackgroundView.addSubview(render.view)
renderBackgroundView.sendSubviewToBack(render.view)
render.start()

崩溃信息如图:
qq20161019-0

手势无响应.

给的方法无效. 如果底层视图有手势,则不响应点击事件.

自定义图文混排弹幕

参考你demo中的图文混排弹幕继承于 BarrageWalkTextSprite 类,并且覆盖 - (UIView *)bindingView
和 - (CGSize)size 方法来实现自定义图文混排弹幕,demo中是直接 self.text 获取要展示的文本,但是当我文本较多并且有图片之类的元素该如何传值到自定义的这个类中并在 bindingView 中进行计算设置。

自定义View

最近在做直播的项目,恰好找到您这个BarrageRenderer,非常高兴,现在我想模仿其他App(inke)的弹幕功能,就是弹幕是在收到socket时动态产生,弹幕主题由用户头像、用户昵称、用户发言内容组成,要求用户头像可以点击,进入用户的个人主页,现在想请教下,这个能否用BarrageRenderer实现,并说下大致思路;或者自己写一个view,然后动画从右至左,然后解决复用;哪种比较好?

容器视图内存问题


您好,我在容器子视图内使用了您的弹幕,切换子视图都将子视图至为空,当第二次加载以前加载过的视图的时候会出现内存问题,请问是为什么呢。

iOS10 swift3.0 使用pod 安装 按照demo写了一段代码,但是没有生成弹幕,log 显示弹幕数量一直为0,代码如下

import UIKit
class BarrageViewController: UIViewController {

let renderer = BarrageRenderer.init()
var timer: Timer?

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    view.addSubview(renderer.view)
    renderer.canvasMargin = UIEdgeInsetsMake(10, 10, 10, 10)
    renderer.view.isUserInteractionEnabled = true
    view.sendSubview(toBack: renderer.view)
    

}


override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    renderer.start()
    let safeObj = NSSafeObject.init(object: self, with: #selector(BarrageViewController.autoSendBarrage))
    if #available(iOS 10.0, *) {
        timer = Timer.scheduledTimer(timeInterval: 0.5, target: safeObj!, selector: #selector(NSSafeObject.excute), userInfo: nil, repeats: true)
    } else {
        // Fallback on earlier versions
        
    }
    
}


func autoSendBarrage() {
    let spriteNumber = renderer.spritesNumber(withName: nil)
    print("当前屏幕的弹幕数量: \(spriteNumber)")
    if spriteNumber < 100 {
        renderer.receive(walkTextSpriteDescription(withDirection: BarrageWalkDirection.R2L, side: BarrageWalkSide.left))
        renderer.receive(floatingTextSpriteDescriptor(withDirection: BarrageWalkDirection.B2T, side: BarrageFloatSide.center))
    }
}



func walkTextSpriteDescription(withDirection direction: BarrageWalkDirection, side: BarrageWalkSide) -> BarrageDescriptor {
    let descriptor = BarrageDescriptor()
    descriptor.spriteName = "walk"
    descriptor.params["attributedText"] = NSAttributedString.init(string: "过场文字: \(index = index + 1)", attributes: [NSForegroundColorAttributeName: UIColor.red, NSFontAttributeName: UIFont.systemFont(ofSize: 20)])

    descriptor.params["speed"] = NSNumber.init(value: 1.0)
    descriptor.params["direction"] = NSNumber.init(value: direction.rawValue)
    descriptor.params["side"] = NSNumber.init(value: side.rawValue)
    descriptor.params["clickAction"] = {
        print("click")
    }
    return descriptor
}

var index = 0
func floatingTextSpriteDescriptor(withDirection direction: BarrageWalkDirection, side: BarrageFloatSide) -> BarrageDescriptor {
    let descriptor = BarrageDescriptor()
    descriptor.spriteName = "floating"
    descriptor.params["text"] = "浮动文字: \(index = index + 1)"
    descriptor.params["textColor"] = UIColor.red
    descriptor.params["duration"] = NSNumber.init(value: 3)
    descriptor.params["fadeInTime"] = NSNumber.init(value: 1)

    descriptor.params["fadeOutTime"] = NSNumber.init(value: 1)
    descriptor.params["direction"] = NSNumber.init(value: direction.rawValue)
    descriptor.params["side"] = NSNumber.init(value: side.rawValue)
    return descriptor

}
deinit {
    renderer.stop()
}

}
`

优化建议

添加一个缓存池存储弹幕,现在bindingView每次都要新建一个view
barrageImageScaleToSize里面试试用CoreImage替代CoreGraphics来处理图片

弹幕显示的区域/行数限制问题

当我设置弹幕view的edges之后,比如底部留下1/3的位置不显示,当横屏下弹幕数量较多,我切回竖屏的时候,屏幕上的弹幕会被切,view的布局更新了,但是弹幕本身的布局还是横屏的,就会被view的边缘砍到。
另:有没有灵活的改变字体大小的方法呢?
另:每次load都会导致所有弹幕全部从当前时间加载,而不会按照播放时间加载~descriptor能提供timestamp参数么,当前用的是delay

自定义表情图文混排问题。

请教图文混排问题:

我在descriptor.params里加入了attributedText字段,弹幕中无法显示。
是否需要进行其他设置?

BarrageClock.h请引入UIKit.h

我是复制的文件来编译为.a库的,这里少个引入,没什么大问题。

另外我想说我的效果是这样的:
从最左下方一行一行往上弹,发一个弹一个,最后发的在最下面,发一下把前面发的依次往上弹一个位置,就像聊天框那样的效果,不知道这个要用你的库怎么弄才好,给个思路吧。

error Barrage*6

Undefined symbols for architecture x86_64:
"OBJC_CLASS$_BarrageDescriptor", referenced from:
objc-class-ref in AdvancedBarrageController.o
"OBJC_CLASS$_BarrageRenderer", referenced from:
objc-class-ref in AdvancedBarrageController.o
"OBJC_CLASS$_BarrageWalkTextSprite", referenced from:
objc-class-ref in AdvancedBarrageController.o
OBJC_CLASS$_BarrageWalkImageTextSprite in BarrageWalkImageTextSprite.o
"OBJC_CLASS$_MLEmojiLabel", referenced from:
objc-class-ref in BarrageWalkImageTextSprite.o
"OBJC_METACLASS$_BarrageWalkTextSprite", referenced from:
OBJC_METACLASS$_BarrageWalkImageTextSprite in BarrageWalkImageTextSprite.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

希望可以对初始显示的轨道,进行设置。

首先感谢作者的付出。
现在默认都是上往下,我希望第一个出现的轨道,是最底部的轨道。
我试着继承 BarrageWalkTextSprite , 然后覆盖- (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites 返回你设置的弹幕初始位置.

- (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites
{
    CGPoint point = [super originInBounds:rect withSprites:sprites];
    return point+200;
}

初始位置改变了,但是路线,变成了从”右下->左上“的斜线。

希望可以添加一个接口,设置初始显示的轨道。谢谢

请问没有碰撞检测么

比如在视频某个时间点有很多弹幕,这个时候所有弹幕都重叠在了一起,这个目前有解决方案么?

在view上添加了弹幕,在程序运行期间崩溃

我的数据源是200条数据,当执行到200条之后就报了这个错误,是执行完还要什么操作吗?
libc++abi.dylib: terminate_handler unexpectedly threw an exception

  • (void)setAllViews{
    [self initBarrageRenderer];

    NSSafeObject * safeObj = [[NSSafeObject alloc]initWithObject:self withSelector:@selector(autoSendBarrage)];
    _timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:safeObj selector:@selector(excute) userInfo:nil repeats:YES];
    }

  • (void)initBarrageRenderer
    {
    _renderer = [[BarrageRenderer alloc]init];
    _renderer.view.backgroundColor = UIColorFromRGB(SubjectColorValue);
    _renderer.canvasMargin = UIEdgeInsetsMake(0, 0, 70, 0);

    [self addSubview:_renderer.view];
    [self sendSubviewToBack:_renderer.view];
    }

  • (void)setDataStartWith:(NSMutableArray *)loadArray{

    _modelArr = [NSMutableArray array];
    for (NSDictionary *tempDict in loadArray) {
    BarrageModel *model = [[BarrageModel alloc]initWithDict:tempDict];
    [_modelArr addObject:model];
    }
    [_renderer start];
    }

  • (void)autoSendBarrage{
    NSInteger spriteNumber = [_renderer spritesNumberWithName:nil];
    if (spriteNumber <= 20) {
    [_renderer receive:[self walkTextSpriteDescriptorWithDirection:BarrageWalkDirectionR2L]];
    }

}

  • (BarrageDescriptor *)walkTextSpriteDescriptorWithDirection:(NSInteger)direction
    {
    BarrageModel *model = _modelArr[_index];
    BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init];
    descriptor.spriteName = NSStringFromClass([BarrageWalkTextSprite class]);
    descriptor.params[@"text"] = [NSString stringWithFormat:@"%@ %@",model.UserNick,model.content];
    descriptor.params[@"textColor"] = [UIColor blueColor];
    descriptor.params[@"speed"] = @(100 * (double)random()/RAND_MAX+50);
    descriptor.params[@"direction"] = @(direction);
    descriptor.params[@"trackNumber"] = @20; // 轨道数量

    _index ++;
    return descriptor;

}

绑定了delay 却没有从指定时间出来

NSLog(@"视频总长速度:%d",time);
NSMutableArray *descriptors = [NSMutableArray new];
for (int i = 0; i< time; i++) {
[descriptors addObject:[self walkTextSpriteDescriptorWithDirection:BarrageWalkDirectionR2L withIndex:i withAdd:NO]];
}
[_renderer load:descriptors];
[_renderer start];

index 为上述for循环里的 i ,delay是绑定事件参数,可是视频播放 弹幕却是一个脑儿得横着飘出来,视频播到一半 弹幕已经显示完全了
descriptor.params[@"delay"] = @(index);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.