浅谈iOS如何做弹幕
2016-05-06 16:44
645 查看
iOS弹幕demo网上有很多,而且大多数是开源的,很多源代码我们可以自己查看。
弹幕有哪些特点呢?
最基本的特点:
1、文字越长的弹幕,跑的越快,弹幕的速度和文字长度有关系。
2、弹幕不相互碰撞。
3、如果有数据,会一条接着一跳的播放。
基于以上的基本特点,我们可以很简单的想到iOS中可以实现的方式:
1、在view中一贞一贞的draw
2、用系统的动画去做
上面一种柔性很强,可是对编程功底要求比较高,搞不好又影响了性能,还做不出好的效果。
下面一种很简单,只需要动画做好分段,处理好动画间的衔接就可以了;可是柔性相对较差一点。
这里采用系统动画来做,这样我们只需要做好下面几个方面就OK了:
1、重用或者释放;
2、暂停和继续;
3、load新数据;
4、碰撞问题;
这里简单的贴一下思路,希望能为各位敞开一扇门。
首先我们做一个单独的带动画的视图,作为我们的弹幕基本试图,后面我们只需要把这些试图组合起来就成为我们想要的弹幕了。
@interface BulletView : UIView
@property (nonatomic, copy) void(^moveBlock)(CommentMoveStatus status);//状态回调的block
@property (nonatomic, assign) NSInteger trajectory;//所在轨道编号
- (instancetype)initWithCommentDic:(BulletSettingDic *)commentDic;//这里的<span style="font-family: Arial, Helvetica, sans-serif;">BulletSettingDic</span><span style="font-family: Arial, Helvetica, sans-serif;">是一个自定义的类,里面主要设置了我们弹幕的背景颜色、文字颜色、字体、速率等</span>
- (void)reloadDataWithDic:(BulletSettingDic *)reloadDic;//写这个方法方便重用
- (void)startAnimation;
- (void)stopAnimation;
- (void)pauseAnimation;
- (void)resumeAnimation;
@end
CommentMoveStatus是一个枚举集合,里面设置了动画的几种状态:
typedef NS_ENUM(NSInteger, CommentMoveStatus)
{
MoveIn,
Enter,
MoveOut
};
BulletSettingDic是一个设置类,里面给出了弹幕视图的各种设置参数,头文件如下:
这个里面的内容,我们可以随便自定义,然后修改写入视图就好了。
@interface BulletSettingDic : NSObject
{
NSMutableDictionary *_settingDic;
}
//设置字颜色
-(void)setBulletTextColor:(UIColor *)color;
-(UIColor *)bulletTextColor;
//设置背景颜色
-(void)setBulletBackgroundColor:(UIColor *)color;
-(UIColor *)bulletBackgroundColor;
//设置字体
-(void)setBulletTextFont:(UIFont *)font;
-(UIFont *)bulletTextFont;
//设置内容
-(void)setbulletText:(NSString *)text;
-(NSString *)bulletText;
//设置高度
-(void)setBulletHeight:(CGFloat)height;
-(CGFloat)bulletHeight;
//设置动画时长
-(void)setBulletAnimationDuration:(float)duration;
-(float)bulletAnimationDuration;
//设置速度比率
-(void)setBulletAnimationSpeedRate:(float)speedRate;
-(float)bulletAnimationSpeedRate;
-(NSMutableDictionary *)settingDic;@end
下面是核心思路:
//开始动画
- (void)startAnimation
{
__block CGRect frame = self.frame;
__unsafe_unretained typeof(self)weakSelf = self;
//计算移动的时间
CGFloat dur = (CGRectGetMinX(frame)-_screenWidth)/_real_speed;
[UIView animateWithDuration:dur delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
frame.origin.x = _screenWidth;
weakSelf.frame = frame;
} completion:^(BOOL finished)
{
[weakSelf.layer removeAllAnimations];
//弹幕开始进入屏幕
if (weakSelf.moveBlock)
weakSelf.moveBlock(MoveIn);
[weakSelf beginMoveIn];
}];
}
//开始移入-->完全进入
-(void)beginMoveIn
{
__block CGRect frame = self.frame;
__unsafe_unretained typeof(self)weakSelf = self;
//计算移动的时间
CGFloat dur = CGRectGetWidth(frame)/_real_speed;
[UIView animateWithDuration:dur delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
frame.origin.x = _screenWidth-CGRectGetWidth(frame);
weakSelf.frame = frame;
} completion:^(BOOL finished)
{
[weakSelf.layer removeAllAnimations];
//弹幕完全进入屏幕
if (weakSelf.moveBlock)
weakSelf.moveBlock(Enter);
[weakSelf enterIn];
}];
}
//完全进入-->完全移出
-(void)enterIn
{
__block CGRect frame = self.frame;
__unsafe_unretained typeof(self)weakSelf = self;
CGFloat dur = _screenWidth/_real_speed;
[UIView animateWithDuration:dur delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
frame.origin.x = -CGRectGetWidth(frame);
weakSelf.frame = frame;
} completion:^(BOOL finished)
{
//弹幕完全离开屏幕
if (weakSelf.moveBlock)
weakSelf.moveBlock(MoveOut);
[weakSelf.layer removeAllAnimations];
[weakSelf removeFromSuperview];
}];
}
这里面,我做了三段动画:开始-->移入,移入-->完全移入,完全移入-->完全移出,然后每一个完成后都会有相应的block回调出去(这样做是为了方便我们根据状态进行数据和视图的处理,下面会用到)。
_real_speed是动画移动的实际速度,它是由动画移动的速度率和速度相乘得到的,动画的速度是通过屏幕宽度和文本宽度以及动画时间计算得到的,速度率是设置的。
关于暂停和继续,我使用CALayer里面与CAMediaTimming相关的方法,改变速度,纪录时间,做到的。代码如下:
//暂停动画
- (void)pauseAnimation
{
CALayer *layer = self.layer;
layer.fillMode = kCAFillModeForwards;
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
//继续动画
- (void)resumeAnimation
{
CALayer*layer = self.layer;
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
停止和开始就不详述了。
下面说视图的重用,我用了两个数组来做视图的重用,当有弹幕视图移出屏幕的时候我会把移出的视图保存在一个数组中。
在下一个视图制作的时候,直接设置取出来重新加载数据,而不是重新创建对象。
核心操作很简单:
if (_reuseableBulletArray.count)
{
view = [_reuseableBulletArray firstObject];
[view reloadDataWithDic:_bulletDic];
[_reuseableBulletArray removeObjectAtIndex:0];
}else
{
view = [[BulletView alloc] initWithCommentDic:_bulletDic];
}<span style="font-family: Arial, Helvetica, sans-serif;">[_bulletArray addObject:view];</span>
然后在合适的地方(一般是有弹幕视图移出屏幕的地方):
这样就可以不用创建很多的对象了。
整体的一个demo在这里,希望大家用的开心:点击跳转到项目托管地址
弹幕有哪些特点呢?
最基本的特点:
1、文字越长的弹幕,跑的越快,弹幕的速度和文字长度有关系。
2、弹幕不相互碰撞。
3、如果有数据,会一条接着一跳的播放。
基于以上的基本特点,我们可以很简单的想到iOS中可以实现的方式:
1、在view中一贞一贞的draw
2、用系统的动画去做
上面一种柔性很强,可是对编程功底要求比较高,搞不好又影响了性能,还做不出好的效果。
下面一种很简单,只需要动画做好分段,处理好动画间的衔接就可以了;可是柔性相对较差一点。
这里采用系统动画来做,这样我们只需要做好下面几个方面就OK了:
1、重用或者释放;
2、暂停和继续;
3、load新数据;
4、碰撞问题;
这里简单的贴一下思路,希望能为各位敞开一扇门。
首先我们做一个单独的带动画的视图,作为我们的弹幕基本试图,后面我们只需要把这些试图组合起来就成为我们想要的弹幕了。
@interface BulletView : UIView
@property (nonatomic, copy) void(^moveBlock)(CommentMoveStatus status);//状态回调的block
@property (nonatomic, assign) NSInteger trajectory;//所在轨道编号
- (instancetype)initWithCommentDic:(BulletSettingDic *)commentDic;//这里的<span style="font-family: Arial, Helvetica, sans-serif;">BulletSettingDic</span><span style="font-family: Arial, Helvetica, sans-serif;">是一个自定义的类,里面主要设置了我们弹幕的背景颜色、文字颜色、字体、速率等</span>
- (void)reloadDataWithDic:(BulletSettingDic *)reloadDic;//写这个方法方便重用
- (void)startAnimation;
- (void)stopAnimation;
- (void)pauseAnimation;
- (void)resumeAnimation;
@end
CommentMoveStatus是一个枚举集合,里面设置了动画的几种状态:
typedef NS_ENUM(NSInteger, CommentMoveStatus)
{
MoveIn,
Enter,
MoveOut
};
BulletSettingDic是一个设置类,里面给出了弹幕视图的各种设置参数,头文件如下:
这个里面的内容,我们可以随便自定义,然后修改写入视图就好了。
@interface BulletSettingDic : NSObject
{
NSMutableDictionary *_settingDic;
}
//设置字颜色
-(void)setBulletTextColor:(UIColor *)color;
-(UIColor *)bulletTextColor;
//设置背景颜色
-(void)setBulletBackgroundColor:(UIColor *)color;
-(UIColor *)bulletBackgroundColor;
//设置字体
-(void)setBulletTextFont:(UIFont *)font;
-(UIFont *)bulletTextFont;
//设置内容
-(void)setbulletText:(NSString *)text;
-(NSString *)bulletText;
//设置高度
-(void)setBulletHeight:(CGFloat)height;
-(CGFloat)bulletHeight;
//设置动画时长
-(void)setBulletAnimationDuration:(float)duration;
-(float)bulletAnimationDuration;
//设置速度比率
-(void)setBulletAnimationSpeedRate:(float)speedRate;
-(float)bulletAnimationSpeedRate;
-(NSMutableDictionary *)settingDic;@end
下面是核心思路:
//开始动画
- (void)startAnimation
{
__block CGRect frame = self.frame;
__unsafe_unretained typeof(self)weakSelf = self;
//计算移动的时间
CGFloat dur = (CGRectGetMinX(frame)-_screenWidth)/_real_speed;
[UIView animateWithDuration:dur delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
frame.origin.x = _screenWidth;
weakSelf.frame = frame;
} completion:^(BOOL finished)
{
[weakSelf.layer removeAllAnimations];
//弹幕开始进入屏幕
if (weakSelf.moveBlock)
weakSelf.moveBlock(MoveIn);
[weakSelf beginMoveIn];
}];
}
//开始移入-->完全进入
-(void)beginMoveIn
{
__block CGRect frame = self.frame;
__unsafe_unretained typeof(self)weakSelf = self;
//计算移动的时间
CGFloat dur = CGRectGetWidth(frame)/_real_speed;
[UIView animateWithDuration:dur delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
frame.origin.x = _screenWidth-CGRectGetWidth(frame);
weakSelf.frame = frame;
} completion:^(BOOL finished)
{
[weakSelf.layer removeAllAnimations];
//弹幕完全进入屏幕
if (weakSelf.moveBlock)
weakSelf.moveBlock(Enter);
[weakSelf enterIn];
}];
}
//完全进入-->完全移出
-(void)enterIn
{
__block CGRect frame = self.frame;
__unsafe_unretained typeof(self)weakSelf = self;
CGFloat dur = _screenWidth/_real_speed;
[UIView animateWithDuration:dur delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
frame.origin.x = -CGRectGetWidth(frame);
weakSelf.frame = frame;
} completion:^(BOOL finished)
{
//弹幕完全离开屏幕
if (weakSelf.moveBlock)
weakSelf.moveBlock(MoveOut);
[weakSelf.layer removeAllAnimations];
[weakSelf removeFromSuperview];
}];
}
这里面,我做了三段动画:开始-->移入,移入-->完全移入,完全移入-->完全移出,然后每一个完成后都会有相应的block回调出去(这样做是为了方便我们根据状态进行数据和视图的处理,下面会用到)。
_real_speed是动画移动的实际速度,它是由动画移动的速度率和速度相乘得到的,动画的速度是通过屏幕宽度和文本宽度以及动画时间计算得到的,速度率是设置的。
关于暂停和继续,我使用CALayer里面与CAMediaTimming相关的方法,改变速度,纪录时间,做到的。代码如下:
//暂停动画
- (void)pauseAnimation
{
CALayer *layer = self.layer;
layer.fillMode = kCAFillModeForwards;
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
//继续动画
- (void)resumeAnimation
{
CALayer*layer = self.layer;
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
停止和开始就不详述了。
下面说视图的重用,我用了两个数组来做视图的重用,当有弹幕视图移出屏幕的时候我会把移出的视图保存在一个数组中。
在下一个视图制作的时候,直接设置取出来重新加载数据,而不是重新创建对象。
核心操作很简单:
if (_reuseableBulletArray.count)
{
view = [_reuseableBulletArray firstObject];
[view reloadDataWithDic:_bulletDic];
[_reuseableBulletArray removeObjectAtIndex:0];
}else
{
view = [[BulletView alloc] initWithCommentDic:_bulletDic];
}<span style="font-family: Arial, Helvetica, sans-serif;">[_bulletArray addObject:view];</span>
然后在合适的地方(一般是有弹幕视图移出屏幕的地方):
[_bulletArray removeObject:weakBulletView]; [_reuseableBulletArray addObject:weakBulletView];
这样就可以不用创建很多的对象了。
整体的一个demo在这里,希望大家用的开心:点击跳转到项目托管地址
相关文章推荐
- bilibili弹幕转ass程序制作思路及过程
- 实例解析如何在Android应用中实现弹幕动画效果
- 专家建议弹幕“实名制”,混A站B站的你们方不方?
- 八国联军字幕组----弹幕文化可能会发展的更大,在未来
- 在线弹幕xml文件转ass 附:在线转换其他文件格式的网站
- iOS 封装之 直播弹幕
- 弹幕小demo
- 网站实时弹幕
- Android_弹幕_效果_学习
- 【Android效果集】弹幕效果
- Android:简易弹幕效果实现
- 弹幕效果
- B站的DanmakuFlameMaster的使用
- HTML5演示碰撞及基本弹幕的实现
- android--------自定义弹幕控件(视频弹幕操作)
- 用原生js+HTML+CSS实现一个弹幕的效果
- 移动端页面弹幕小Demo实例说明
- 微信小程序开发之视频播放器 Video 弹幕 弹幕颜色自定义
- Android之---仿斗鱼直播弹屏效果的实现
- Android 自定义View——BarrageView实现弹幕功能