| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- // Part of BarrageRenderer. Created by UnAsh.
- // Blog: http://blog.exbye.com
- // Github: https://github.com/unash/BarrageRenderer
- // This code is distributed under the terms and conditions of the MIT license.
- // Copyright (c) 2015年 UnAsh.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #import "BarrageRenderer.h"
- #import "BarrageCanvas.h"
- #import "BarrageDispatcher.h"
- #import "BarrageSprite.h"
- #import "BarrageSpriteFactory.h"
- #import "BarrageClock.h"
- #import "BarrageDescriptor.h"
- NSString * const kBarrageRendererContextCanvasBounds = @"kBarrageRendererContextCanvasBounds"; // 画布大小
- NSString * const kBarrageRendererContextRelatedSpirts = @"kBarrageRendererContextRelatedSpirts"; // 相关精灵
- NSString * const kBarrageRendererContextTimestamp = @"kBarrageRendererContextTimestamp"; // 时间戳
- @interface BarrageRenderer()<BarrageDispatcherDelegate>
- {
- BarrageDispatcher * _dispatcher; //调度器
- BarrageCanvas * _canvas; // 画布
- BarrageClock * _clock;
- NSMutableDictionary * _spriteClassMap;
- NSMutableDictionary * _context; // 渲染器上下文
-
- NSMutableArray * _preloadedDescriptors; //预加载的弹幕
- NSMutableArray * _records;//记录数组
- NSDate * _startTime; //如果是nil,表示弹幕渲染不在运行中; 否则,表示开始的时间
- NSTimeInterval _pausedDuration; // 暂停持续时间
- NSDate * _pausedTime; // 上次暂停时间; 如果为nil, 说明当前没有暂停
- }
- @property(nonatomic,assign)NSTimeInterval time;
- @property(nonatomic,assign)NSTimeInterval pausedDuration; // 暂停时间
- @end
- @implementation BarrageRenderer
- @synthesize pausedDuration = _pausedDuration;
- #pragma mark - init
- - (instancetype)init
- {
- if (self = [super init]) {
- _canvas = [[BarrageCanvas alloc]init];
- _spriteClassMap = [[NSMutableDictionary alloc]init];
- _zIndex = NO;
- _context = [[NSMutableDictionary alloc]init];
- _recording = NO;
- _startTime = nil; // 尚未开始
- _pausedTime = nil;
- _redisplay = NO;
- _smoothness = 0.0f;
- self.pausedDuration = 0;
- [self initClock];
- }
- return self;
- }
- /// 初始化时钟
- - (void)initClock
- {
- __weak id weakSelf = self;
- _clock = [BarrageClock clockWithHandler:^(NSTimeInterval time){
- BarrageRenderer * strongSelf = weakSelf;
- strongSelf.time = time;
- [strongSelf update];
- }];
- }
- #pragma mark - control
- /// 接收弹幕,并校对时间
- - (void)receive:(BarrageDescriptor *)descriptor
- {
- [self receive:descriptor withCorrection:YES];
- }
- /// 接收弹幕, 并决定是否要校对时间
- - (void)receive:(BarrageDescriptor *)descriptor withCorrection:(BOOL)correction
- {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (!_startTime) { // 如果没有启动,则抛弃接收弹幕
- return;
- }
- BarrageDescriptor * descriptorCopy = [descriptor copy];
- if (correction) {
- [self convertDelayTime:descriptorCopy];
- }
- BarrageSprite * sprite = [BarrageSpriteFactory createSpriteWithDescriptor:descriptorCopy];
- [_dispatcher addSprite:sprite];
- if (_recording) {
- [self recordDescriptor:descriptorCopy];
- }
- });
- }
- - (void)start
- {
- // 如果之前调整过frame,_canvas的layoutSubviews不会及时调用,于是显示时会出问题
- // 解决bug: https://github.com/unash/BarrageRenderer/issues/16
- [_canvas setNeedsLayout];
- if (!_startTime) { // 尚未启动,则初始化时间系统
- _startTime = [NSDate date];
- _records = [[NSMutableArray alloc]init];
- _dispatcher = [[BarrageDispatcher alloc]init];
- _dispatcher.smoothness = self.smoothness;
- _dispatcher.cacheDeadSprites = self.redisplay;
- _dispatcher.delegate = self;
- }
- else if(_pausedTime)
- {
- _pausedDuration += [[NSDate date]timeIntervalSinceDate:_pausedTime];
- }
- _pausedTime = nil;
- [_clock start];
- if (_preloadedDescriptors.count) {
- for (BarrageDescriptor * descriptor in _preloadedDescriptors) {
- [self receive:descriptor withCorrection:NO];
- }
- [_preloadedDescriptors removeAllObjects];
- }
- }
- - (void)pause
- {
- if (!_startTime) { // 没有运行, 则暂停无效
- return;
- }
- if (!_pausedTime) { // 当前没有暂停
- [_clock pause];
- _pausedTime = [NSDate date];
- }
- else
- {
- _pausedDuration += [[NSDate date]timeIntervalSinceDate:_pausedTime];
- _pausedTime = [NSDate date];
- }
- }
- - (void)stop
- {
- _startTime = nil;
- [_clock stop];
- _pausedDuration = 0.0f;
- [_dispatcher deactiveAllSprites];
- }
- - (void)setSpeed:(CGFloat)speed
- {
- if (speed > 0) {
- _clock.speed = speed;
- }
- }
- - (CGFloat)speed
- {
- return _clock.speed;
- }
- - (void)setRedisplay:(BOOL)redisplay
- {
- _redisplay = redisplay;
- if (_dispatcher) {
- _dispatcher.cacheDeadSprites = _redisplay;
- }
- }
- - (void)setSmoothness:(CGFloat)smoothness
- {
- _smoothness = smoothness;
- if (_dispatcher) {
- _dispatcher.smoothness = smoothness;
- }
- }
- - (NSTimeInterval)pausedDuration
- {
- return _pausedDuration + (_pausedTime?[[NSDate date]timeIntervalSinceDate:_pausedTime]:0); // 当前处于暂停当中
- }
- /// 获取当前时间
- - (NSTimeInterval)currentTime
- {
- NSTimeInterval currentTime = 0.0f;
- if (self.delegate && [self.delegate respondsToSelector:@selector(timeForBarrageRenderer:)]) {
- currentTime = [self.delegate timeForBarrageRenderer:self];
- }
- else
- {
- currentTime = [[NSDate date]timeIntervalSinceDate:_startTime]-self.pausedDuration;
- }
- return currentTime;
- }
- /// 转换descriptor的delay时间(相对于start), 如果delay<0, 则将delay置为0
- - (void)convertDelayTime:(BarrageDescriptor *)descriptor
- {
- NSTimeInterval delay = [[descriptor.params objectForKey:@"delay"]doubleValue];
- delay += [self currentTime];
- if (delay < 0) {
- delay = 0;
- }
- [descriptor.params setObject:@(delay) forKey:@"delay"];
- }
- - (NSInteger)spritesNumberWithName:(NSString *)spriteName
- {
- NSInteger number = 0;
- if (spriteName) {
- Class class = NSClassFromString(spriteName);
- if (class) {
- for (BarrageSprite * sprite in _dispatcher.activeSprites) {
- number += [sprite class] == class;
- }
- }
- }
- else
- {
- number = _dispatcher.activeSprites.count;
- }
- return number;
- }
- - (void)removePresentSpritesWithName:(NSString *)spriteName
- {
- Class class = NSClassFromString(spriteName);
- for (BarrageSprite * sprite in _dispatcher.activeSprites) {
- if (!class || [sprite class] == class) {
- [sprite forceInvalid];
- }
- }
- }
- - (void)removeSpriteWithIdentifier:(NSString *)identifier
- {
- for (BarrageSprite * sprite in _dispatcher.activeSprites) {
- if ([sprite.viewParams[@"identifier"] isEqualToString:identifier]) {
- [sprite forceInvalid];
- break;
- }
- }
- }
- #pragma mark - record
- /// 此方法会修改desriptor的值
- - (void)recordDescriptor:(BarrageDescriptor *)descriptor
- {
- __block BOOL exists = NO;
- [_records enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL * stop){
- if([((BarrageDescriptor *)obj).identifier isEqualToString:descriptor.identifier]){
- exists = YES;
- *stop = YES;
- }
- }];
- if(!exists){
- [_records addObject:descriptor];
- }
- }
- - (NSArray *)records
- {
- return [_records copy];
- }
- - (void)load:(NSArray *)descriptors
- {
- if (_startTime) {
- for (BarrageDescriptor * descriptor in descriptors) {
- [self receive:descriptor withCorrection:NO];
- }
- }
- else
- {
- if (!_preloadedDescriptors) {
- _preloadedDescriptors = [[NSMutableArray alloc]init];
- }
- for (BarrageDescriptor * descriptor in descriptors) {
- [_preloadedDescriptors addObject:[descriptor copy]];
- }
- }
- }
- #pragma mark - update
- /// 每个刷新周期执行一次
- - (void)update
- {
- [_dispatcher dispatchSprites]; // 分发精灵
- for (BarrageSprite * sprite in _dispatcher.activeSprites) {
- [sprite updateWithTime:self.time];
- }
- }
- #pragma mark - BarrageDispatcherDelegate
- - (BOOL)shouldActiveSprite:(BarrageSprite *)sprite
- {
- return !_pausedTime;
- }
- - (void)willActiveSprite:(BarrageSprite *)sprite
- {
- NSValue * value = [NSValue valueWithCGRect:_canvas.bounds];
- [_context setObject:value forKey:kBarrageRendererContextCanvasBounds];
-
- NSArray * itemMap = [_spriteClassMap objectForKey:NSStringFromClass([sprite class])];
- if (itemMap) {
- [_context setObject:[itemMap copy] forKey:kBarrageRendererContextRelatedSpirts];
- }
-
- [_context setObject:@(self.time) forKey:kBarrageRendererContextTimestamp];
-
- NSInteger index = [self viewIndexOfSprite:sprite];
-
- [sprite activeWithContext:_context];
- [self indexAddSprite:sprite];
- [_canvas insertSubview:sprite.view atIndex:index];
- if (self.delegate && [self.delegate respondsToSelector:@selector(barrageRenderer:spriteStage:spriteParams:)]) {
- [self.delegate barrageRenderer:self spriteStage:BarrageSpriteStageBegin spriteParams:sprite.viewParams];
- }
- }
- - (NSUInteger)viewIndexOfSprite:(BarrageSprite *)sprite
- {
- NSInteger index = _dispatcher.activeSprites.count;
-
- /// 添加根据z-index 增序排列
- if (self.zIndex) {
- NSMutableArray * preSprites = [[NSMutableArray alloc]initWithArray:_dispatcher.activeSprites];
- [preSprites addObject:sprite];
- NSArray * sortedSprites = [preSprites sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
- return [@(((BarrageSprite *)obj1).z_index) compare:@(((BarrageSprite *)obj2).z_index)];
- }];
- index = [sortedSprites indexOfObject:sprite];
- }
- return index;
- }
- - (void)willDeactiveSprite:(BarrageSprite *)sprite
- {
- [self indexRemoveSprite:sprite];
- [sprite.view removeFromSuperview];
- [sprite deactive];
- if (self.delegate && [self.delegate respondsToSelector:@selector(barrageRenderer:spriteStage:spriteParams:)]) {
- [self.delegate barrageRenderer:self spriteStage:BarrageSpriteStageEnd spriteParams:sprite.viewParams];
- }
- }
- - (NSTimeInterval)timeForBarrageDispatcher:(BarrageDispatcher *)dispatcher
- {
- if ([dispatcher isEqual:_dispatcher]) {
- return [self currentTime];
- }
- return 0.0f; // 错误情况
- }
- #pragma mark - indexing className-sprites
- /// 更新活跃精灵类型索引
- - (void)indexAddSprite:(BarrageSprite *)sprite
- {
- NSString * className = NSStringFromClass([sprite class]);
- NSMutableArray * itemMap = [_spriteClassMap objectForKey:className];
- if (!itemMap) {
- itemMap = [[NSMutableArray alloc]init];
- [_spriteClassMap setObject:itemMap forKey:className];
- }
- [itemMap addObject:sprite];
- }
- /// 更新活跃精灵类型索引
- - (void)indexRemoveSprite:(BarrageSprite *)sprite
- {
- NSString * className = NSStringFromClass([sprite class]);
- NSMutableArray * itemMap = [_spriteClassMap objectForKey:className];
- if (!itemMap) {
- itemMap = [[NSMutableArray alloc]init];
- [_spriteClassMap setObject:itemMap forKey:className];
- }
- [itemMap removeObject:sprite];
- }
- #pragma mark - attributes
- - (UIView *)view
- {
- return _canvas;
- }
- - (void)setCanvasMargin:(UIEdgeInsets)canvasMargin
- {
- _canvas.margin = canvasMargin;
- }
- - (void)setMasked:(BOOL)masked
- {
- _canvas.masked = masked;
- }
- @end
|