blob: 71b0d46cb6d0852cec5d93f8ec0a31586eef63d1 (
plain)
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
|
#import "MWMSpringAnimation.h"
@interface MWMSpringAnimation ()
@property (nonatomic) CGPoint velocity;
@property (nonatomic) CGPoint targetPoint;
@property (nonatomic) UIView * view;
@property (copy, nonatomic) TMWMVoidBlock completion;
@end
@implementation MWMSpringAnimation
+ (instancetype)animationWithView:(UIView *)view target:(CGPoint)target velocity:(CGPoint)velocity completion:(TMWMVoidBlock)completion
{
return [[self alloc] initWithView:view target:target velocity:velocity completion:completion];
}
- (instancetype)initWithView:(UIView *)view target:(CGPoint)target velocity:(CGPoint)velocity completion:(TMWMVoidBlock)completion
{
self = [super init];
if (self)
{
self.view = view;
self.targetPoint = target;
self.velocity = velocity;
self.completion = completion;
}
return self;
}
- (void)animationTick:(CFTimeInterval)dt finished:(BOOL *)finished
{
CGFloat const frictionConstant = 25.;
CGFloat const springConstant = 300.;
// friction force = velocity * friction constant
CGPoint const frictionForce = MultiplyCGPoint(self.velocity, frictionConstant);
// spring force = (target point - current position) * spring constant
CGPoint const springForce = MultiplyCGPoint(SubtractCGPoint(self.targetPoint, self.view.center), springConstant);
// force = spring force - friction force
CGPoint const force = SubtractCGPoint(springForce, frictionForce);
// velocity = current velocity + force * time / mass
self.velocity = AddCGPoint(self.velocity, MultiplyCGPoint(force, dt));
// position = current position + velocity * time
self.view.center = AddCGPoint(self.view.center, MultiplyCGPoint(self.velocity, dt));
CGFloat const speed = LengthCGPoint(self.velocity);
CGFloat const distanceToGoal = LengthCGPoint(SubtractCGPoint(self.targetPoint, self.view.center));
if (speed < 0.05 && distanceToGoal < 1)
{
self.view.center = self.targetPoint;
*finished = YES;
if (self.completion)
self.completion();
}
}
+ (CGFloat)approxTargetFor:(CGFloat)startValue velocity:(CGFloat)velocity
{
CGFloat const decelaration = (velocity > 0 ? -1.0 : 1.0) * 300.0;
return startValue - (velocity * velocity) / (2.0 * decelaration);
}
@end
|