前言
气泡框作为社交聊天软件的基本功能,想必大家都了解怎么实现了。但是像国内巨头腾讯旗下的QQ气泡(尤其是动态的)我想应该是没有几个。这里就为大家提供一种类似QQ动态气泡的解决方案(还不完善)。
首先用到了一个第三方Lottie, airbnb做的一个专门做动画的东西。专门研究过动画的应该都知道或了解。简单讲就是通过插件将AE中画的动画转换成json文件,然后按照特定规则转换到页面上通过CABaseAnimation来实现。
效果预览
明确需求
- 基本的聊天气泡,随文本大小变更气泡大小并保证边界圆角等形状不变
- 动态气泡,气泡内容会能动态展示,并根据文本大小,部分动画(如位移)会有区别
lottie分析
一下是一段lottie的json文件,部分删减,源文件可在demo中查看,这里标注上重要字段
{
"v": "5.1.10",
"fr": 25,
"ip": 0,
"op": 750,
"w": 128,//画布宽
"h": 72,//画布高
"nm": "合成 1",
"ddd": 0,
"assets": [//使用的图片资源
{
"id": "image_0",
"w": 26,
"h": 19,
"u": "images/",
"p": "data:image/png;base64,"//图片内容,愿框架不支持网络地址,仅能检索本地文件与base64编码后的内容
}
],
"layers": [//layer层
{
"ddd": 0,
"ind": 1,
"ty": 2,
"nm": "星星.png",
"cl": "png",
"refId": "image_0",//使用图片资源id
"sr": 1,
"ks": {//位置,大小,缩放等信息
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 1,
"k": [
{//数组表示核心桢时的位置
"i": {
"x": 0.833,
"y": 0.833
},
"o": {
"x": 0.167,
"y": 0.167
},
"n": "0p833_0p833_0p167_0p167",
"t": 0,
"s": [//大小
21.375,
49.125,
0
],
"e": [//点位
20.5,
48.375,
0
],
"to": [//点位
-0.14583332836628,
-0.125,
0
],
"ti": [
0.14583332836628,
0.25,
0
]
},
{
"i": {
"x": 0.833,
"y": 0.833
},
"o": {
"x": 0.167,
"y": 0.167
},
"n": "0p833_0p833_0p167_0p167",
"t": 8,
"s": [
20.5,
48.375,
0
],
"e": [
20.5,
47.625,
0
],
"to": [
-0.14583332836628,
-0.25,
0
],
"ti": [
0,
0.14583332836628,
0
]
},
{
"t": 185
}
],
"ix": 2
},
"a": {
"a": 0,
"k": [
13,
9.5,
0
],
"ix": 1
},
"s": {
"a": 1,
"k": [
{//数组表示核心桢时的大小
"i": {
"x": [
0.833,
0.833,
0.833
],
"y": [
0.833,
0.833,
0.833
]
},
"o": {
"x": [
0.167,
0.167,
0.167
],
"y": [
0.167,
0.167,
0.167
]
},
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 19,
"s": [
71.154,
78.947,
100
],
"e": [
61.538,
76.316,
100
]
},
{
"i": {
"x": [
0.833,
0.833,
0.833
],
"y": [
0.833,
0.833,
0.833
]
},
"o": {
"x": [
0.167,
0.167,
0.167
],
"y": [
0.167,
0.167,
0.167
]
},
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 42,
"s": [
61.538,
76.316,
100
],
"e": [
79.808,
78.947,
100
]
},
{
"t": 66
}
],
"ix": 6
}
},
"ao": 0,
"ip": 0,
"op": 750,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 4,
"ty": 4,
"nm": "右下角",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {//图层位置
"a": 0,
"k": [
141.25,
55.875,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [//子层数组
{
"ty": "gr",
"it": [
{
"ty": "rc",
"d": 1,
"s": {//大小
"a": 0,
"k": [
24.875,
16.125
],
"ix": 2
},
"p": {//位置
"a": 0,
"k": [
0,
0
],
"ix": 3
},
"r": {
"a": 0,
"k": 20,
"ix": 4
},
"nm": "矩形路径 1",
"mn": "ADBE Vector Shape - Rect",
"hd": false
},
{
"ty": "st",
"c": {//颜色
"a": 0,
"k": [
0.901176542394,
0.699030438591,
0.699030438591,
1
],
"ix": 3
},
"o": {
"a": 0,
"k": 100,
"ix": 4
},
"w": {
"a": 0,
"k": 0,
"ix": 5
},
"lc": 1,
"lj": 1,
"ml": 4,
"nm": "描边 1",
"mn": "ADBE Vector Graphic - Stroke",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [
0.878431432387,
0.760784373564,
0.301960784314,
1
],
"ix": 4
},
"o": {
"a": 0,
"k": 100,
"ix": 5
},
"r": 1,
"nm": "填充 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
-38.562,
-9.438
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}
],
"nm": "矩形 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": 0,
"op": 750,
"st": 0,
"bm": 0
}
],
"markers": []//暂时没用到,也不推荐使用
}
简单讲,通常p之下的k表示位置,s下表示大小,c表示颜色等,另外位置信息一般表示图形中心点相对父层的位置而不是相对原始画布的位置,这一点要特别注意。大小有两种,使用图片的一般使用缩放比来表示大小,而普通图形则为具体尺寸。
首先如果不修改原始画布大小,则lottie会根据展示区域的大小自动缩放整个图形,那么相应的所有元素都会被缩放,所以我们想要的聊天气泡一定是要先修改画布大小的,修改完大小后内部的各个相对点位也都要做相应调整。
我们把气泡划分几个区域来分析
划分成如上9块区域,规定横向为x轴,纵向为y轴。其中左上角内容不需要做任何变更,即可保持原始状态,右上角区域需要修改x轴位置坐标即可,上边区域需要修改x轴位置及x轴大小,下面几块区域依此类推。我们需要制定以下规则来达成修改对应值的目的(a表示原始值,dif表示差值,rat表示比例值)。
- 差值相加
- 差值相减
- 比例相乘
- 半差相加
- 半差相减
- 图片比例s的修改 b为图片原始尺寸,由于比例值本身是乘以100的,所以这里计算也要乘100
参考lottie源码,该部分内容均由LOTKeyframe实现,而s和p等在json中是同级的,并且x,y,z轴是分开的,因此我们制定如下规则,采用6位16进制数表示(json文件中应转换为对应的十进制数值存储),前三位表示大小的修改(s),后三位表示未知的修改(p),三位分别对应 z y x。数值定义如下
16进制 前三位大小、后三位位置 zyx,运算方法与数值如上边规则所示。eg:0x012045->69649表示宽度(大小x轴)需要加上原始画布与结果画布的宽度差,高度(大小y轴)需要减去原始画布与结果画布的高度差,x轴位置需要加上原始画布与结果画布的宽度差的一般,y轴位置需要减去原始画布与结果画布的高度差的一般。
使用说明
由于lottie不支持网络地址图片加载,需要调用内部方法等问题,所以对lottie原始框架内做了部分更改,其他均为扩展或子类实现。建议将demo中的LottieExtension(自定内容)和lottie-ios(原始框架)直接放入项目中使用。使用时只需使用RAAnimationView替换气泡图层即可。
修改lottie文件摘要如下:
- LOTAnimationView.h
由于lottie对AE插件导出后的图片资源是不在json中的,所以做了一个简易的mac程序来自动识别并导入图片数据(默认为base64方式)参见changeBubbleJson
注意,这个mac程序并不能实现自动添加计算方法的参数,因此需要用户自行修改json,在合适的位置添加pty以实现对应图层的位置大小修改,在最外层添加raty以保证修改画布原始尺寸。
本文由博客群发一文多发等运营工具平台 OpenWrite 发布