一种动画框架Lottie的解析(二)—— 基本介绍(二)

版本记录

版本号 时间
V1.0 2017.09.15

前言

app中好的炫的动画可以让用户耳目一新,为产品增色不少,关于动画的实现我们可以用基本动画、关键帧动画、序列帧动画以及基于CoreGraphic的动画等等,接下来这几篇我不介绍系统给的这几种动画绘制方法,给大家介绍的是一种动画框架。感兴趣的可以看我上面几篇。
1. 一种动画框架Lottie的解析(一)—— 基本介绍(一)

iOS View Controller Transitioning - iOS转场动画

Lottie带有一个UIViewController动画控制器,用于定制自定义viewController转场动画!

称为转场的代理

- (void)_showTransitionA 
{
  ToAnimationViewController *vc = [[ToAnimationViewController alloc] init];
  vc.transitioningDelegate = self;
  [self presentViewController:vc animated:YES completion:NULL];
}

LOTAnimationTransitionController中实现代理方法。

#pragma mark -- View Controller Transitioning

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source 
{
  LOTAnimationTransitionController *animationController = [[LOTAnimationTransitionController alloc] initWithAnimationNamed:@"vcTransition1" fromLayerNamed:@"outLayer" toLayerNamed:@"inLayer" applyAnimationTransform:NO];
  return animationController;
}

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed 
{
  LOTAnimationTransitionController *animationController = [[LOTAnimationTransitionController alloc] initWithAnimationNamed:@"vcTransition2" fromLayerNamed:@"outLayer" toLayerNamed:@"inLayer" applyAnimationTransform:NO];
  return animationController;
}

通过将applyAnimationTransform设置为YES,您可以做Lottie动画移向和移出控制器。 它们将位于层的原点。 当设置为NO时,Lottie只需对指定的层进行遮挡,同时重新检测z顺序。


Debugging - 调试

Lottie有几个调试功能可以了解。 当加载动画时,不支持的功能将在控制台中以其功能名称打印输出。

如果你检查LOTHelpers.h,你会看到两个调试标志。 ENABLE_DEBUG_LOGGINGENABLE_DEBUG_SHAPES

ENABLE_DEBUG_LOGGING增加了Lottie Logging的详细程度。 它在动画过程中随时记录动画节点。 如果您的动画不工作,请打开并播放动画。 控制台日志可能会给你一些关于发生了什么的线索。

ENABLE_DEBUG_SHAPES为每个图层和形状的锚点绘制一个彩色方块。 这有助于查看屏幕上是否有任何内容。

1. keyPath

LOTAnimationView提供 - (void)logHierarchyKeypaths,它会递归地记录动画的所有可设置的关键字。 这有助于在运行时更改动画。


Adding Views to an Animation at Runtime - 在运行时将视图添加到动画

Lautie不仅可以在运行时更改动画,还可以在运行时将自定义UI添加到LOTAnimation。 下面的例子显示了一些高级的用法来创建动态图像加载器。


A Dynamic Image Loading Spinner - 一种动态图像加载转动动画

上面的示例显示了使用加载旋转动画设置的单个LOTAnimationView。 加载旋转循环其动画的一部分,而此时图像被异步下载。 当下载完成后,将图像添加到动画中,其余的动画将无缝播放。 图像干净地动画化,完成块被调用。

如果说,动画已被设计师改变,需要更新。 所有这些都是更新捆绑包中的JSON文件。 不需要更改代码!

在这里,设计决定为app添加“黑暗模式”。 只是几行代码在运行时更改动画的颜色。

具体代码如下所示:

import UIKit
import Lottie

class ViewController: UIViewController {
  
  var animationView: LOTAnimationView = LOTAnimationView(name: "SpinnerSpin");
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    // Setup our animaiton view
    animationView.contentMode = .scaleAspectFill
    animationView.frame = CGRect(x: 20, y: 20, width: 200, height: 200)

    self.view.addSubview(animationView)
    // Lets change some of the properties of the animation
    // We arent going to use the MaskLayer, so lets just hide it
    animationView.setValue(0, forKeypath: "MaskLayer.Ellipse 1.Transform.Opacity", atFrame: 0)
    // All of the strokes and fills are white, lets make them DarkGrey
    animationView.setValue(UIColor.darkGray, forKeypath: "OuterRing.Stroke.Color", atFrame: 0)
    animationView.setValue(UIColor.darkGray, forKeypath: "InnerRing.Stroke.Color", atFrame: 0)
    animationView.setValue(UIColor.darkGray, forKeypath: "InnerRing.Fill.Color", atFrame: 0)
    
    // Lets turn looping on, since we want it to repeat while the image is 'Downloading'
    animationView.loopAnimation = true
    // Now play from 0 to 0.5 progress and loop indefinitely.
    animationView.play(fromProgress: 0, toProgress: 0.5, withCompletion: nil)
    
    // Lets simulate a download that finishes in 4 seconds.
    let dispatchTime = DispatchTime.now() + 4.0
    DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
      self.simulateImageDownloaded()
    }
  }
  
  func simulateImageDownloaded() {
    // Our downloaded image
    let image = UIImage(named: "avatar.jpg")
    let imageView = UIImageView(image: image)

    // We want the image to show up centered in the animation view at 150Px150P
    // Convert that rect to the animations coordinate space
    // The origin is set to -75, -75 because the origin is centered in the animation view
    let imageRect = animationView.convert(CGRect(x: -75, y: -75, width: 150, height: 150), toLayerNamed: nil)
    
    // Setup our image view with the rect and add rounded corners
    imageView.frame = imageRect
    imageView.layer.masksToBounds = true
    imageView.layer.cornerRadius = imageRect.width / 2;
    
    // Now we set the completion block on the currently running animation
    animationView.completionBlock = { (result: Bool) in ()
      // Add the image view to the layer named "TransformLayer"
      self.animationView.addSubview(imageView, toLayerNamed: "TransformLayer", applyTransform: true)
      // Now play the last half of the animation
      self.animationView.play(fromProgress: 0.5, toProgress: 1, withCompletion: { (complete: Bool) in
        // Now the animation has finished and our image is displayed on screen
        print("Image Downloaded and Displayed")
      })
    }
    
    // Turn looping off. Once the current loop finishes the animation will stop 
    // and the completion block will be called.
    animationView.loopAnimation = false
  }
  
}

Changing Animations At Runtime - 在运行时更改动画

Lottie可以做的不仅仅是播放美丽的动画。 Lottie允许您在运行时更改动画。

下面看一下例子,包含了四个开关。

下面看实现代码

let animationView = LOTAnimationView(name: "toggle");
self.view.addSubview(animationView)
animationView.frame.origin.x = 40
animationView.frame.origin.y = 20
animationView.autoReverseAnimation = true
animationView.loopAnimation = true
animationView.play()

let animationView2 = LOTAnimationView(name: "toggle");
self.view.addSubview(animationView2)
animationView2.frame.origin.x = 40
animationView2.frame.origin.y = animationView.frame.maxY + 4
animationView2.autoReverseAnimation = true
animationView2.loopAnimation = true
animationView2.play()

let animationView3 = LOTAnimationView(name: "toggle");
self.view.addSubview(animationView3)
animationView3.frame.origin.x = 40
animationView3.frame.origin.y = animationView2.frame.maxY + 4
animationView3.autoReverseAnimation = true
animationView3.loopAnimation = true
animationView3.play()

let animationView4 = LOTAnimationView(name: "toggle");
self.view.addSubview(animationView4)
animationView4.frame.origin.x = 40
animationView4.frame.origin.y = animationView3.frame.maxY + 4
animationView4.autoReverseAnimation = true
animationView4.loopAnimation = true
animationView4.play()

下面接着我们更改开关的颜色

下面看实现代码

animationView2.setValue(UIColor.green, forKeypath: "BG-On.Group 1.Fill 1.Color", atFrame: 0)
animationView3.setValue(UIColor.red, forKeypath: "BG-On.Group 1.Fill 1.Color", atFrame: 0)
animationView4.setValue(UIColor.orange, forKeypath: "BG-On.Group 1.Fill 1.Color", atFrame: 0)
[animationView2 setValue:[UIColor greenColor] forKeypath:@"BG-On.Group 1.Fill 1.Color" atFrame:@0];

keyPathAfter Effects中图层和属性名称的点分隔路径。 LOTAnimationView提供- (void)logHierarchyKeypaths,它会递归地记录动画的所有可设置的关键字。

现在让我们改变一些属性。

animationView2.setValue(UIColor.green, forKeypath: "BG-On.Group 1.Fill 1.Color", atFrame: 0)
animationView2.setValue(UIColor.red, forKeypath: "BG-Off.Group 1.Fill 1.Color", atFrame: 0)

Lottie允许您更改After Effects中可动画的任何属性。 如果关键帧不存在,则为您创建一个线性关键帧。 如果关键帧确实存在,那么只是其数据被替换。


Animated Controls and Switches - 动画控制和开关

Lottie还拥有UIControl的自定义子类,用于创建自定义的动画交互式控件。 目前,Lottie拥有LOTAnimatedSwitch,它是一种切换式开关控制。 开关开关播放开启或关闭动画,并向所有目标发送UIControlStateValueChanged广播。 与使用UISwitch的方式相同,使用几个附加功能来设置Lottie的动画。

您可以使用方便方法或直接提供动画来初始化开关。

// Convenience
LOTAnimatedSwitch *toggle1 = [LOTAnimatedSwitch switchNamed:@"Switch"];
 
// Manually 
LOTComposition *comp = [LOTComposition animationNamed:@"Switch"];
LOTAnimatedSwitch *toggle1 = [[LOTAnimatedSwitch alloc] initWithFrame:CGRectZero];
[toggle1 setAnimationComp:comp];

您还可以为开机和关闭动画指定动画时间轴的特定部分。 默认情况下,LOTAnimatedSwitch将向前开始播放动画,然后向后结束播放动画。

让我们说,提供的动画从0.5-1开启和从0-0.5关闭动画。

/// On animation is 0.5 to 1 progress.
[toggle1 setProgressRangeForOnState:0.5 toProgress:1];

/// Off animation is 0 to 0.5 progress.
[toggle1 setProgressRangeForOffState:0 toProgress:0.5];

此外,所有LOTAnimatedControls都增加了状态更改外观更改的支持。 这需要After Effects中的一些设置。 Lottie将根据控件状态切换可见的动画图层。 这可以用于具有DisabledselectedHighlighted状态。 这些状态与After Effects中的图层名称相关联,并作为控件更改状态动态显示。

假设我们有一个具有Normal和Disable状态的开关。 在效果中,我们有一个组合包含常规“按钮”和禁用的“ Disable”状态的Precomps。 他们有不同的视觉风格。

现在在代码中,我们可以将UIControlState与这些层相关联。

// Specify the layer names for different states
[statefulSwitch setLayerName:@"Button" forState:UIControlStateNormal];
[statefulSwitch setLayerName:@"Disabled" forState:UIControlStateDisabled];

// Changes visual appearance by switching animation layer to "Disabled"
statefulSwitch.enabled = NO;

// Changes visual appearance by switching animation layer to "Button"
statefulSwitch.enabled = YES;

Supported After Effects Features - 支持After Effects功能

1. Keyframe Interpolation - 关键帧插值

  • Linear Interpolation - 线性插值
  • Bezier Interpolation - 贝塞尔插值
  • Hold Interpolation - 保持插值
  • Rove Across Time - 漫长的时间
  • Spatial Bezier - 空间贝塞尔

2. Solids

  • Transform Anchor Point - 改变锚点
  • Transform Position - 转变位置
  • Transform Scale - 改变尺寸
  • Transform Rotation - 改变旋转
  • Transform Opacity - 改变不透明度

3. Masks - 遮罩

  • Path - 路径
  • Opacity - 不透明度
  • Multiple Masks (additive, subtractive and intersection) - 多重掩模(加法,减法和交叉)

4. Track Mattes

  • Alpha Matte

5. Parenting

  • Multiple Parenting
  • Nulls

6. Shape Layers

  • Anchor Point
  • Position
  • Scale
  • Rotation
  • Opacity
  • Path
  • Group Transforms (Anchor point, position, scale etc) - 组变换(锚点,位置,尺度等)
  • Rectangle (All properties) - 矩形(所有属性)
  • Eclipse (All properties) - 椭圆(所有属性)
  • paths in one group - 一组中有多条路径
  • Even-Odd winding paths - 基偶绕数路径
  • Reverse Fill Rule - 反向填充规则

7. Stroke (shape layer)

  • Stroke Color
  • Stroke Opacity
  • Stroke Width
  • Line Cap
  • Dashes (Now Animated!)

8. Fill (shape layer)

  • Fill Color
  • Fill Opacity

9. Trim Paths (shape layer)

  • Trim Paths Start
  • Trim Paths End
  • Trim Paths Offset

10. Repeaters

  • Supports repeater transforms
  • Offset currently not supported

11. Gradients

  • Support for Linear Gradients
  • Support for Radial Gradients

12. Polystar and Polygon

  • Supported! Theres a known bug if the roundness is greater than 100 percent

13. Layer Features

  • Precomps
  • Image Layers
  • Shape Layers
  • Null Layers
  • Solid Layers
  • Parenting Layers
  • Alpha Matte Layers

14. Currently Unsupported After Effects Features

  • Merge Shapes
  • Alpha Inverted Masks
  • Trim Shapes Individually feature of Trim Paths
  • Expressions
  • 3d Layer support
  • Time remapping / Layer Reverse
  • Layer Blend Modes
  • Layer Effects

可替代方案

  • 手工制作动画。 手工制作动画是Android和iOS设计工程需要巨大的时间。 花费太多时间来制作动画通常很难甚至不可能。
  • Facebook Keyframes,关键帧是Facebook构建的一个非常好的新库。 然而,关键帧不支持一些Lottie的功能,如遮罩,修剪路径等等。
  • GIF。Gif的体积大小是bodymovin JSON的两倍,并且呈现为固定大小,无法按比例扩大以匹配大型和高密度屏幕。
  • Png序列。 Png序列甚至比gif更差,因为它们的文件大小通常是bodymovin json的大小的30-50倍,也不能放大。

后记

未完,待续~~

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

推荐阅读更多精彩内容