最近,读完今年的第三本书《大话移动APP测试 Android与iOS》,在读到陈晔前辈改变中国测试行业的决心时,内心无比激动,作为一名初生的开发人员,我可能还无法理解测试行业的本质,但他那份通过分享改变现状的决心我深感共鸣。在此为每一位愿分享愿奉献的朋友点个赞!

弹幕,国内流行于视频网站A站和B站。网上关于弹幕的实现方法有很多,目前Android平台已经有比较成熟的解决方案DanmakuFlameMaster 。而iOS平台尚无比较成熟的开源库,在借鉴DanmakuFlameMaster的实现思想后,特分享iOS平台弹幕解决方案HJDanmakuDemo。本文将介绍弹幕的大致实现原理。

看过DanmakuFlameMaster源码的朋友都知道,弹幕实现主要需要解决以下几个问题

  1. 弹幕绘制方式
  2. 弹幕时间控制
  3. 弹幕碰撞检测原理
  4. 弹幕暂停及恢复

本文主要从以上4个方面介绍弹幕的详细实现原理。首先是弹幕绘制方式,在DanmakuFlameMaster库中,它主要通过view的自定义draw一帧一帧的绘制来完成弹幕的显示,这种方式最大的问题在于性能以及动画的不流畅。弹幕流畅的前提要求每秒绘制的帧数在30帧以上,而移动设备性能千差万别,当同一时刻需要绘制大量弹幕的时候,对于低端设备就会出现卡帧不流畅的情况,这会大大降低用户的体验。因此,在本项目中放弃采用自定义绘制帧的方式,而是采用系统动画的方式来实现弹幕文本的滚动。

Objective-C

[UIView animateWithDuration:danmaku.remainTime delay:0 options:UIViewAnimationOptionCurveLinear animations:^{

danmaku.label.frame = CGRectMake(-danmaku.size.width, danmaku.py, danmaku.size.width, danmaku.size.height);

} completion:nil];

其次,就是弹幕时间的控制。由于采用系统动画的方式,所以不需要时刻计算每一个弹幕的显示时间以及其X坐标(假设弹幕横向滚动),我们需要做的就是在弹幕需要出现的时候创建它,然后设定弹幕存活的时间,剩余滚动动画交给系统负责,当然,弹幕剩余时间需要我们来更新。本项目中,创建了一个0.5s间隔的定时器,主要负责创建新的弹幕并更新已显示弹幕的剩余时间,也就是说0.5s执行一次计算,如果需要,可以将刷新间隔设置成5s或者更长。

Objective-C

_timer = [NSTimer timerWithTimeInterval:_frameInterval target:self selector:@selector(onTimeCount) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];

[_timer fire];

然后,就是弹幕碰撞检测的问题。碰撞检测主要难点在于检测横向滚动弹幕之间的碰撞,弹幕存活时间由其显示时间和存活长短决定,因此,弹幕之间是否碰撞只需检测开始和消失是否碰撞即可。

Objective-C

- (BOOL)checkIsWillHitWithWidth:(float)width DanmakuL:(DanmakuBaseModel *)danmakuL DanmakuR:(DanmakuBaseModel *)danmakuR

{

if (danmakuL.remainTime<=0) {

return NO;

}

if (danmakuL.px+danmakuL.size.width>danmakuR.px) {

return YES;

}

float minRemainTime = MIN(danmakuL.remainTime, danmakuR.remainTime);

float px1 = [danmakuL pxWithScreenWidth:width RemainTime:(danmakuL.remainTime-minRemainTime)];

float px2 = [danmakuR pxWithScreenWidth:width RemainTime:(danmakuR.remainTime-minRemainTime)];

if (px1+danmakuL.size.width>px2) {

最后,弹幕的暂停及恢复。由于弹幕滚动采用系统动画,所以在解决弹幕暂停前需要先了解系统动画的实现原理,有兴趣的朋友可以参考动画解释这篇文章,在此就不做过多介绍。暂停的基本原理就是通过view的presentationLayer获取对象的当前坐标并赋给其frame,然后移除layer动画,考虑到缓冲,可以在当前坐标基础上-1

Objective-C

- (void)pauseRenderer

{

for (DanmakuBaseModel *danmaku in _drawArray.objectEnumerator) {

CALayer *layer = danmaku.label.layer;

CGRect rect = danmaku.label.frame;

if (layer.presentationLayer) {

rect = ((CALayer *)layer.presentationLayer).frame;

rect.origin.x-=1;

}

danmaku.label.frame = rect;

[danmaku.label.layer removeAllAnimations];

有兴趣的童鞋可以下载HJDanmakuDemo查看具体使用方法,有什么疑问可以在后面留言。 如果你喜欢,希望能在github上为本demo点上一赞,感谢你的来访!

笔记-iOS弹幕(源码)实现原理解析的更多相关文章

  1. Spring笔记(3) - debug源码AOP原理解析

    案例 @EnableAspectJAutoProxy//开启基于注解的aop模式 @Configuration public class AOPConfig { //业务逻辑类加入容器中 @Bean ...

  2. 笔记-twisted源码-import reactor解析

    笔记-twisted源码-import reactor解析 1.      twisted源码解析-1 twisted reactor实现原理: 第一步: from twisted.internet ...

  3. Laravel学习笔记之Session源码解析(上)

    说明:本文主要通过学习Laravel的session源码学习Laravel是如何设计session的,将自己的学习心得分享出来,希望对别人有所帮助.Laravel在web middleware中定义了 ...

  4. memcached学习笔记——存储命令源码分析上篇

    原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...

  5. memcached学习笔记——存储命令源码分析下篇

    上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...

  6. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  7. jQuery源码:从原理到实战

    jQuery源码:从原理到实战 jQuery选择器对象 $(".my-class"); document.querySelectorAll*".my-class" ...

  8. ios源码-ios游戏源码-ios源码下载

    游戏源码   一款休闲类的音乐小游戏源码 该源码实现了一款休闲类的音乐小游戏源码,该游戏的源码很简单,而且游戏的玩法也很容易学会,只要我们点击视图中的grid,就可以 人气:2943运行环境:/Xco ...

  9. [转]【安卓笔记】AsyncTask源码剖析

    [转][安卓笔记]AsyncTask源码剖析 http://blog.csdn.net/chdjj/article/details/39122547 前言: 初学AsyncTask时,就想研究下它的实 ...

随机推荐

  1. Eclipse插件—Easy Explorer

    如何使用Eclipse插件—Easy Explorer Easy Explorer是一个Eclipse插件,主要用于快速浏览项目文件的目录,实用性比较强. 使用方法如下:  1.下载Easy Expl ...

  2. Qt读写二进制文件

    http://blog.csdn.net/mjlsuccess/article/details/22194653 http://www.cnblogs.com/weiweiqiao99/archive ...

  3. 你不知道的pogo pin连接器

    pogo pin连接器是一种带弹簧的探针式连接器,pogo pin连接器结构看起来非常简单,但其制造工艺要求极其的精细与复杂,从车床加工,电镀,组装等每道工序,如果没有一个有良好品质控制和完善的制造水 ...

  4. 【HDOJ】4426 Palindromic Substring

    综合性很强的一道题目,结合manacher,后缀数组,哈希,RMQ,二分可解.基本思路是通过manacher可以找到所有可能的回文串,哈希去重,后缀数组二分找数目.最后暴力求解.需要注意kth需要为_ ...

  5. POJ2528 线段树的区间操作

    首先应该对该[0,10000000]进行离散化 即先将点集进行排序,然后从小到大缩小其中的间距,使得最后点数不会超过2*n 然后就是线段树操作 只需进行染色,然后最后用nlgn进行一个个查询颜色记录即 ...

  6. 函数fsp_alloc_seg_inode

    从inode page中申请inode entry inode = fsp_alloc_seg_inode(space_header, mtr); /************************* ...

  7. (五)学习JavaScript之firstChild 属性

    参考:http://www.w3school.com.cn/jsref/prop_node_firstchild.asp 定义和用法 firstChild 属性返回指定节点的首个子节点,以 Node ...

  8. 如何将域中的AD数据导入SharePoint

    转:http://www.cnblogs.com/wallis0922/archive/2010/09/29/1838292.html 最近刚装好sharepoint2010,想要研究一下,第一件想做 ...

  9. 从linux启动到rootfs的挂载分析

    简单的来说,根文件系统包括虚拟根文件系统和真实根文件系统.在Kernel启动的初始阶段,首先去创建虚拟的根文件系统,接下来再去调用do_mount来加载真正的文件系统,并将根文件系统切换到真正的文件系 ...

  10. 负载均衡、LVS概述

    1. 负载均衡概述 负载均衡的基本思路是:在一个服务器集群中尽可能的平衡负载量.通常的做法是在服务器前端设置一个负载均衡器(一般是专门的硬件设备).然后负载均衡器将请求的连接路由到最空闲的可用服务器. ...