- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章ios弹幕高效加载实现方式实例代码由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
看直播的童鞋们应该会经常看到满屏幕的滚动弹幕,看到密密麻麻的弹幕第一印象就是怎么样高效加载来避免卡顿,弹幕组成部分包含用户头像、用户昵称、弹幕的内容、表情等,本文介绍的实现原理就是把这几部分绘制成一张图片,然后通过定时器移动弹幕图片,当图片不在屏幕范围内即销毁.
先看下效果 。
下面我会详细介绍下实现原理 。
1 .获取弹幕数据来源,因为我是模拟生成弹幕,弹幕的数据存放在工程里的plist文件中 。
emotions存放这条弹幕的表情,type表示是否是自己发的,text表示弹幕内容,username表示用户昵称。取出plist文件的数据并转换成model.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#pragma mark - 获取数据源
- (
void
)loaddata{
// 获取plist全路径
nsstring *filepath = [[nsbundle mainbundle] pathforresource:@
"barrage.plist"
oftype:nil];
// 从指定的路径中加载数据
nsarray *array = [nsarray arraywithcontentsoffile:filepath];
// 遍历数组
for
(nsdictionary *dict in array) {
// 字典转模型
bamodle *barragem = [bamodle barragewithdict:dict];
[self.danmus addobject:barragem];
}
}
|
2 .根据模型生成弹幕图片,通过点击屏幕生成模型,根据模型绘制图片.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#pragma mark - 触摸屏幕响应事件
- (
void
)touchesbegan:(nsset<uitouch *> *)touches withevent:(uievent *)event{
// 获得一个随机整数
nsinteger index = arc4random_uniform((u_int32_t)self.danmus.count);
// 获得一个随机模型
bamodle *danmu = self.danmus[index];
// 根据模型生成图片
baimage *image = [self.danmuview imagewithbarrage:danmu];
// 调整弹幕加载区域
image.x = self.view.bounds.size.width;
image.y = arc4random_uniform(self.danmuview.bounds.size.height - image.size.height);
// 把图片加到弹幕view上
[self.danmuview addimage:image];
}
|
下面是具体绘制弹幕图片过程,我先简单介绍下,首先在绘图之前要确定上下文的尺寸,相当于画板的大小,画板的长 = 头像的长 + 昵称的长 + 内容的长 + 表情的长 * 表情个数 + 间距。然后就是分别绘制背景图片,用户昵称,内容和表情,最后返回一张图片.
此处有两点需要注意:
1.由于头像是矩形,想显示成圆形,要先画一个圆,并设置超出圆形的部分要裁剪,再绘制头像.
2.由于上面设置超出圆形的部分要裁剪,那即将要绘制背景岂不是要被裁剪,所以在绘制圆形区域上一句执行了cgcontextsavegstate(ctx)表示复制了一份画板(上下文)存到栈里,在绘制背景图片之前执行cgcontextrestoregstate(ctx),表示用之前保存的画板替换当前的,因为之前保存的画板没有设置超出圆形区域要裁剪的需求,当然替换当前的画板,会把当前画板上的绘图也copy过去.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
#pragma mark - 绘制弹幕图片
- (baimage *)imagewithbarrage:(bamodle *)danmu{
// 开启绘图上下文
//
uifont *font = [uifont systemfontofsize:13];
// 头像
cgfloat iconh = 30;
cgfloat iconw = iconh;
// 间距
cgfloat marginx = 5;
// 表情的尺寸
cgfloat emotionw = 25;
cgfloat emotionh = emotionw;
// 计算用户名占据的区域
cgsize namesize = [danmu.username boundingrectwithsize:cgsizemake(maxfloat, maxfloat) options:nsstringdrawinguseslinefragmentorigin attributes:@{nsfontattributename:font} context:nil].size;
// 计算内容占据的区域
cgsize textsize = [danmu.text boundingrectwithsize:cgsizemake(maxfloat, maxfloat) options:nsstringdrawinguseslinefragmentorigin attributes:@{nsfontattributename:font} context:nil].size;
// 位图上下文的尺寸
cgfloat contenth = iconh;
cgfloat contentw = iconw + 4 * marginx + namesize.width + textsize.width + danmu.emotions.count * emotionh;
cgsize contextsize = cgsizemake(contentw, contenth);
uigraphicsbeginimagecontextwithoptions(contextsize, no, 0.0);
// 获得位图上下文
cgcontextref ctx = uigraphicsgetcurrentcontext();
// 将上下文保存到栈中
cgcontextsavegstate(ctx);
// 1.绘制圆形区域
cgrect iconframe = cgrectmake(0, 0, iconw, iconh);
// 绘制头像圆形
cgcontextaddellipseinrect(ctx, iconframe);
// 超出圆形的要裁剪
cgcontextclip(ctx);
// 2.绘制头像
uiimage *icon = danmu.type ? [uiimage imagenamed:@
"headimage_1"
]:[uiimage imagenamed:@
"headimage_2"
];
[icon drawinrect:iconframe];
// 将上下文出栈替换当前上下文
cgcontextrestoregstate(ctx);
// 3.绘制背景图片
cgfloat bgx = iconw + marginx;
cgfloat bgy = 0;
cgfloat bgw = contentw - bgx;
cgfloat bgh = contenth;
danmu.type ? [[uicolor orangecolor] set]:[[uicolor whitecolor] set];
[[uibezierpath bezierpathwithroundedrect:cgrectmake(bgx, bgy, bgw, bgh) cornerradius:20.0] fill];
// 4.绘制用户名
cgfloat namex = bgx + marginx;
cgfloat namey = (contenth - namesize.height) * 0.5;
[danmu.username drawatpoint:cgpointmake(namex, namey) withattributes:@{nsattachmentattributename:font,nsforegroundcolorattributename:danmu.type == no ? [uicolor orangecolor]:[uicolor blackcolor]}];
// 5.绘制内容
cgfloat textx = namex + namesize.width + marginx;
cgfloat texty = namey;
[danmu.text drawatpoint:cgpointmake(textx, texty) withattributes:@{nsattachmentattributename:font,nsforegroundcolorattributename:danmu.type == no ? [uicolor blackcolor]:[uicolor whitecolor]}];
// 6.绘制表情
__block cgfloat emotionx = textx + textsize.width;
cgfloat emotiony = (contenth - emotionh) * 0.5;
[danmu.emotions enumerateobjectsusingblock:^(nsstring *emotionname, nsuinteger idx,
bool
* _nonnull stop) {
// 加载表情图片
uiimage *emotion = [uiimage imagenamed:emotionname];
[emotion drawinrect:cgrectmake(emotionx, emotiony, emotionw, emotionh)];
// 修改emotionx
emotionx += emotionw;
}];
// 从位图上下文中获得绘制好的图片
uiimage *image = uigraphicsgetimagefromcurrentimagecontext();
return
[[baimage alloc] initwithcgimage:image.cgimage scale:[uiscreen mainscreen].scale orientation:uiimageorientationup];
}
|
3 .开启绘图定时器,回调方法是setneedsdisplay,这样就会执行- (void)drawrect:(cgrect)rect每次修改image.x(由于uiimage没有x、y属性,所以写了个类拓展baimage),滚动不在屏幕范围内的会销毁 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#pragma mark - 添加定时器
- (
void
)addtimer{
if
(self.link) {
return
;
}
// 每秒执行60次回调
cadisplaylink *link = [cadisplaylink displaylinkwithtarget:self selector:@selector(setneedsdisplay)];
// 将定时器添加到runloop
[link addtorunloop:[nsrunloop currentrunloop] formode:nsrunloopcommonmodes];
self.link = link;
}
#pragma mark - 绘制移动
- (
void
)drawrect:(cgrect)rect{
for
(baimage *image in self.imagearray) {
image.x -= 3;
// 绘制图片
[image drawatpoint:cgpointmake(image.x, image.y)];
// 判断图片是否超出屏幕
if
(image.x + image.size.width < 0) {
[self.deleteimagearray addobject:image];
}
}
// 移除超过屏幕的弹幕
for
(baimage *image in self.deleteimagearray) {
[self.imagearray removeobject:image];
}
[self.deleteimagearray removeallobjects];
}
|
最后附上代码地址:barragedemo.rar 。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
原文链接:http://www.jianshu.com/p/be53cbf4590c 。
最后此篇关于ios弹幕高效加载实现方式实例代码的文章就讲到这里了,如果你想了解更多关于ios弹幕高效加载实现方式实例代码的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想要显示正在加载的 .gif,直到所有内容都已加载,包括嵌入的 iframe。但是,目前加载 gif 会在除 iframe 之外的所有内容都已加载后消失。我怎样才能让它等到 iframe 也加载完毕
首先,这是我第一次接触 Angular。 我想要实现的是,我有一个通知列表,我必须以某种方式限制 limitTo,因此元素被限制为三个,在我单击按钮后,其余的应该加载。 我不明白该怎么做: 设置“ V
我正在尝试在我的设备上运行这个非常简单的应用程序(使用 map API V2),并且出于某种原因尝试使用 MapView 时: 使用 java 文件: public class MainMap e
我正在使用 Python 2.6、Excel 2007 Professional 和最新版本的 PyXLL。在 PyXLL 中加载具有 import scipy 抛出异常,模块未加载。有没有人能够在
我想做这个: 创建并打包原始游戏。然后我想根据原始游戏中的蓝图创建具有新网格/声音/动画和蓝图的其他 PAK 文件。原始游戏不应该知道有关其他网格/动画/等的任何信息。因此,我需要在原始游戏中使用 A
**摘要:**在java项目中经常会使用到配置文件,这里就介绍几种加载配置文件的方法。 本文分享自华为云社区《【Java】读取/加载 properties配置文件的几种方法》,作者:Copy工程师。
在 Groovy 脚本中是否可以执行条件导入语句? if (test){ import this.package.class } else { import that.package.
我正在使用 NVidia 视觉分析器(来自 CUDA 5.0 beta 版本的基于 eclipse 的版本)和 Fermi 板,我不了解其中两个性能指标: 全局加载/存储效率表示实际内存事务数与请求事
有没有办法在通过 routeProvider 加载特定 View 时清除 Angular JS 存储的历史记录? ? 我正在使用 Angular 创建一个公共(public)安装,并且历史会积累很多,
使用 Xcode 4.2,在我的应用程序中, View 加载由 segue 事件触发。 在 View Controller 中首先调用什么方法? -(void) viewWillAppear:(BOO
我在某些Django模型中使用JSONField,并希望将此数据从Oracle迁移到Postgres。 到目前为止,当使用Django的dumpdata和loaddata命令时,我仍然没有运气来保持J
创建 Nib 时,我需要创建两种类型:WindowNib 或 ViewNib。我看到的区别是,窗口 Nib 有一个窗口和一个 View 。 如何将 View Nib 加载到另一个窗口中?我是否必须创建
我想将多个env.variables转换为静态结构。 我可以手动进行: Env { is_development: env::var("IS_DEVELOPMENT")
正如我从一个测试用例中看到的:https://godbolt.org/z/K477q1 生成的程序集加载/存储原子松弛与普通变量相同:ldr 和 str 那么,宽松的原子变量和普通变量之间有什么区别吗
我有一个重定向到外部网站的按钮/链接,但是外部网站需要一些时间来加载。所以我想添加一个加载屏幕,以便外部页面在显示之前完全加载。我无法控制外部网站,并且外部网站具有同源策略,因此我无法在 iFrame
我正在尝试为我的应用程序开发一个Dockerfile,该文件在初始化后加载大量环境变量。不知何故,当我稍后执行以下命令时,这些变量是不可用的: docker exec -it container_na
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我刚刚遇到一个问题,我有一个带有一些不同选项的选择标签。 现在我想检查用户选择了哪些选项。 然后我想将一个新的 html 文件加载到该网站(取决于用户选中的选项)宽度 javascript,我该怎么做
我知道两种保存/加载应用程序设置的方法: 使用PersistentStore 使用文件系统(存储,因为 SDCard 是可选的) 我想知道您使用应用程序设置的做法是什么? 使用 PersistentS
我开始使用 Vulkan 时偶然发现了我的第一个问题。尝试创建调试报告回调时(验证层和调试扩展在我的英特尔 hd vulkan 驱动程序上可用,至少它是这么说的),它没有告诉我 vkCreateDeb
我是一名优秀的程序员,十分优秀!