如何构造酷炫的物理效果和过场动画效果 - Lottie


Lottie(官网) 是 Airbnb 开源的一个动画框架。Lottie这个名字来自于一名德国导演洛特·赖尼格尔(Lotte Reiniger),她最著名的电影叫作“阿赫迈德王子历险记(The Adventures of Prince Achmed)”。这个框架和其他动画框架不太一样,动画的编写和维护将由动画设计师完成,完全无需开发者操心。

动画设计师做好动画以后,可以使用After Effects将动画导出JSON文件,然后由Lottie加载和渲染这个JSON文件,并转换成对应的动画代码。由于是JSON格式,文件也会很小,可以减少App包大小。运行时还可以通过代码控制更改动画,比如更改颜色、位置以及任何关键值。另外,Lottie还支持页面切换的过场动画(UIViewController Transitions)。

上图的引导图,就是Lottie的加载图效果,其动画就是由动画设计师使用After Effects 创作,然后使用Bodymovin 进行导出的,开发者完全不用做什么额外的代码工作,就能够使用原生方式将其渲染出来。

bodymovin是Hernan Torrisi 做的一个 After Effects的插件,起初导出的JSON文件只是通过JavaScript在网页中进行动画的播放,后来才将JSON文件的解析渲染应用到了其他平台上。

Bodymovin

先去Adobe官网下载Bodymovin插件(如果找不到相关加载则进入这个百度盘去下载AE 插件 & Bodymovin 百度云盘下载地址  密码:zdsh),并在After Effects中安装。使用After Effects制作完动画后,选择Window菜单,找到Extensions的Bodymovin项,在菜单中选择Render按钮就可以输出JSON文件了。

Lottie Files网站还是一个动画设计师分享作品的平台,每个动画效果的JSON文件都可以下载使用。所以,如果你现在没有动画设计师配合的话,可以到这个网站去查找并下载一个Bodymovin生成的JSON文件,然后运用到工程中去试试效果。

在iOS中使用Lottie

在iOS开发中使用Lottie也很简单,只要集成Lottie框架,然后在程序中通过Lottie的接口控制After Effects 生成的动画JSON就行了。

首先,你可以通过CocoaPods集成Lottie框架到你工程中。Lottie iOS框架的GitHub地址是https://github.com/airbnb/lottie-ios,官方也提供了demo

然后,快速读取一个由 Bodymovin 生成的 JSON 文件进行播放。具体代码:

let animationTestView = AnimationView()                                                                             let animationTest = Animation.named("loader2")                                                                 animationTestView.animation = animationTest                                                view.addSubview(animationTestView)                                                                              animationTestView.play()


利用Lottie的动画进度控制能力,还可以完成手势与动效同步的问题。动画进度控制是:

animationTestView.play(fromProgress: 0,

toProgress : 1,

loopMode: LottieLoopMode.playOnce,

completion: {(finished)  in

if finished {

print("Animation Complete")

} else {

print("Animation cancelled")

}

})



Lottie还带有一个UIViewController  animation-controller,可以自定义页面切换的过场动画,等。

Lottie在运行期间提供接口和协议来更改动画,有动画数据搜索接口LOTKeyPath,以及设置动画数据的协议LOTValueDelegate。详细的说明和使用实例代码,可以参照官方iOS教程

多平台支持

Lottie支持多平台,除了支持iOS,还支持Android,React Native 和Flutter。除了官方维护的这些平台外,Lottie还支持Windows,Qt,Skia。陈卿还实现了Rect、Vue和Angular对Lottie的支持,并已将代码放到GitHub上。

Lottie 实现原理

实际上,Lottie iOS在iOS内做的事情就是将After Effects编辑的动画内容,通过JSON文件这个中间媒介,一一映射到iOS的LayerModel、Keyframe、ShapeItem、DashElement、Marker、Mask、Transform这些类的属性中并保存下来,接下来再通过CoreAnimation进行渲染。这就和你手动写动画代码的实现是一样的,只不过这个过程的精准描述,全部由动画设计师通过JSON文件输入进来。

Lottie iOS使用系统自带的Codable协议来解析JSON文件,这样就可以享受系统升级带来性能提升的便利,比如ShapeItem这个类设计如下:

//Shape Layer

class ShapeItem:Codable {

/// shape 的名字

let name: String

///shape的类型

let type: ShapeType

//和json 中字符映射

private enum CodingKeys : string, CodingKey {

case name = "nm"

case type = "ty"

}

//初始化

required init(from decoder:Decoder)throws {

let container = try decoder.container(keyedBy:shapeItem.CodingKeys.self)

self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Layer"

self.type = try container.decode(ShapeType.self,forKey: .type)

}

}


通过上面代码可以看出,ShapeItem有两个属性,映射到JSON的字符键值是nm和ty,分别代表shape的名字和类型。下面,我们再一起看一段Bodymovin生成的JSON代码:

{"ty":"st","fillEnabled":true,"c":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0,0.65,0.6,1],"e":[0.76,0.76,0.76,1]},{"t":36}]},"o":{"k":100},"w":{"k":3},"lc":2,"lj":2,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"}


在这段JSON代码中,nm键对应的值是Stroke 1,ty键对应的值是st。那我们再来看看,st是什么类型。

我们知道,ShapeType是个枚举类型,它的定义如下:

enum ShapeType:String, Codable {

case ellipse = "el"

case fill = "fl"

case gradientFill = "gf"

case group = "gr"

case gradientStroke = "gs"

case merge = "mm"

case rectangle = "rc"

case repeater = "rp"

case round = "rd"

case shape = "sh"

case star = "sr'

case stroke = "st"

case trim =  "tm"

case transform = "tr"

}

通过上面的枚举定义,可以看到st对应的是stroke类型。


Lottie就是通过这种方式,定义了一系列的类结构,可以将JSON数据全部映射过来。所有映射用的类都放在Lottie的Model目录下。使用CoreAnimation渲染的相关代码都在NodeRenderSystem目录下,比如前面举例的Stoke。

在渲染前会生成一个节点,实现StrokeNode.swift里,然后对StokeNode这个节点渲染的逻辑在StrokeRenderer.swift里。核心代码如下:

//设置 Context

func setuoForStroke(_ inContext: CGContext) {

inContext.setLineWidth(width) //行宽

inContext.setMiterLimit(miterLimit)

inContext.setLineCap(lineCap.cgLineCap) // 行间隔

inContext.setLineJoin(lineJoin.cgLineJoin)  //设置线条样式

if let dashPhase = dashPhase, let lengths = dashLengths {

inContext.setLineDash(phase: dashPhase, lengths: lengths)

} else {

inContext.setLineDash(phase: 0, lengths: [])

}

}

//渲染

func render (_ inContext : CGContext) {

guard inContext.path != nil && inContext.path !. isEmpty == false  else {

return 

}

guard let color = color else  {  return  }

hasUpdate = false

setupForStroke(inContext)

inContext.setAlpha(opacity) //设置透明度

inContext.setStrokeColor(color)  //设置颜色

inContext.strokePath()

}

如果是手写动画,这些代码就需要不断重复地写。使用第三方库去写动画的话,也无非是多封装了一层,而属性的设置、动画时间的设置等,还是需要手动添加很多代码来完成的。

但是,使用Lottie后,就可以完全不需要管理这些代码,只需要在After Effects那设置属性,控制动画时间 就好,当然也可以在代码里面进行设置。

我写的一个 demo 可以进行相关的学习。如果喜欢,可以给颗星,谢谢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,423评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,147评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,019评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,443评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,535评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,798评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,941评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,704评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,152评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,494评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,629评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,295评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,901评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,978评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,333评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,499评论 2 348