版本记录
版本号 | 时间 |
---|---|
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)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
LOTAnimationTransitionController *animationController = [[LOTAnimationTransitionController alloc] initWithAnimationNamed:@"vcTransition1" fromLayerNamed:@"outLayer" toLayerNamed:@"inLayer" applyAnimationTransform:NO];
return animationController;
}
- (id)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_LOGGING
和ENABLE_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];
keyPath
是After 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将根据控件状态切换可见的动画图层。 这可以用于具有Disabled
,selected
或Highlighted
状态。 这些状态与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
倍,也不能放大。
后记
未完,待续~~