一種動(dòng)畫框架Lottie的解析(二)—— 基本介紹(二)

版本記錄

版本號(hào) 時(shí)間
V1.0 2017.09.15

前言

app中好的炫的動(dòng)畫可以讓用戶耳目一新,為產(chǎn)品增色不少,關(guān)于動(dòng)畫的實(shí)現(xiàn)我們可以用基本動(dòng)畫、關(guān)鍵幀動(dòng)畫、序列幀動(dòng)畫以及基于CoreGraphic的動(dòng)畫等等,接下來(lái)這幾篇我不介紹系統(tǒng)給的這幾種動(dòng)畫繪制方法,給大家介紹的是一種動(dòng)畫框架。感興趣的可以看我上面幾篇。
1. 一種動(dòng)畫框架Lottie的解析(一)—— 基本介紹(一)

iOS View Controller Transitioning - iOS轉(zhuǎn)場(chǎng)動(dòng)畫

Lottie帶有一個(gè)UIViewController動(dòng)畫控制器,用于定制自定義viewController轉(zhuǎn)場(chǎng)動(dòng)畫!

稱為轉(zhuǎn)場(chǎng)的代理

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

LOTAnimationTransitionController中實(shí)現(xiàn)代理方法。

#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;
}

通過(guò)將applyAnimationTransform設(shè)置為YES,您可以做Lottie動(dòng)畫移向和移出控制器。 它們將位于層的原點(diǎn)。 當(dāng)設(shè)置為NO時(shí),Lottie只需對(duì)指定的層進(jìn)行遮擋,同時(shí)重新檢測(cè)z順序。


Debugging - 調(diào)試

Lottie有幾個(gè)調(diào)試功能可以了解。 當(dāng)加載動(dòng)畫時(shí),不支持的功能將在控制臺(tái)中以其功能名稱打印輸出。

如果你檢查LOTHelpers.h,你會(huì)看到兩個(gè)調(diào)試標(biāo)志。 ENABLE_DEBUG_LOGGINGENABLE_DEBUG_SHAPES

ENABLE_DEBUG_LOGGING增加了Lottie Logging的詳細(xì)程度。 它在動(dòng)畫過(guò)程中隨時(shí)記錄動(dòng)畫節(jié)點(diǎn)。 如果您的動(dòng)畫不工作,請(qǐng)打開并播放動(dòng)畫。 控制臺(tái)日志可能會(huì)給你一些關(guān)于發(fā)生了什么的線索。

ENABLE_DEBUG_SHAPES為每個(gè)圖層和形狀的錨點(diǎn)繪制一個(gè)彩色方塊。 這有助于查看屏幕上是否有任何內(nèi)容。

1. keyPath

LOTAnimationView提供 - (void)logHierarchyKeypaths,它會(huì)遞歸地記錄動(dòng)畫的所有可設(shè)置的關(guān)鍵字。 這有助于在運(yùn)行時(shí)更改動(dòng)畫。


Adding Views to an Animation at Runtime - 在運(yùn)行時(shí)將視圖添加到動(dòng)畫

Lautie不僅可以在運(yùn)行時(shí)更改動(dòng)畫,還可以在運(yùn)行時(shí)將自定義UI添加到LOTAnimation。 下面的例子顯示了一些高級(jí)的用法來(lái)創(chuàng)建動(dòng)態(tài)圖像加載器。


A Dynamic Image Loading Spinner - 一種動(dòng)態(tài)圖像加載轉(zhuǎn)動(dòng)動(dòng)畫

上面的示例顯示了使用加載旋轉(zhuǎn)動(dòng)畫設(shè)置的單個(gè)LOTAnimationView。 加載旋轉(zhuǎn)循環(huán)其動(dòng)畫的一部分,而此時(shí)圖像被異步下載。 當(dāng)下載完成后,將圖像添加到動(dòng)畫中,其余的動(dòng)畫將無(wú)縫播放。 圖像干凈地動(dòng)畫化,完成塊被調(diào)用。

如果說(shuō),動(dòng)畫已被設(shè)計(jì)師改變,需要更新。 所有這些都是更新捆綁包中的JSON文件。 不需要更改代碼!

在這里,設(shè)計(jì)決定為app添加“黑暗模式”。 只是幾行代碼在運(yùn)行時(shí)更改動(dòng)畫的顏色。

具體代碼如下所示:

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 - 在運(yùn)行時(shí)更改動(dòng)畫

Lottie可以做的不僅僅是播放美麗的動(dòng)畫。 Lottie允許您在運(yùn)行時(shí)更改動(dòng)畫。

下面看一下例子,包含了四個(gè)開關(guān)。

下面看實(shí)現(xiàn)代碼

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()

下面接著我們更改開關(guān)的顏色

下面看實(shí)現(xiàn)代碼

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中圖層和屬性名稱的點(diǎn)分隔路徑。 LOTAnimationView提供- (void)logHierarchyKeypaths,它會(huì)遞歸地記錄動(dòng)畫的所有可設(shè)置的關(guān)鍵字。

現(xiàn)在讓我們改變一些屬性。

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中可動(dòng)畫的任何屬性。 如果關(guān)鍵幀不存在,則為您創(chuàng)建一個(gè)線性關(guān)鍵幀。 如果關(guān)鍵幀確實(shí)存在,那么只是其數(shù)據(jù)被替換。


Animated Controls and Switches - 動(dòng)畫控制和開關(guān)

Lottie還擁有UIControl的自定義子類,用于創(chuàng)建自定義的動(dòng)畫交互式控件。 目前,Lottie擁有LOTAnimatedSwitch,它是一種切換式開關(guān)控制。 開關(guān)開關(guān)播放開啟或關(guān)閉動(dòng)畫,并向所有目標(biāo)發(fā)送UIControlStateValueChanged廣播。 與使用UISwitch的方式相同,使用幾個(gè)附加功能來(lái)設(shè)置Lottie的動(dòng)畫。

您可以使用方便方法或直接提供動(dòng)畫來(lái)初始化開關(guān)。

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

您還可以為開機(jī)和關(guān)閉動(dòng)畫指定動(dòng)畫時(shí)間軸的特定部分。 默認(rèn)情況下,LOTAnimatedSwitch將向前開始播放動(dòng)畫,然后向后結(jié)束播放動(dòng)畫。

讓我們說(shuō),提供的動(dòng)畫從0.5-1開啟和從0-0.5關(guān)閉動(dòng)畫。

/// 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都增加了狀態(tài)更改外觀更改的支持。 這需要After Effects中的一些設(shè)置。 Lottie將根據(jù)控件狀態(tài)切換可見(jiàn)的動(dòng)畫圖層。 這可以用于具有DisabledselectedHighlighted狀態(tài)。 這些狀態(tài)與After Effects中的圖層名稱相關(guān)聯(lián),并作為控件更改狀態(tài)動(dòng)態(tài)顯示。

假設(shè)我們有一個(gè)具有Normal和Disable狀態(tài)的開關(guān)。 在效果中,我們有一個(gè)組合包含常規(guī)“按鈕”和禁用的“ Disable”狀態(tài)的Precomps。 他們有不同的視覺(jué)風(fēng)格。

現(xiàn)在在代碼中,我們可以將UIControlState與這些層相關(guān)聯(lián)。

// 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 - 關(guān)鍵幀插值

  • Linear Interpolation - 線性插值
  • Bezier Interpolation - 貝塞爾插值
  • Hold Interpolation - 保持插值
  • Rove Across Time - 漫長(zhǎng)的時(shí)間
  • Spatial Bezier - 空間貝塞爾

2. Solids

  • Transform Anchor Point - 改變錨點(diǎn)
  • Transform Position - 轉(zhuǎn)變位置
  • Transform Scale - 改變尺寸
  • Transform Rotation - 改變旋轉(zhuǎn)
  • 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) - 組變換(錨點(diǎn),位置,尺度等)
  • Rectangle (All properties) - 矩形(所有屬性)
  • Eclipse (All properties) - 橢圓(所有屬性)
  • paths in one group - 一組中有多條路徑
  • Even-Odd winding paths - 基偶繞數(shù)路徑
  • Reverse Fill Rule - 反向填充規(guī)則

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

可替代方案

  • 手工制作動(dòng)畫。 手工制作動(dòng)畫是Android和iOS設(shè)計(jì)工程需要巨大的時(shí)間。 花費(fèi)太多時(shí)間來(lái)制作動(dòng)畫通常很難甚至不可能。
  • Facebook Keyframes,關(guān)鍵幀是Facebook構(gòu)建的一個(gè)非常好的新庫(kù)。 然而,關(guān)鍵幀不支持一些Lottie的功能,如遮罩,修剪路徑等等。
  • GIF。Gif的體積大小是bodymovin JSON的兩倍,并且呈現(xiàn)為固定大小,無(wú)法按比例擴(kuò)大以匹配大型和高密度屏幕。
  • Png序列。 Png序列甚至比gif更差,因?yàn)樗鼈兊奈募笮⊥ǔJ?code>bodymovin json的大小的30-50倍,也不能放大。

后記

未完,待續(xù)~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容