美文网首页
OC实现有趣的过山车动画

OC实现有趣的过山车动画

作者: 浪漫恋星空 | 来源:发表于2017-03-10 00:06 被阅读39次

    在网上看到一个有趣的过山车动画,有大神用swift实现了。我参照着用OC实现了一遍,我们不生产代码,只是代码的搬运工。

    动画如图所示:


    过山车动画.gif
    实现动画主要使用到的类:
    • CAShapeLayer
    • CAGradientLayer
    • CAKeyframeAnimation
    实现思路:

    渐变的背景用CAGradientLayer实现,山峰,草坪和轨道利用CAShapeLayer配合UIBezierPath实现,云,树和大地都是图片资源,直接通过设置CALayer的contents实现。动画实现使用的是CAKeyframeAnimation。代码如下:过山车动画OC

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @property (nonatomic, strong) CALayer *groundLayer;
    
    @property (nonatomic, strong) CAShapeLayer *yellowPath;
    
    @property (nonatomic, strong) CAShapeLayer *greenPath;
    
    @property (nonatomic, assign) NSInteger count;
    
    @property (nonatomic, assign) NSInteger count1;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        
        [super viewDidLoad];
        _count = 0;
        _count1 = 0;
        
        CGSize size = self.view.frame.size;
        [self createGradientLayerWithSize:size];
        [self createMountainLayerWithSize:size];
        [self createGrasslandlayerWithSize:size];
        
        _groundLayer = [self createGroundLayerWithSize:size];
        _yellowPath = [self createYellowPathLayerWithSize:size];
        _greenPath = [self createGreenPathLayerWithSize:size];
        
        [self addTreeLayeWithSize:size];
        
        [NSTimer scheduledTimerWithTimeInterval:0.092 target:self selector:@selector(startAnimated:) userInfo:nil repeats:YES];
        [NSTimer scheduledTimerWithTimeInterval:0.095 target:self selector:@selector(startAnimated1:) userInfo:nil repeats:YES];
        
        [self addCloudAnimationWithSize:size];
    }
    
    - (void)startAnimated:(NSTimer *)timer
    {
        if (_count == 5) {
            [timer invalidate];
            timer = nil;
        }
        [self addYellowCarPathAnimation];
        _count ++;
    }
    
    - (void)startAnimated1:(NSTimer *)timer
    {
        if (_count1 == 5) {
            [timer invalidate];
            timer = nil;
        }
        [self addGreenCarPathAnimationWithSize:self.view.frame.size];
        _count1 ++;
    }
    
    - (CGFloat)getPoint:(CGPoint)pointOne pointTow:(CGPoint)pointTow referenceX:(CGFloat)referenceX
    {
        CGFloat x1 = pointOne.x;
        CGFloat y1 = pointOne.y;
        CGFloat x2 = pointTow.x;
        CGFloat y2 = pointTow.y;
        
        CGFloat a,b;
        a = (y2 - y1) / (x2 - x1);
        b = y1 - a * x1;
        
        CGFloat y = a * referenceX + b;
        return y;
    }
    
    - (CAGradientLayer *)createGradientLayerWithSize:(CGSize)size
    {
        CAGradientLayer *layer = [CAGradientLayer layer];
        layer.frame = CGRectMake(0, 0, size.width, size.height - 20);
        layer.colors = @[(__bridge id)[[UIColor alloc] initWithRed:178.0/255.0 green:226.0/255.0 blue:248.0/255.0 alpha:1].CGColor,(__bridge id)[[UIColor alloc] initWithRed:232.0/255.0 green:244.0/255.0 blue:193.0/255.0 alpha:1].CGColor];
        layer.startPoint = CGPointMake(0, 0);
        layer.endPoint = CGPointMake(1, 1);
        [self.view.layer addSublayer:layer];
        return layer;
    }
    
    - (void)createMountainLayerWithSize:(CGSize)size
    {
        CAShapeLayer *mountainOne = [CAShapeLayer layer];
        UIBezierPath *pathOne = [[UIBezierPath alloc] init];
        [pathOne moveToPoint:CGPointMake(0, size.height - 100)];
        [pathOne addLineToPoint:CGPointMake(100, 100)];
        [pathOne addLineToPoint:CGPointMake(size.width / 3, size.height - 100)];
        [pathOne addLineToPoint:CGPointMake(size.width / 1.5, size.width - 50)];
        [pathOne addLineToPoint:CGPointMake(0, size.height)];
        mountainOne.path = pathOne.CGPath;
        mountainOne.fillColor = [UIColor whiteColor].CGColor;
        [self.view.layer addSublayer:mountainOne];
        
        CAShapeLayer *mountainOneLayer = [CAShapeLayer layer];
        UIBezierPath *pathLayerOne = [[UIBezierPath alloc] init];
        [pathLayerOne moveToPoint:CGPointMake(0, size.height - 120)];
        CGFloat pathOneHeight = [self getPoint:CGPointMake(0, size.height - 120) pointTow:CGPointMake(100, 100) referenceX:55];
        CGFloat pathTwoHeight = [self getPoint:CGPointMake(100, 100) pointTow:CGPointMake(size.width / 3, size.height - 100) referenceX:160];
        [pathLayerOne addLineToPoint:CGPointMake(55, pathOneHeight)];
        [pathLayerOne addLineToPoint:CGPointMake(70, pathOneHeight + 15)];
        [pathLayerOne addLineToPoint:CGPointMake(90, pathOneHeight)];
        [pathLayerOne addLineToPoint:CGPointMake(110, pathOneHeight + 25)];
        [pathLayerOne addLineToPoint:CGPointMake(130, pathOneHeight - 5)];
        [pathLayerOne addLineToPoint:CGPointMake(160, pathTwoHeight)];
        
        [pathLayerOne addLineToPoint:CGPointMake(size.width / 3, size.height - 100)];
        [pathLayerOne addLineToPoint:CGPointMake(size.width / 1.5, size.height - 50)];
        [pathLayerOne addLineToPoint:CGPointMake(0, size.height)];
        mountainOneLayer.path = pathLayerOne.CGPath;
        mountainOneLayer.fillColor = [[UIColor alloc] initWithRed:104.0/255.0 green:92.0/255.0 blue:157.0/255.0 alpha:1].CGColor;
        [self.view.layer addSublayer:mountainOneLayer];
        
        CAShapeLayer *mountainTwo = [CAShapeLayer layer];
        UIBezierPath *pathTwo = [[UIBezierPath alloc] init];
        [pathTwo moveToPoint:CGPointMake(size.width / 4, size.height - 90)];
        [pathTwo addLineToPoint:CGPointMake(size.width / 2.7, 200)];
        [pathTwo addLineToPoint:CGPointMake(size.width / 1.8, size.height - 85)];
        [pathTwo addLineToPoint:CGPointMake(size.width / 1.6, size.height - 125)];
        [pathTwo addLineToPoint:CGPointMake(size.width / 1.35, size.height - 70)];
        [pathTwo addLineToPoint:CGPointMake(0, size.height)];
        mountainTwo.path = pathTwo.CGPath;
        mountainTwo.fillColor = [UIColor whiteColor].CGColor;
        [self.view.layer insertSublayer:mountainTwo below:mountainOne];
        
        CAShapeLayer *mountainTwoLayer = [CAShapeLayer layer];
        UIBezierPath *pathLayerTwo = [[UIBezierPath alloc] init];
        [pathLayerTwo moveToPoint:CGPointMake(0, size.height)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 4, size.height - 90)];
        pathOneHeight = [self getPoint:CGPointMake(size.width / 4, size.height - 90) pointTow:CGPointMake(size.width / 2.7, 200) referenceX:size.width / 4 + 50];
        pathTwoHeight = [self getPoint:CGPointMake(size.width / 1.8, size.height - 85) pointTow:CGPointMake(size.width / 2.7, 200) referenceX:size.width / 2.2];
        CGFloat pathThreeHeight = [self getPoint:CGPointMake(size.width / 1.8, size.height - 85) pointTow:CGPointMake(size.width / 1.6, size.height - 125) referenceX:size.width / 1.67];
        CGFloat pathFourHeight = [self getPoint:CGPointMake(size.width / 1.35, size.height - 70) pointTow:CGPointMake(size.width / 1.6, size.height - 125) referenceX:size.width / 1.50];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 4 + 50, pathOneHeight)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 4 + 70, pathOneHeight + 15)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 4 + 90, pathOneHeight)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 4 + 110, pathOneHeight + 15)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 2.2, pathTwoHeight)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 1.8, size.height - 85)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 1.67, pathThreeHeight)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 1.65, pathThreeHeight + 5)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 1.60, pathThreeHeight - 2)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 1.58, pathFourHeight + 2)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 1.55, pathFourHeight - 5)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 1.50, pathFourHeight)];
        [pathLayerTwo addLineToPoint:CGPointMake(size.width / 1.35, size.height - 70)];
        [pathLayerTwo addLineToPoint:CGPointMake(0, size.height)];
        
        mountainTwoLayer.path = pathLayerTwo.CGPath;
        mountainTwoLayer.fillColor = [[UIColor alloc] initWithRed:75.0/255.0 green:65.0/255.0 blue:111.0/255.0 alpha:1.0].CGColor;
        [self.view.layer insertSublayer:mountainTwoLayer below:mountainOne];
    }
    
    - (NSArray<CAShapeLayer *> *)createGrasslandlayerWithSize:(CGSize)size
    {
        CAShapeLayer *grasslandOne = [CAShapeLayer layer];
        UIBezierPath *patnOne = [[UIBezierPath alloc] init];
        [patnOne moveToPoint:CGPointMake(0, size.height - 20)];
        [patnOne addLineToPoint:CGPointMake(0, size.height - 100)];
        [patnOne addQuadCurveToPoint:CGPointMake(size.width / 3.0, size.height - 20) controlPoint:CGPointMake(size.width, size.height - 100)];
        grasslandOne.path = patnOne.CGPath;
        
        grasslandOne.fillColor = [[UIColor alloc] initWithRed:82.0/255.0 green:177.0/255.0 blue:44.0/255.0 alpha:1.0].CGColor;
        [self.view.layer addSublayer:grasslandOne];
        
        CAShapeLayer *grasslandTwo = [CAShapeLayer layer];
        UIBezierPath *pathTwo = [[UIBezierPath alloc] init];
        [pathTwo moveToPoint:CGPointMake(0, size.height - 20)];
        [pathTwo addQuadCurveToPoint:CGPointMake(size.width, size.height - 60) controlPoint:CGPointMake(size.width / 2.0, size.height - 100)];
        [pathTwo addLineToPoint:CGPointMake(size.width, size.height - 20)];
        grasslandTwo.path = pathTwo.CGPath;
        grasslandTwo.fillColor = [[UIColor alloc] initWithRed:92.0/255.0 green:195.0/255.0 blue:52.0/255.0 alpha:1.0].CGColor;
        [self.view.layer addSublayer:grasslandTwo];
        
        return @[grasslandOne, grasslandTwo];
    }
    
    - (CALayer *)createGroundLayerWithSize:(CGSize)size
    {
        CALayer *ground = [CALayer layer];
        ground.frame = CGRectMake(0, size.height - 20, size.width, 20);
        ground.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"7"]].CGColor;
        [self.view.layer addSublayer:ground];
        return ground;
    }
    
    - (CAShapeLayer *)createYellowPathLayerWithSize:(CGSize)size
    {
        CAShapeLayer *calayer = [CAShapeLayer layer];
        calayer.backgroundColor = [UIColor redColor].CGColor;
        calayer.lineWidth = 5;
        calayer.strokeColor = [[UIColor alloc] initWithRed:210.0/255.0 green:179.0/255.0 blue:54.0/255.0 alpha:1.0].CGColor;
        UIBezierPath *path = [[UIBezierPath alloc] init];
        [path moveToPoint:CGPointMake(0, size.height - 70)];
        [path addCurveToPoint:CGPointMake(size.width / 1.5, 200) controlPoint1:CGPointMake(size.width / 6, size.height - 200) controlPoint2:CGPointMake(size.width / 2.5, size.height + 50)];
        [path addQuadCurveToPoint:CGPointMake(size.width + 10, size.height / 3) controlPoint:CGPointMake(size.width - 100, 50)];
        [path addLineToPoint:CGPointMake(size.width + 10, size.height + 10)];
        [path addLineToPoint:CGPointMake(0, size.height + 10)];
        calayer.fillColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"2"]].CGColor;
        calayer.path = path.CGPath;
        [self.view.layer insertSublayer:calayer below:_groundLayer];
        
        CAShapeLayer *lineLayer = [CAShapeLayer layer];
        lineLayer.lineCap = kCALineCapRound;
        lineLayer.strokeColor = [UIColor whiteColor].CGColor;
        lineLayer.lineDashPattern = @[@1,@5];
        lineLayer.lineWidth = 2;
        lineLayer.fillColor = [UIColor clearColor].CGColor;
        lineLayer.path = path.CGPath;
        
        [calayer addSublayer:lineLayer];
        return calayer;
    }
    
    - (CAShapeLayer *)createGreenPathLayerWithSize:(CGSize)size
    {
        CAShapeLayer *calayer = [CAShapeLayer layer];
        calayer.backgroundColor = [UIColor redColor].CGColor;
        calayer.lineWidth = 5;
        calayer.fillRule = kCAFillRuleEvenOdd;
        calayer.strokeColor = [[UIColor alloc] initWithRed:0.0/255.0 green:147.0/255.0 blue:163.0/255.0 alpha:1.0].CGColor;
        UIBezierPath *path = [[UIBezierPath alloc] init];
        path.lineCapStyle = kCGLineCapRound;
        path.lineJoinStyle = kCGLineJoinRound;
        [path moveToPoint:CGPointMake(size.width + 10, size.height)];
        [path addLineToPoint:CGPointMake(size.width + 10, size.height - 70)];
        [path addQuadCurveToPoint:CGPointMake(size.width / 1.8, size.height - 70) controlPoint:CGPointMake(size.width - 120, 200)];
        [path addArcWithCenter:CGPointMake(size.width / 1.9, size.height - 140) radius:70 startAngle:0.5 * M_PI endAngle:2.5 * M_PI clockwise:YES];
        [path addCurveToPoint:CGPointMake(0, size.height - 100) controlPoint1:CGPointMake(size.width / 1.8 - 60, size.height - 60) controlPoint2:CGPointMake(150, size.height / 2.3)];
        [path addLineToPoint:CGPointMake(-100, size.height + 10)];
        calayer.fillColor = [UIColor clearColor].CGColor;
        calayer.path = path.CGPath;
        [self.view.layer insertSublayer:calayer below:_groundLayer];
        
        CAShapeLayer *greenLayer = [CAShapeLayer layer];
        greenLayer.fillRule = kCAFillRuleEvenOdd;
        greenLayer.strokeColor = [[UIColor alloc] initWithRed:0.0/255.0 green:147.0/255.0 blue:163.0/255.0 alpha:1.0].CGColor;
        UIBezierPath *greenPath = [[UIBezierPath alloc] init];
        [greenPath moveToPoint:CGPointMake(size.width + 10, size.height)];
        [greenPath addLineToPoint:CGPointMake(size.width + 10, size.height - 70)];
        [greenPath addQuadCurveToPoint:CGPointMake(size.width / 1.8, size.height - 70) controlPoint:CGPointMake(size.width - 120, 200)];
        [greenPath addCurveToPoint:CGPointMake(0, size.height - 100) controlPoint1:CGPointMake(size.width / 1.8 - 60, size.height - 60) controlPoint2:CGPointMake(150, size.height / 2.3)];
        [greenPath addLineToPoint:CGPointMake(-100, size.height + 10)];
        greenLayer.fillColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"3"]].CGColor;
        greenLayer.path = greenPath.CGPath;
        [self.view.layer insertSublayer:greenLayer below:calayer];
        
        CAShapeLayer *lineLayer = [CAShapeLayer layer];
        lineLayer.lineCap = kCALineCapRound;
        lineLayer.strokeColor = [UIColor whiteColor].CGColor;
        lineLayer.lineDashPattern = @[@1,@5];
        lineLayer.lineWidth = 2;
        lineLayer.fillColor = [UIColor clearColor].CGColor;
        lineLayer.path = path.CGPath;
        [calayer addSublayer:lineLayer];
        
        return calayer;
    }
    
    - (void)addYellowCarPathAnimation
    {
        CALayer *carLayer = [CALayer layer];
        carLayer.frame = CGRectMake(0, 0, 17, 11);
        [carLayer setAffineTransform:CGAffineTransformTranslate(carLayer.affineTransform, 0, -7)];
        carLayer.contents = (__bridge id)[UIImage imageNamed:@"6"].CGImage;
        CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        animation.path = [_yellowPath path];
        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        animation.duration = 6;
        animation.repeatCount = MAXFLOAT;
        animation.autoreverses = NO;
        animation.calculationMode = kCAAnimationCubicPaced;
        animation.rotationMode = kCAAnimationRotateAuto;
        [_yellowPath addSublayer:carLayer];
        [carLayer addAnimation:animation forKey:nil];
    }
    
    - (void)addGreenCarPathAnimationWithSize:(CGSize)size
    {
        CALayer *carLayer = [CALayer layer];
        carLayer.frame = CGRectMake(0, 0, 17, 11);
        carLayer.contents = (__bridge id)[UIImage imageNamed:@"1"].CGImage;
        UIBezierPath *path = [[UIBezierPath alloc] init];
        path.lineCapStyle = kCGLineCapRound;
        path.lineJoinStyle = kCGLineJoinRound;
        [path moveToPoint:CGPointMake(size.width + 10, size.height - 7)];
        [path addLineToPoint:CGPointMake(size.width + 10, size.height - 77)];
        [path addQuadCurveToPoint:CGPointMake(size.width / 1.8, size.height - 77) controlPoint:CGPointMake(size.width - 120, 193)];
        [path addArcWithCenter:CGPointMake(size.width / 1.9, size.height - 140) radius:63 startAngle:0.5 * M_PI endAngle:2.5 * M_PI clockwise:YES];
        [path addCurveToPoint:CGPointMake(0, size.height - 107) controlPoint1:CGPointMake(size.width / 1.8 - 60, size.height - 67) controlPoint2:CGPointMake(150, size.height / 2.3)];
        [path addLineToPoint:CGPointMake(-100, size.height + 7)];
        
        CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        animation.path = path.CGPath;
        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        animation.duration = 6;
        animation.repeatCount = MAXFLOAT;
        animation.autoreverses = NO;
        animation.calculationMode = kCAAnimationCubicPaced;
        animation.rotationMode = kCAAnimationRotateAuto;
        [self.view.layer addSublayer:carLayer];
        
        [carLayer addAnimation:animation forKey:nil];
    }
    
    - (CALayer *)addCloudAnimationWithSize:(CGSize)size
    {
        CALayer *cloudLayer = [CALayer layer];
        cloudLayer.contents = (__bridge id)[UIImage imageNamed:@"5"].CGImage;
        cloudLayer.frame = CGRectMake(0, 0, 63, 20);
        [self.view.layer addSublayer:cloudLayer];
        
        UIBezierPath *path = [[UIBezierPath alloc] init];
        [path moveToPoint:CGPointMake(size.width + 63, 40)];
        [path addLineToPoint:CGPointMake(- 63, 40)];
        
        CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        animation.path = path.CGPath;
        animation.duration = 40;
        animation.repeatCount = MAXFLOAT;
        animation.autoreverses = NO;
        animation.calculationMode = kCAAnimationCubicPaced;
        [cloudLayer addAnimation:animation forKey:nil];
        
        return cloudLayer;
    }
    
    - (void)addTreeLayeWithSize:(CGSize)size
    {
        for (int i = 0; i < 7; i ++) {
            CALayer *treeOne = [CALayer layer];
            treeOne.contents = (__bridge id)[UIImage imageNamed:@"4"].CGImage;
            treeOne.frame = CGRectMake([@[@5,@55,@70,@(size.width / 3 + 15),@(size.width / 3 + 25), @(size.width - 130),@(size.width - 160)][i] floatValue], size.height - 43, 13, 23);
            [self.view.layer addSublayer:treeOne];
        }
        for (int i = 0; i < 4; i ++) {
            CALayer *treeOne = [CALayer layer];
            treeOne.contents = (__bridge id)[UIImage imageNamed:@"4"].CGImage;
            treeOne.frame = CGRectMake([@[@10,@60,@(size.width / 3),@(size.width - 150)][i] floatValue], size.height - 52, 18, 32);
            [self.view.layer addSublayer:treeOne];
        }
        for (int i = 0; i < 2; i ++) {
            CALayer *treeOne = [CALayer layer];
            treeOne.contents = (__bridge id)[UIImage imageNamed:@"4"].CGImage;
            treeOne.frame = CGRectMake([@[@(size.width - 210),@(size.width - 50)][i] floatValue], [@[@(size.height - 75),@(size.height - 80)][i] floatValue], 18, 32);
            [self.view.layer addSublayer:treeOne];
        }
        for (int i = 0; i < 3; i ++) {
            CALayer *treeOne = [CALayer layer];
            treeOne.contents = (__bridge id)[UIImage imageNamed:@"4"].CGImage;
            treeOne.frame = CGRectMake([@[@(size.width - 235),@(size.width - 220), @(size.width - 40)][i] floatValue], [@[@(size.height - 67),@(size.height - 67),@(size.height - 72)][i] floatValue], 13, 23);
            [self.view.layer addSublayer:treeOne];
        }
    }
    

    如果有人想要swift版本的,这里留下原作者的博客地址。需要详细了解的小伙伴可以看这里。

    原作者博客点这里

    相关文章

      网友评论

          本文标题:OC实现有趣的过山车动画

          本文链接:https://www.haomeiwen.com/subject/ntxgyttx.html