版本記錄
版本號(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_LOGGING
和ENABLE_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];
keyPath
是After 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)畫圖層。 這可以用于具有Disabled
,selected
或Highlighted
狀態(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ù)~~