使用一元二次方程做实时动画
使用一元二次方程做实时动画
效果:
原理(图中坐标略有错误,仅供参考-_-!!):
YXMath.h + YXMath.m
//
// YXMath.h
//
// http://home.cnblogs.com/u/YouXianMing/
//
// Copyright (c) 2014年 Y.X. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface YXMath : NSObject
/*---- 计算一元一次方程 ----
y = kX + b
------------------------*/
@property (nonatomic, assign, readonly) CGFloat k;
@property (nonatomic, assign, readonly) CGFloat b;
- (instancetype)initWithLinearFunctionPointA:(CGPoint)pointA
pointB:(CGPoint)pointB;
/*---- 计算一元二次方程普通式 ----
y = aX^2 + bX + c
----------------------------*/
@property (nonatomic, assign, readonly) CGFloat A;
@property (nonatomic, assign, readonly) CGFloat B;
@property (nonatomic, assign, readonly) CGFloat C;
- (instancetype)initWithQuadraticFunctionPointA:(CGPoint)pointA
pointB:(CGPoint)pointB
pointC:(CGPoint)pointC;
/*---- 计算一元二次方程顶点式 ----
y = a(X - h)^2 + k
注意:顶点为(h, k)
----------------------------*/
@property (nonatomic, assign, readonly) CGFloat a;
- (instancetype)initWithQuadraticFunctionPointApex:(CGPoint)apex
point:(CGPoint)point;
@end
//
// YXMath.m
//
// http://home.cnblogs.com/u/YouXianMing/
//
// Copyright (c) 2014年 Y.X. All rights reserved.
//
#import "YXMath.h"
@implementation YXMath
- (instancetype)initWithQuadraticFunctionPointA:(CGPoint)pointA
pointB:(CGPoint)pointB
pointC:(CGPoint)pointC
{
self = [super init];
if (self)
{
CGFloat x1 = pointA.x; CGFloat y1 = pointA.y;
CGFloat x2 = pointB.x; CGFloat y2 = pointB.y;
CGFloat x3 = pointC.x; CGFloat y3 = pointC.y;
_A = calculateA(x1, y1, x2, y2, x3, y3);
_B = calculateB(x1, y1, x2, y2, x3, y3);
_C = calculateC(x1, y1, x2, y2, x3, y3);
}
return self;
}
- (instancetype)initWithLinearFunctionPointA:(CGPoint)pointA
pointB:(CGPoint)pointB
{
self = [super init];
if (self)
{
CGFloat x1 = pointA.x; CGFloat y1 = pointA.y;
CGFloat x2 = pointB.x; CGFloat y2 = pointB.y;
_k = calculateSlope(x1, y1, x2, y2);
_b = calculateConstant(x1, y1, x2, y2);
}
return self;
}
- (instancetype)initWithQuadraticFunctionPointApex:(CGPoint)apex
point:(CGPoint)point
{
self = [super init];
if (self)
{
CGFloat h = apex.x; CGFloat k = apex.y;
CGFloat x = point.x; CGFloat y = point.y;
_a = (y - k)/((x - h)*(x - h));
}
return self;
}
#pragma mark - 计算常数a b c
CGFloat calculateA(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat x3, CGFloat y3)
{
return ((y2 - y1)/(x2 - x1) - (y3 - y2)/(x3 - x2))/(x1 - x3);
}
CGFloat calculateB(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat x3, CGFloat y3)
{
return (y2 - y1)/(x2 - x1) - (((y2 - y1)/(x2 - x1) - (y3 - y2)/(x3 - x2))/(x1 - x3));
}
CGFloat calculateC(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat x3, CGFloat y3)
{
CGFloat a = calculateA(x1, y1, x2, y2, x3, y3);
CGFloat b = calculateB(x1, y1, x2, y2, x3, y3);
return y1 - a*x1*x1 - b*x1;
}
#pragma mark - 计算斜率 k
CGFloat calculateSlope(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2)
{
return (y2 - y1) / (x2 - x1);
}
#pragma mark - 计算常数 b
CGFloat calculateConstant(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2)
{
return (y1*(x2 - x1) - x1*(y2 - y1)) / (x2 - x1);
}
@end
RootViewController.m
//
// RootViewController.m
// Line
//
// Copyright (c) 2014年 Y.X. All rights reserved.
//
#import "RootViewController.h"
#import "YXMath.h"
@interface RootViewController ()<UIScrollViewDelegate>
{
UIView *_circle;
YXMath *_parabola;
YXMath *_line;
}
@end
@implementation RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIScrollView *rootScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
rootScrollView.contentSize = CGSizeMake(self.view.bounds.size.width*2,
self.view.bounds.size.height);
rootScrollView.pagingEnabled = YES;
rootScrollView.delegate = self;
[self.view addSubview:rootScrollView];
// 计算一元二次方程顶点式
_parabola = [[YXMath alloc] initWithQuadraticFunctionPointApex:CGPointMake(20, 20)
point:CGPointMake(340, 600)];
// 计算一元一次方程
_line = [[YXMath alloc] initWithLinearFunctionPointA:CGPointMake(0, 20)
pointB:CGPointMake(320, 620)];
_circle = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 10, 10)];
_circle.layer.cornerRadius = 5.f;
_circle.backgroundColor = [UIColor redColor];
[rootScrollView addSubview:_circle];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(@"%f", scrollView.contentOffset.x);
CGRect tmp = _circle.frame;
tmp.origin.y = _parabola.a*(tmp.origin.x - 20)*(tmp.origin.x - 20) + 20;
tmp.origin.x = _line.k*scrollView.contentOffset.x + _line.b;
_circle.frame = tmp;
}
@end
注意:
动态设置动画都是需要精确计算的,按照线性关系,或者抛物线关系等等,需要精确计算.