Added support for layer in out, updated play mechanics

This commit is contained in:
Brandon Withrow
2016-07-21 16:49:06 -07:00
parent 3c9f1e27c8
commit af697b6fa0
22 changed files with 317 additions and 103 deletions

View File

@@ -83,6 +83,8 @@
62FE40E81D3FFBB400CA389D /* LARectShapeLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FE40E71D3FFBB400CA389D /* LARectShapeLayer.m */; };
62FE40EA1D401C0700CA389D /* scaleTest.json in Resources */ = {isa = PBXBuildFile; fileRef = 62FE40E91D401C0700CA389D /* scaleTest.json */; };
62FE40EC1D402C0200CA389D /* parentTest.json in Resources */ = {isa = PBXBuildFile; fileRef = 62FE40EB1D402C0200CA389D /* parentTest.json */; };
62FE41161D41526D00CA389D /* InOutAnimation.json in Resources */ = {isa = PBXBuildFile; fileRef = 62FE41151D41526D00CA389D /* InOutAnimation.json */; };
62FE41431D41862200CA389D /* LAAnimatableLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FE41421D41862200CA389D /* LAAnimatableLayer.m */; };
F5B4E946F7B28B4594824641 /* libPods-LotteAnimator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BC68632413866F85CEACB7EA /* libPods-LotteAnimator.a */; };
/* End PBXBuildFile section */
@@ -198,6 +200,9 @@
62FE40E71D3FFBB400CA389D /* LARectShapeLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LARectShapeLayer.m; sourceTree = "<group>"; };
62FE40E91D401C0700CA389D /* scaleTest.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = scaleTest.json; sourceTree = "<group>"; };
62FE40EB1D402C0200CA389D /* parentTest.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = parentTest.json; sourceTree = "<group>"; };
62FE41151D41526D00CA389D /* InOutAnimation.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InOutAnimation.json; sourceTree = "<group>"; };
62FE41411D41862200CA389D /* LAAnimatableLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAAnimatableLayer.h; sourceTree = "<group>"; };
62FE41421D41862200CA389D /* LAAnimatableLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAAnimatableLayer.m; sourceTree = "<group>"; };
AEE1A33E4CE2F6024DE7793E /* Pods-LotteAnimator.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LotteAnimator.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LotteAnimator/Pods-LotteAnimator.debug.xcconfig"; sourceTree = "<group>"; };
BC68632413866F85CEACB7EA /* libPods-LotteAnimator.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LotteAnimator.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -249,6 +254,8 @@
4804B2FB1C1F55E600DA8AF7 /* AppDelegate.m */,
48372A421C1F84D700AD0293 /* LACompView.h */,
48372A431C1F84D700AD0293 /* LACompView.m */,
62FE41411D41862200CA389D /* LAAnimatableLayer.h */,
62FE41421D41862200CA389D /* LAAnimatableLayer.m */,
4804B32A1C1F835F00DA8AF7 /* LALayerView.h */,
4804B32B1C1F835F00DA8AF7 /* LALayerView.m */,
620CD7E31D38180800055AD1 /* LAGroupLayerView.h */,
@@ -325,6 +332,7 @@
48372A651C20B04300AD0293 /* JSONExamples */ = {
isa = PBXGroup;
children = (
62FE41151D41526D00CA389D /* InOutAnimation.json */,
62FE40EB1D402C0200CA389D /* parentTest.json */,
62FE40E91D401C0700CA389D /* scaleTest.json */,
62FE40DA1D3FF0D900CA389D /* Heart58OnOff.json */,
@@ -501,6 +509,7 @@
62FE40D51D3EDC7400CA389D /* 073_AnimaterectangleSizeEasyEase.json in Resources */,
62FE40D21D3EDC7400CA389D /* 063_AnimateTransformPrecomped.json in Resources */,
62FE40C41D3EDC7400CA389D /* 016_GroupedShapes_01.json in Resources */,
62FE41161D41526D00CA389D /* InOutAnimation.json in Resources */,
62FE40B61D3EDC7400CA389D /* 003_TwoShapeLayerSquares_01.json in Resources */,
62FE40DE1D3FF0D900CA389D /* Heart58TurnOff.json in Resources */,
62FE40CF1D3EDC7400CA389D /* 059_AnimateTransformPositionRoveAcrossTime.json in Resources */,
@@ -572,6 +581,7 @@
4804B31E1C1F757600DA8AF7 /* UIColor+Expanded.m in Sources */,
4804B2FF1C1F55E600DA8AF7 /* ViewController.m in Sources */,
48372A641C20A91C00AD0293 /* LAJSONExplorerViewController.m in Sources */,
62FE41431D41862200CA389D /* LAAnimatableLayer.m in Sources */,
620A56651D1C81930030EBFB /* LAAnimatableShapeValue.m in Sources */,
4804B3191C1F6DEA00DA8AF7 /* LAComposition.m in Sources */,
4804B2FC1C1F55E600DA8AF7 /* AppDelegate.m in Sources */,
@@ -713,6 +723,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
GCC_PREFIX_HEADER = PrefixHeader.pch;
INFOPLIST_FILE = LotteAnimator/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "-.LotteAnimator";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -726,6 +737,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
GCC_PREFIX_HEADER = PrefixHeader.pch;
INFOPLIST_FILE = LotteAnimator/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "-.LotteAnimator";
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -48,15 +48,15 @@
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "No"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "LotteAnimator/LAAnimatableColorValue.m"
timestampString = "490743941.196227"
timestampString = "490830150.413192"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "146"
endingLineNumber = "146"
startingLineNumber = "152"
endingLineNumber = "152"
landmarkName = "-_colorValueFromArray:"
landmarkType = "5">
</BreakpointContent>
@@ -131,13 +131,29 @@
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "LotteAnimator/LALayerView.m"
timestampString = "490746878.165806"
filePath = "LotteAnimator/LALayer.m"
timestampString = "490825912.611288"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "78"
endingLineNumber = "78"
landmarkName = "-_setupViewFromModelInComposition:"
startingLineNumber = "94"
endingLineNumber = "94"
landmarkName = "-_mapFromJSON:fromComposition:"
landmarkType = "5">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "LotteAnimator/LAAnimatableLayer.m"
timestampString = "490837507.04002"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "48"
endingLineNumber = "48"
landmarkName = "-setAnimationProgress:"
landmarkType = "5">
</BreakpointContent>
</BreakpointProxy>

File diff suppressed because one or more lines are too long

View File

@@ -8,6 +8,7 @@
#import <QuartzCore/QuartzCore.h>
#import "LAAnimatableValue.h"
@class LAComposition;
@interface CAAnimationGroup (LAAnimatableGroup)

View File

@@ -0,0 +1 @@
{"assets":[],"v":"4.1.7","ddd":0,"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"Shape Layer 6","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[118.677,264.516,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[69.793,157.939]},"p":{"k":[0,0]},"r":{"k":0},"nm":"Rectangle Path 1","closed":true},{"ty":"st","fillEnabled":true,"c":{"k":[255,255,255,255]},"o":{"k":100},"w":{"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[21,255,0,255]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[86.38,178.163]},"a":{"k":[0,0]},"s":{"k":[100,100]},"r":{"k":0},"o":{"k":100},"sk":{"k":0},"sa":{"k":0}}],"nm":"Rectangle 1"}],"bounds":{"l":24,"t":74,"b":318,"r":149},"ip":124,"op":180,"st":0},{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 4","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[384,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[71.103,123.916]},"p":{"k":[0,0]},"r":{"k":0},"nm":"Rectangle Path 1","closed":true},{"ty":"st","fillEnabled":true,"c":{"k":[255,255,255,255]},"o":{"k":100},"w":{"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[21,255,0,255]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[96.713,149.861]},"a":{"k":[0,0]},"s":{"k":[100,100]},"r":{"k":0},"o":{"k":100},"sk":{"k":0},"sa":{"k":0}}],"nm":"Rectangle 1"}],"bounds":{"l":36,"t":68,"b":255,"r":157},"ip":0,"op":54,"st":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[384,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[68.435,133.468]},"p":{"k":[0,0]},"r":{"k":0},"nm":"Rectangle Path 1","closed":true},{"ty":"st","fillEnabled":true,"c":{"k":[255,255,255,255]},"o":{"k":100},"w":{"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[21,255,0,255]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[-90.912,152.218]},"a":{"k":[0,0]},"s":{"k":[100,100]},"r":{"k":0},"o":{"k":100},"sk":{"k":0},"sa":{"k":0}}],"nm":"Rectangle 1"}],"bounds":{"l":-137,"t":64,"b":240,"r":-45},"ip":0,"op":18,"st":-98},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[427.548,318.548,0],"e":[800.935,318.548,0],"to":[62.2311820983887,0,0],"ti":[14.1129026412964,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":68,"s":[800.935,318.548,0],"e":[342.871,318.548,0],"to":[-14.1129026412964,0,0],"ti":[76.3440856933594,0,0]},{"t":132}]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[104.48,110.843]},"p":{"k":[0,0]},"r":{"k":0},"nm":"Rectangle Path 1","closed":true},{"ty":"st","fillEnabled":true,"c":{"k":[255,255,255,255]},"o":{"k":100},"w":{"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,24,255,255]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[-212.405,-86.514]},"a":{"k":[0,0]},"s":{"k":[100,100]},"r":{"k":0},"o":{"k":100},"sk":{"k":0},"sa":{"k":0}}],"nm":"Rectangle 1"}],"bounds":{"l":-282,"t":-160,"b":-13,"r":-143},"ip":0,"op":180,"st":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 2","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[384,300,0]},"a":{"k":[0,0,0]},"s":{"k":[43.845,100,100]}},"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[165.108,64.778]},"p":{"k":[0,0]},"r":{"k":0},"nm":"Rectangle Path 1","closed":true},{"ty":"st","fillEnabled":true,"c":{"k":[255,255,255,255]},"o":{"k":100},"w":{"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[255,0,15,255]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[-20.801,-72.45]},"a":{"k":[0,0]},"s":{"k":[100,100]},"r":{"k":0},"o":{"k":100},"sk":{"k":0},"sa":{"k":0}}],"nm":"Rectangle 1"}],"bounds":{"l":-130,"t":-116,"b":-29,"r":88},"ip":30,"op":98,"st":0}],"ip":0,"op":180,"fr":60,"w":768,"h":600}

View File

@@ -0,0 +1,23 @@
//
// LAAnimatableLayer.h
// LotteAnimator
//
// Created by brandon_withrow on 7/21/16.
// Copyright © 2016 Brandon Withrow. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
@interface LAAnimatableLayer : CALayer
@property (nonatomic, strong) NSArray *animationSublayers;
@property (nonatomic, strong) NSArray<LAAnimatableLayer *> *childLayers;
@property (nonatomic, assign) BOOL loopAnimation;
@property (nonatomic, assign) BOOL autoReverseAnimation;
@property (nonatomic, assign) CGFloat animationProgress;
- (void)play;
- (void)pause;
@end

View File

@@ -0,0 +1,94 @@
//
// LAAnimatableLayer.m
// LotteAnimator
//
// Created by brandon_withrow on 7/21/16.
// Copyright © 2016 Brandon Withrow. All rights reserved.
//
#import "LAAnimatableLayer.h"
@implementation LAAnimatableLayer
- (void)play {
[self _resumeLayer:self];
for (CALayer *layer in self.animationSublayers) {
[self _resumeLayer:layer];
}
for (LAAnimatableLayer *layer in self.childLayers) {
[layer play];
}
}
- (void)pause {
[self _pauseLayer:self];
for (CALayer *layer in self.animationSublayers) {
[self _pauseLayer:layer];
}
for (LAAnimatableLayer *layer in self.childLayers) {
[layer pause];
}
}
- (void)setLoopAnimation:(BOOL)loopAnimation {
self.repeatCount = loopAnimation ? HUGE_VALF : 0;
for (CALayer *layer in self.animationSublayers) {
layer.repeatCount = loopAnimation ? HUGE_VALF : 0;
}
for (LAAnimatableLayer *layer in self.childLayers) {
[layer setLoopAnimation:loopAnimation];
}
}
- (void)setAnimationProgress:(CGFloat)animationProgress {
self.speed = 0.0;
self.timeOffset = 0.0;
self.beginTime = 0.0;
self.beginTime = [self convertTime:CACurrentMediaTime() fromLayer:nil];
self.timeOffset = [self convertTime:CACurrentMediaTime() fromLayer:nil] + animationProgress;
for (CALayer *layer in self.animationSublayers) {
layer.speed = 0.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
layer.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.timeOffset = [layer convertTime:CACurrentMediaTime() fromLayer:nil] + animationProgress;
}
for (LAAnimatableLayer *layer in self.childLayers) {
[layer setAnimationProgress:animationProgress];
}
}
- (void)setAutoReverseAnimation:(BOOL)autoReverseAnimation {
self.autoreverses = autoReverseAnimation;
for (CALayer *layer in self.animationSublayers) {
layer.autoreverses = autoReverseAnimation;
}
for (LAAnimatableLayer *layer in self.childLayers) {
[layer setAutoReverseAnimation:autoReverseAnimation];
}
}
-(void)_pauseLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
-(void)_resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
@end

View File

@@ -12,10 +12,14 @@
- (instancetype)initWithModel:(LAComposition *)model;
@property (nonatomic, strong) LAComposition *sceneModel;
@property (nonatomic, readonly) LAComposition *sceneModel;
@property (nonatomic, assign) BOOL debugModeOn;
@property (nonatomic, assign) BOOL loop;
@property (nonatomic, assign) BOOL loopAnimation;
@property (nonatomic, assign) BOOL autoReverseAnimation;
@property (nonatomic, assign) CGFloat animationProgress;
- (void)play;
- (void)pause;
@end

View File

@@ -26,11 +26,13 @@
- (void)_buildSubviewsFromModel {
NSMutableDictionary *layerMap = [NSMutableDictionary dictionary];
for (LALayer *layer in _sceneModel.layers) {
NSArray *reversedItems = [[_sceneModel.layers reverseObjectEnumerator] allObjects];
for (LALayer *layer in reversedItems) {
LALayerView *layerView = [[LALayerView alloc] initWithModel:layer inComposition:_sceneModel];
layerMap[layer.layerID] = layerView;
[self addSubview:layerView];
[self sendSubviewToBack:layerView];
[self.layer addSublayer:layerView];
}
_layerMap = layerMap;
}
@@ -44,15 +46,30 @@
for (LALayerView *child in _layerMap.allValues) {
if ([child isKindOfClass:[LALayerView class]]) {
[child setDebugModeOn:debugModeOn];
child.alpha = debugModeOn ? 0.5 : 1;
child.opacity = debugModeOn ? 0.5 : 1;
}
}
}
- (void)play {
[CATransaction begin];
for (LALayerView *layerView in _layerMap.allValues) {
[layerView startAnimation];
[layerView play];
}
[CATransaction commit];
}
- (void)pause {
for (LALayerView *layerView in _layerMap.allValues) {
[layerView pause];
}
}
- (void)setAnimationProgress:(CGFloat)animationProgress {
for (LALayerView *layerView in _layerMap.allValues) {
[layerView setAnimationProgress:animationProgress];
}
}
@end

View File

@@ -43,27 +43,10 @@
NSMutableArray *layers = [NSMutableArray array];
NSMutableDictionary *modelMap = [NSMutableDictionary dictionary];
NSMutableDictionary *parentToChildrenMap = [NSMutableDictionary dictionary];
for (NSDictionary *layerJSON in layersJSON) {
LALayer *layer = [[LALayer alloc] initWithJSON:layerJSON frameRate:_framerate compBounds:_compBounds];
LALayer *layer = [[LALayer alloc] initWithJSON:layerJSON fromComposition:self];
[layers addObject:layer];
modelMap[layer.layerID] = layer;
if (layer.parentID) {
NSMutableArray *children = parentToChildrenMap[layer.parentID];
if (!children) {
children = [NSMutableArray array];
parentToChildrenMap[layer.parentID] = children;
}
[children addObject:[layer.layerID copy]];
}
}
for (NSNumber *parent in parentToChildrenMap.allKeys) {
NSArray *children = parentToChildrenMap[parent];
LALayer *parentLayer = modelMap[parent];
parentLayer.childrenIDs = children;
}
_modelMap = modelMap;

View File

@@ -7,10 +7,12 @@
//
#import <QuartzCore/QuartzCore.h>
#import "LAAnimatableLayer.h"
@class LAShapeGroup;
@class LAShapeTransform;
@interface LAGroupLayerView : CALayer
@interface LAGroupLayerView : LAAnimatableLayer
- (instancetype)initWithShapeGroup:(LAShapeGroup *)shapeGroup
transform:(LAShapeTransform *)transform;

View File

@@ -81,7 +81,14 @@
}
_groupLayers = groupLayers;
_shapeLayers = shapeLayers;
NSMutableArray *childLayers = [NSMutableArray array];
[childLayers addObjectsFromArray:groupLayers];
[childLayers addObjectsFromArray:shapeLayers];
self.childLayers = childLayers;
[self _buildAnimation];
[self pause];
}
- (void)_buildAnimation {
@@ -91,20 +98,8 @@
@"anchorPoint" : _shapeTransform.anchor,
@"transform" : _shapeTransform.scale,
@"sublayerTransform.rotation" : _shapeTransform.rotation}];
}
}
- (void)startAnimation {
if (_animation) {
[self addAnimation:_animation forKey:@"lotteAnimation"];
}
for (LAGroupLayerView *groupLayer in _groupLayers) {
[groupLayer startAnimation];
}
for (LAShapeLayerView *shapeLayer in _shapeLayers) {
[shapeLayer startAnimation];
}
}
- (void)setDebugModeOn:(BOOL)debugModeOn {

View File

@@ -16,6 +16,7 @@
@class LAAnimatableNumberValue;
@class LAAnimatableRectValue;
@class LAAnimatableScaleValue;
@class LAComposition;
typedef enum : NSInteger {
LALayerTypeNone,
@@ -27,16 +28,16 @@ typedef enum : NSInteger {
@interface LALayer : NSObject
- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate compBounds:(CGRect)compBounds;
- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary fromComposition:(LAComposition *)composition;
@property (nonatomic, readonly) NSString *layerName;
@property (nonatomic, readonly) NSNumber *layerID;
@property (nonatomic, readonly) LALayerType layerType;
@property (nonatomic, readonly) NSNumber *parentID;
@property (nonatomic, strong) NSArray *childrenIDs;
@property (nonatomic, readonly) NSNumber *inFrame;
@property (nonatomic, readonly) NSNumber *outFrame;
@property (nonatomic, readonly) CGRect compBounds;
@property (nonatomic, readonly) NSNumber *framerate;
@property (nonatomic, readonly) NSArray<LAShapeGroup *> *shapes;
@property (nonatomic, readonly) NSArray<LAMask *> *masks;
@@ -51,4 +52,9 @@ typedef enum : NSInteger {
@property (nonatomic, readonly) LAAnimatablePointValue *anchor;
@property (nonatomic, readonly) LAAnimatableScaleValue *scale;
@property (nonatomic, readonly) BOOL hasInOutAnimation;
@property (nonatomic, readonly) NSArray *inOutKeyframes;
@property (nonatomic, readonly) NSArray *inOutKeyTimes;
@property (nonatomic, readonly) NSTimeInterval compDuration;
@end

View File

@@ -12,21 +12,23 @@
#import "LAAnimatableNumberValue.h"
#import "LAAnimatableScaleValue.h"
#import "LAShapeGroup.h"
#import "LAComposition.h"
@implementation LALayer
- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate compBounds:(CGRect)compBounds {
- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary fromComposition:(LAComposition *)composition {
self = [super init];
if (self) {
[self _mapFromJSON:jsonDictionary frameRate:frameRate compBounds:compBounds];
[self _mapFromJSON:jsonDictionary fromComposition:composition];
}
return self;
}
- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate compBounds:(CGRect)compBounds {
- (void)_mapFromJSON:(NSDictionary *)jsonDictionary fromComposition:(LAComposition *)composition {
_layerName = [jsonDictionary[@"nm"] copy];
_layerID = [jsonDictionary[@"ind"] copy];
_compBounds = compBounds;
_compBounds = composition.compBounds;
_framerate = composition.framerate;
NSNumber *layerType = jsonDictionary[@"ty"];
if (layerType.integerValue <= LALayerTypeShape) {
@@ -47,13 +49,13 @@
NSDictionary *opacity = ks[@"o"];
if (opacity) {
_opacity = [[LAAnimatableNumberValue alloc] initWithNumberValues:opacity frameRate:frameRate];
_opacity = [[LAAnimatableNumberValue alloc] initWithNumberValues:opacity frameRate:_framerate];
[_opacity remapValuesFromMin:@0 fromMax:@100 toMin:@0 toMax:@1];
}
NSDictionary *rotation = ks[@"r"];
if (rotation) {
_rotation = [[LAAnimatableNumberValue alloc] initWithNumberValues:rotation frameRate:frameRate];
_rotation = [[LAAnimatableNumberValue alloc] initWithNumberValues:rotation frameRate:_framerate];
[_rotation remapValueWithBlock:^CGFloat(CGFloat inValue) {
return DegreesToRadians(inValue);
}];
@@ -61,34 +63,69 @@
NSDictionary *position = ks[@"p"];
if (position) {
_position = [[LAAnimatablePointValue alloc] initWithPointValues:position frameRate:frameRate];
_position = [[LAAnimatablePointValue alloc] initWithPointValues:position frameRate:_framerate];
}
NSDictionary *anchor = ks[@"a"];
if (anchor) {
_anchor = [[LAAnimatablePointValue alloc] initWithPointValues:anchor frameRate:frameRate];
[_anchor remapPointsFromBounds:compBounds toBounds:CGRectMake(0, 0, 1, 1)];
_anchor = [[LAAnimatablePointValue alloc] initWithPointValues:anchor frameRate:_framerate];
[_anchor remapPointsFromBounds:_compBounds toBounds:CGRectMake(0, 0, 1, 1)];
_anchor.usePathAnimation = NO;
}
NSDictionary *scale = ks[@"s"];
if (scale) {
_scale = [[LAAnimatableScaleValue alloc] initWithScaleValues:scale frameRate:frameRate];
_scale = [[LAAnimatableScaleValue alloc] initWithScaleValues:scale frameRate:_framerate];
}
NSMutableArray *masks = [NSMutableArray array];
for (NSDictionary *maskJSON in jsonDictionary[@"masksProperties"]) {
LAMask *mask = [[LAMask alloc] initWithJSON:maskJSON frameRate:frameRate];
LAMask *mask = [[LAMask alloc] initWithJSON:maskJSON frameRate:_framerate];
[masks addObject:mask];
}
_masks = masks;
NSMutableArray *shapes = [NSMutableArray array];
for (NSDictionary *shapeJSON in jsonDictionary[@"shapes"]) {
LAShapeGroup *group = [[LAShapeGroup alloc] initWithJSON:shapeJSON frameRate:frameRate compBounds:compBounds];
LAShapeGroup *group = [[LAShapeGroup alloc] initWithJSON:shapeJSON frameRate:_framerate compBounds:_compBounds];
[shapes addObject:group];
}
_shapes = shapes;
BOOL hasInAnmation = (_inFrame.integerValue > composition.startFrame.integerValue);
BOOL hasOutAnimation = (_outFrame.integerValue < composition.endFrame.integerValue);
_hasInOutAnimation = hasInAnmation || hasOutAnimation;
if (_hasInOutAnimation) {
NSMutableArray *keys = [NSMutableArray array];
NSMutableArray *keyTimes = [NSMutableArray array];
CGFloat compLength = composition.endFrame.floatValue - composition.startFrame.floatValue;
if (hasInAnmation) {
[keys addObject:@1];
[keyTimes addObject:@0];
[keys addObject:@0];
CGFloat inTime = _inFrame.floatValue / compLength;
[keyTimes addObject:@(inTime)];
} else {
[keys addObject:@0];
[keyTimes addObject:@0];
}
if (hasOutAnimation) {
[keys addObject:@1];
CGFloat outTime = _outFrame.floatValue / compLength;
[keyTimes addObject:@(outTime)];
[keys addObject:@1];
[keyTimes addObject:@1];
} else {
[keys addObject:@0];
[keyTimes addObject:@1];
}
_compDuration = composition.timeDuration;
_inOutKeyTimes = keyTimes;
_inOutKeyframes = keys;
}
}
@end

View File

@@ -7,15 +7,15 @@
//
#import <UIKit/UIKit.h>
#import "LAAnimatableLayer.h"
#import "LAModels.h"
@interface LALayerView : UIView
@interface LALayerView : LAAnimatableLayer
- (instancetype)initWithModel:(LALayer *)model inComposition:(LAComposition *)comp;
@property (nonatomic, readonly) LALayer *layerModel;
@property (nonatomic, assign) BOOL debugModeOn;
- (void)startAnimation;
@end

View File

@@ -11,7 +11,7 @@
#import "LAGroupLayerView.h"
#import "CAAnimationGroup+LAAnimatableGroup.h"
@interface LAParentLayer : CALayer
@interface LAParentLayer : LAAnimatableLayer
- (instancetype)initWithParentModel:(LALayer *)parent compBounds:(CGRect)bounds;
- (void)startAnimation;
@@ -39,6 +39,7 @@
self.transform = _parentModel.scale.initialScale;
self.sublayerTransform = CATransform3DMakeRotation(_parentModel.rotation.initialValue.floatValue, 0, 0, 1);
[self _buildAnimations];
[self pause];
}
- (void)_buildAnimations {
@@ -46,12 +47,7 @@
@"anchorPoint" : _parentModel.anchor,
@"transform" : _parentModel.scale,
@"sublayerTransform.rotation" : _parentModel.rotation}];
}
- (void)startAnimation {
if (_animation) {
[self addAnimation:_animation forKey:@"lotteAnimation"];
}
[self addAnimation:_animation forKey:@"lotteAnimation"];
}
@end
@@ -61,11 +57,12 @@
CALayer *_childContainerLayer;
CALayer *_rotationLayer;
CAAnimationGroup *_animation;
CAKeyframeAnimation *_inOutAnimation;
NSArray<LAParentLayer *> *_parentLayers;
}
- (instancetype)initWithModel:(LALayer *)model inComposition:(LAComposition *)comp {
self = [super initWithFrame:model.compBounds];
self = [super init];
if (self) {
_layerModel = model;
[self _setupViewFromModelInComposition:comp];
@@ -74,8 +71,10 @@
}
- (void)_setupViewFromModelInComposition:(LAComposition *)comp {
self.bounds = comp.compBounds;
self.anchorPoint = CGPointZero;
_childContainerLayer = [CALayer new];
// Setup Parents
self.animationSublayers = @[_childContainerLayer];
NSNumber *parentID = _layerModel.parentID;
CALayer *currentChild = _childContainerLayer;
@@ -93,14 +92,14 @@
if (parentLayers.count) {
_parentLayers = parentLayers;
}
[self.layer addSublayer:currentChild];
[self addSublayer:currentChild];
self.alpha = _layerModel.opacity.initialValue.floatValue;
_childContainerLayer.opacity = _layerModel.opacity.initialValue.floatValue;
_childContainerLayer.position = _layerModel.position.initialPoint;
_childContainerLayer.anchorPoint = _layerModel.anchor.initialPoint;
_childContainerLayer.transform = _layerModel.scale.initialScale;
_childContainerLayer.sublayerTransform = CATransform3DMakeRotation(_layerModel.rotation.initialValue.floatValue, 0, 0, 1);
self.clipsToBounds = NO;
self.hidden = _layerModel.inFrame.integerValue > comp.startFrame.integerValue;
NSArray *groupItems = _layerModel.shapes;
NSArray *reversedItems = [[groupItems reverseObjectEnumerator] allObjects];
@@ -119,7 +118,14 @@
}
_shapeLayers = shapeLayers;
NSMutableArray *childLayers = [NSMutableArray array];
[childLayers addObjectsFromArray:_parentLayers];
[childLayers addObjectsFromArray:_shapeLayers];
self.childLayers = childLayers;
[self _buildAnimations];
[self pause];
}
- (void)_buildAnimations {
@@ -128,17 +134,23 @@
@"anchorPoint" : _layerModel.anchor,
@"transform" : _layerModel.scale,
@"sublayerTransform.rotation" : _layerModel.rotation}];
}
- (void)startAnimation {
if (_animation) {
[_childContainerLayer addAnimation:_animation forKey:@"lotteAnimation"];
}
for (LAGroupLayerView *groupLayer in _shapeLayers) {
[groupLayer startAnimation];
}
for (LAParentLayer *parent in _parentLayers) {
[parent startAnimation];
if (_layerModel.hasInOutAnimation) {
CAKeyframeAnimation *inOutAnimation = [CAKeyframeAnimation animationWithKeyPath:@"hidden"];
inOutAnimation.keyTimes = _layerModel.inOutKeyTimes;
inOutAnimation.values = _layerModel.inOutKeyframes;
inOutAnimation.duration = _layerModel.compDuration;
inOutAnimation.calculationMode = kCAAnimationDiscrete;
inOutAnimation.fillMode = kCAFillModeForwards;
inOutAnimation.removedOnCompletion = NO;
_inOutAnimation = inOutAnimation;
[self addAnimation:_inOutAnimation forKey:@""];
}
}
@@ -148,9 +160,9 @@
- (void)setDebugModeOn:(BOOL)debugModeOn {
_debugModeOn = debugModeOn;
self.layer.borderColor = debugModeOn ? [UIColor redColor].CGColor : nil;
self.layer.borderWidth = debugModeOn ? 2 : 0;
self.backgroundColor = debugModeOn ? [[UIColor blueColor] colorWithAlphaComponent:0.2] : [UIColor clearColor];
self.borderColor = debugModeOn ? [UIColor redColor].CGColor : nil;
self.borderWidth = debugModeOn ? 2 : 0;
self.backgroundColor = debugModeOn ? [[UIColor blueColor] colorWithAlphaComponent:0.2].CGColor : [UIColor clearColor].CGColor;
for (LAGroupLayerView *group in _shapeLayers) {
group.debugModeOn = debugModeOn;

View File

@@ -7,8 +7,9 @@
//
#import <QuartzCore/QuartzCore.h>
#import "LAAnimatableLayer.h"
@interface LARectShapeLayer : CALayer
@interface LARectShapeLayer : LAAnimatableLayer
- (instancetype)initWithRectShape:(LAShapeRectangle *)rectShape
fill:(LAShapeFill *)fill

View File

@@ -60,6 +60,7 @@
_strokeLayer.backgroundColor = nil;
[self addSublayer:_strokeLayer];
[self _buildAnimation];
[self pause];
}
return self;

View File

@@ -7,8 +7,9 @@
//
#import <UIKit/UIKit.h>
#import "LAAnimatableLayer.h"
@interface LAShapeLayerView : CALayer
@interface LAShapeLayerView : LAAnimatableLayer
- (instancetype)initWithShape:(LAShapePath *)shape
fill:(LAShapeFill *)fill

View File

@@ -56,8 +56,11 @@
_strokeLayer.opacity = _stroke.opacity.initialValue.floatValue;
_strokeLayer.lineWidth = _stroke.width.initialValue.floatValue;
_strokeLayer.fillColor = nil;
self.animationSublayers = @[_fillLayer, _strokeLayer];
[self addSublayer:_strokeLayer];
[self _buildAnimation];
[self pause];
}
return self;
@@ -70,6 +73,7 @@
@"anchorPoint" : _transform.anchor,
@"transform" : _transform.scale,
@"sublayerTransform.rotation" : _transform.rotation}];
[self addAnimation:_animation forKey:@"LotteAnimation"];
}
if (_stroke) {
@@ -77,24 +81,13 @@
@"opacity" : _stroke.opacity,
@"lineWidth" : _stroke.width,
@"path" : _path.shapePath}];
[_strokeLayer addAnimation:_strokeAnimation forKey:@""];
}
if (_fill) {
_fillAnimation = [CAAnimationGroup animationGroupForAnimatablePropertiesWithKeyPaths:@{@"fillColor" : _fill.color,
@"opacity" : _fill.opacity,
@"path" : _path.shapePath}];
}
}
- (void)startAnimation {
if (_animation) {
[self addAnimation:_animation forKey:@"LotteAnimation"];
}
if (_strokeAnimation) {
[_strokeLayer addAnimation:_strokeAnimation forKey:@""];
}
if (_fillAnimation) {
[_fillLayer addAnimation:_fillAnimation forKey:@""];
}
}

View File

@@ -43,12 +43,12 @@
@property (nonatomic, strong) UIButton *openButton;
@property (nonatomic, strong) LAComposition *currentScene;
@property (nonatomic, strong) UIView *currentSceneView;
@property (nonatomic, strong) LACompView *currentSceneView;
@property (nonatomic, strong) UIView *logView;
@property (nonatomic, strong) UITextView *logTextField;
@property (nonatomic, strong) UIButton *openLogButton;
@property (nonatomic, strong) UIButton *closeLogButton;
@property (nonatomic, strong) UISlider *animationSlider;
@end
@implementation ViewController
@@ -104,12 +104,20 @@
[self.openLogButton addTarget:self action:@selector(_openLogButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.openLogButton];
self.animationSlider = [[UISlider alloc] initWithFrame:CGRectZero];
[self.animationSlider addTarget:self action:@selector(_sliderChanged) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:self.animationSlider];
self.closeLogButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.closeLogButton.frame = self.view.bounds;
[self.closeLogButton addTarget:self action:@selector(_closeLog) forControlEvents:UIControlEventTouchUpInside];
self.closeLogButton.hidden = YES;
[self.view addSubview:self.closeLogButton];
self.logView = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height * 0.3, self.view.bounds.size.width, self.view.bounds.size.height * 0.7)];
self.logView.backgroundColor = [UIColor blackColor];
self.logView.transform = CGAffineTransformMakeTranslation(0, self.logView.bounds.size.height);
@@ -206,6 +214,12 @@
self.closeLogButton.frame = self.view.bounds;
self.animationSlider.frame = CGRectAttachedRightToRect(self.openLogButton.frame, CGSizeMake(200, 44), 12, YES);
}
- (void)_sliderChanged {
self.currentSceneView.animationProgress = self.animationSlider.value;
}
- (void)_openButtonPressed {
@@ -280,5 +294,6 @@
self.currentSceneView = compView;
[self.view sendSubviewToBack:self.currentSceneView];
[compView performSelector:@selector(play) withObject:nil afterDelay:1];
[compView performSelector:@selector(pause) withObject:nil afterDelay:2];
}
@end