mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-17 11:00:07 +00:00
150 lines
6.9 KiB
Objective-C
150 lines
6.9 KiB
Objective-C
//
|
|
// LOTRoundedRectAnimator.m
|
|
// Lottie
|
|
//
|
|
// Created by brandon_withrow on 7/19/17.
|
|
// Copyright © 2017 Airbnb. All rights reserved.
|
|
//
|
|
|
|
#import "LOTRoundedRectAnimator.h"
|
|
#import "LOTPointInterpolator.h"
|
|
#import "LOTNumberInterpolator.h"
|
|
#import "CGGeometry+LOTAdditions.h"
|
|
|
|
@implementation LOTRoundedRectAnimator {
|
|
LOTPointInterpolator *_centerInterpolator;
|
|
LOTPointInterpolator *_sizeInterpolator;
|
|
LOTNumberInterpolator *_cornerRadiusInterpolator;
|
|
BOOL _reversed;
|
|
}
|
|
|
|
- (instancetype _Nonnull )initWithInputNode:(LOTAnimatorNode *_Nullable)inputNode
|
|
shapeRectangle:(LOTShapeRectangle *_Nonnull)shapeRectangle {
|
|
self = [super initWithInputNode:inputNode keyName:shapeRectangle.keyname];
|
|
if (self) {
|
|
_centerInterpolator = [[LOTPointInterpolator alloc] initWithKeyframes:shapeRectangle.position.keyframes];
|
|
_sizeInterpolator = [[LOTPointInterpolator alloc] initWithKeyframes:shapeRectangle.size.keyframes];
|
|
_cornerRadiusInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:shapeRectangle.cornerRadius.keyframes];
|
|
_reversed = shapeRectangle.reversed;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSDictionary *)valueInterpolators {
|
|
return @{@"Size" : _sizeInterpolator,
|
|
@"Position" : _centerInterpolator,
|
|
@"Roundness" : _cornerRadiusInterpolator};
|
|
}
|
|
|
|
- (BOOL)needsUpdateForFrame:(NSNumber *)frame {
|
|
return [_centerInterpolator hasUpdateForFrame:frame] || [_sizeInterpolator hasUpdateForFrame:frame] || [_cornerRadiusInterpolator hasUpdateForFrame:frame];
|
|
}
|
|
|
|
- (void)addCorner:(CGPoint)cornerPoint withRadius:(CGFloat)radius toPath:(LOTBezierPath *)path clockwise:(BOOL)clockwise {
|
|
CGPoint currentPoint = path.currentPoint;
|
|
|
|
CGFloat ellipseControlPointPercentage = 0.55228;
|
|
|
|
if (cornerPoint.y == currentPoint.y) {
|
|
// Moving east/west
|
|
if (cornerPoint.x < currentPoint.x) {
|
|
// Moving west
|
|
CGPoint corner = CGPointMake(cornerPoint.x + radius, currentPoint.y);
|
|
[path LOT_addLineToPoint:corner];
|
|
if (radius) {
|
|
CGPoint curvePoint = clockwise ? CGPointMake(cornerPoint.x, cornerPoint.y - radius) : CGPointMake(cornerPoint.x, cornerPoint.y + radius);
|
|
CGPoint cp1 = CGPointMake(corner.x - (radius * ellipseControlPointPercentage), corner.y);
|
|
CGPoint cp2 = (clockwise ?
|
|
CGPointMake(curvePoint.x, curvePoint.y + (radius * ellipseControlPointPercentage)) :
|
|
CGPointMake(curvePoint.x, curvePoint.y - (radius * ellipseControlPointPercentage)));
|
|
[path LOT_addCurveToPoint:curvePoint controlPoint1:cp1 controlPoint2:cp2];
|
|
}
|
|
} else {
|
|
// Moving east
|
|
CGPoint corner = CGPointMake(cornerPoint.x - radius, currentPoint.y);
|
|
[path LOT_addLineToPoint:corner];
|
|
if (radius) {
|
|
CGPoint curvePoint = clockwise ? CGPointMake(cornerPoint.x, cornerPoint.y + radius) : CGPointMake(cornerPoint.x, cornerPoint.y - radius);
|
|
CGPoint cp1 = CGPointMake(corner.x + (radius * ellipseControlPointPercentage), corner.y);
|
|
CGPoint cp2 = (clockwise ?
|
|
CGPointMake(curvePoint.x, curvePoint.y - (radius * ellipseControlPointPercentage)) :
|
|
CGPointMake(curvePoint.x, curvePoint.y + (radius * ellipseControlPointPercentage)));
|
|
[path LOT_addCurveToPoint:curvePoint controlPoint1:cp1 controlPoint2:cp2];
|
|
}
|
|
}
|
|
} else {
|
|
// Moving North/South
|
|
if (cornerPoint.y < currentPoint.y) {
|
|
// Moving North
|
|
CGPoint corner = CGPointMake(currentPoint.x, cornerPoint.y + radius);
|
|
[path LOT_addLineToPoint:corner];
|
|
if (radius) {
|
|
CGPoint curvePoint = clockwise ? CGPointMake(cornerPoint.x + radius, cornerPoint.y) : CGPointMake(cornerPoint.x - radius, cornerPoint.y);
|
|
CGPoint cp1 = CGPointMake(corner.x, corner.y - (radius * ellipseControlPointPercentage));
|
|
CGPoint cp2 = (clockwise ?
|
|
CGPointMake(curvePoint.x - (radius * ellipseControlPointPercentage), curvePoint.y) :
|
|
CGPointMake(curvePoint.x + (radius * ellipseControlPointPercentage), curvePoint.y));
|
|
[path LOT_addCurveToPoint:curvePoint controlPoint1:cp1 controlPoint2:cp2];
|
|
}
|
|
|
|
} else {
|
|
// moving south
|
|
CGPoint corner = CGPointMake(currentPoint.x, cornerPoint.y - radius);
|
|
[path LOT_addLineToPoint:corner];
|
|
if (radius) {
|
|
CGPoint curvePoint = clockwise ? CGPointMake(cornerPoint.x - radius, cornerPoint.y) : CGPointMake(cornerPoint.x + radius, cornerPoint.y);
|
|
CGPoint cp1 = CGPointMake(corner.x, corner.y + (radius * ellipseControlPointPercentage));
|
|
CGPoint cp2 = (clockwise ?
|
|
CGPointMake(curvePoint.x + (radius * ellipseControlPointPercentage), curvePoint.y) :
|
|
CGPointMake(curvePoint.x - (radius * ellipseControlPointPercentage), curvePoint.y));
|
|
[path LOT_addCurveToPoint:curvePoint controlPoint1:cp1 controlPoint2:cp2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)performLocalUpdate {
|
|
CGFloat cornerRadius = [_cornerRadiusInterpolator floatValueForFrame:self.currentFrame];
|
|
CGPoint size = [_sizeInterpolator pointValueForFrame:self.currentFrame];
|
|
CGPoint position = [_centerInterpolator pointValueForFrame:self.currentFrame];
|
|
|
|
CGFloat halfWidth = size.x / 2;
|
|
CGFloat halfHeight = size.y / 2;
|
|
|
|
CGRect rectFrame = CGRectMake(position.x - halfWidth, position.y - halfHeight, size.x, size.y);
|
|
|
|
CGPoint topLeft = CGPointMake(CGRectGetMinX(rectFrame), CGRectGetMinY(rectFrame));
|
|
CGPoint topRight = CGPointMake(CGRectGetMaxX(rectFrame), CGRectGetMinY(rectFrame));
|
|
CGPoint bottomLeft = CGPointMake(CGRectGetMinX(rectFrame), CGRectGetMaxY(rectFrame));
|
|
CGPoint bottomRight = CGPointMake(CGRectGetMaxX(rectFrame), CGRectGetMaxY(rectFrame));
|
|
// UIBezierPath Draws rects from the top left corner, After Effects draws them from the top right.
|
|
// Switching to manual drawing.
|
|
|
|
CGFloat radius = MIN(MIN(halfWidth, halfHeight), cornerRadius);
|
|
BOOL clockWise = !_reversed;
|
|
|
|
LOTBezierPath *path1 = [[LOTBezierPath alloc] init];
|
|
path1.cacheLengths = self.pathShouldCacheLengths;
|
|
CGPoint startPoint = (clockWise ?
|
|
CGPointMake(topRight.x, topRight.y + radius) :
|
|
CGPointMake(topRight.x - radius, topRight.y));
|
|
[path1 LOT_moveToPoint:startPoint];
|
|
if (clockWise) {
|
|
[self addCorner:bottomRight withRadius:radius toPath:path1 clockwise:clockWise];
|
|
[self addCorner:bottomLeft withRadius:radius toPath:path1 clockwise:clockWise];
|
|
[self addCorner:topLeft withRadius:radius toPath:path1 clockwise:clockWise];
|
|
[self addCorner:topRight withRadius:radius toPath:path1 clockwise:clockWise];
|
|
} else {
|
|
[self addCorner:topLeft withRadius:radius toPath:path1 clockwise:clockWise];
|
|
[self addCorner:bottomLeft withRadius:radius toPath:path1 clockwise:clockWise];
|
|
[self addCorner:bottomRight withRadius:radius toPath:path1 clockwise:clockWise];
|
|
[self addCorner:topRight withRadius:radius toPath:path1 clockwise:clockWise];
|
|
}
|
|
[path1 LOT_closePath];
|
|
self.localPath = path1;
|
|
}
|
|
|
|
|
|
|
|
@end
|