使用一元二次方程做实时动画
使用一元二次方程做实时动画
效果:
原理(图中坐标略有错误,仅供参考-_-!!):
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
注意:
动态设置动画都是需要精确计算的,按照线性关系,或者抛物线关系等等,需要精确计算.