LOTMaskContainer.m
3.0 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//
// LOTMaskContainer.m
// Lottie
//
// Created by brandon_withrow on 7/19/17.
// Copyright © 2017 Airbnb. All rights reserved.
//
#import "LOTMaskContainer.h"
#import "LOTPathInterpolator.h"
#import "LOTNumberInterpolator.h"
@interface LOTMaskNodeLayer : CAShapeLayer
@property (nonatomic, readonly) LOTMask *maskNode;
- (instancetype)initWithMask:(LOTMask *)maskNode;
- (BOOL)hasUpdateForFrame:(NSNumber *)frame;
@end
@implementation LOTMaskNodeLayer {
LOTPathInterpolator *_pathInterpolator;
LOTNumberInterpolator *_opacityInterpolator;
LOTNumberInterpolator *_expansionInterpolator;
}
- (instancetype)initWithMask:(LOTMask *)maskNode {
self = [super init];
if (self) {
_pathInterpolator = [[LOTPathInterpolator alloc] initWithKeyframes:maskNode.maskPath.keyframes];
_opacityInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:maskNode.opacity.keyframes];
_expansionInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:maskNode.expansion.keyframes];
_maskNode = maskNode;
self.fillColor = [UIColor blueColor].CGColor;
}
return self;
}
- (void)updateForFrame:(NSNumber *)frame withViewBounds:(CGRect)viewBounds {
if ([self hasUpdateForFrame:frame]) {
LOTBezierPath *path = [_pathInterpolator pathForFrame:frame cacheLengths:NO];
if (self.maskNode.maskMode == LOTMaskModeSubtract) {
CGMutablePathRef pathRef = CGPathCreateMutable();
CGPathAddRect(pathRef, NULL, viewBounds);
CGPathAddPath(pathRef, NULL, path.CGPath);
self.path = pathRef;
self.fillRule = @"even-odd";
CGPathRelease(pathRef);
} else {
self.path = path.CGPath;
}
self.opacity = [_opacityInterpolator floatValueForFrame:frame];
}
}
- (BOOL)hasUpdateForFrame:(NSNumber *)frame {
return ([_pathInterpolator hasUpdateForFrame:frame] ||
[_opacityInterpolator hasUpdateForFrame:frame]);
}
@end
@implementation LOTMaskContainer {
NSArray<LOTMaskNodeLayer *> *_masks;
}
- (instancetype)initWithMasks:(NSArray<LOTMask *> *)masks {
self = [super init];
if (self) {
NSMutableArray *maskNodes = [NSMutableArray array];
CALayer *containerLayer = [CALayer layer];
for (LOTMask *mask in masks) {
LOTMaskNodeLayer *node = [[LOTMaskNodeLayer alloc] initWithMask:mask];
[maskNodes addObject:node];
if (mask.maskMode == LOTMaskModeAdd ||
mask == masks.firstObject) {
[containerLayer addSublayer:node];
} else {
containerLayer.mask = node;
CALayer *newContainer = [CALayer layer];
[newContainer addSublayer:containerLayer];
containerLayer = newContainer;
}
}
[self addSublayer:containerLayer];
_masks = maskNodes;
}
return self;
}
- (void)setCurrentFrame:(NSNumber *)currentFrame {
if (_currentFrame == currentFrame) {
return;
}
_currentFrame = currentFrame;
for (LOTMaskNodeLayer *nodeLayer in _masks) {
[nodeLayer updateForFrame:currentFrame withViewBounds:self.bounds];
}
}
@end