CAShapeLayer(UIBezierPath)、CAGradientLayer繪製動態小車

看到一個大神寫的代碼,引用過來讓你們看看!框架

//  一、CAShapeLayer是一種特殊的層,能夠在上面渲染圖形。dom

//  二、CAShapeLayer繼承自CALayer,可以使用CALayer的全部屬性。性能

//  三、CAShapeLayer須要和貝塞爾曲線配合使用纔有意義,貝塞爾曲線爲其提供渲染的圖形。動畫

//  四、使用CAShapeLayer與貝塞爾曲線能夠實現再也不view的drawRect方法中畫出一些想要的圖形。ui

 

//  關於CAShapeLayer和drawRect的比較:atom

//  在drawRect中繪製圖形調用CoreGraphics框架中得方法,佔用CPU,消耗性能大;spa

//  CAShapeLayer屬於CoreAnimation框架,經過GPU來渲染圖形,節省性能。動畫渲染直接提交給GPU,不消耗內存。code

 

//  一、CAGradientLayer是一種特殊的層,用於渲染漸變效果。orm

//  二、CAGradientLayer繼承自CALayer,可以使用CALayer全部的屬性。blog

//  三、CAGradientLayer是除了在圖形上下文繪製漸變效果外的另外一種方法,它不須要藉助圖形上下文,是直接渲染在層上的,所以易於使用。

 

#import "ViewController.h"

#define k_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
#define k_LAND_BEGIN_HEIGHT k_SCREEN_HEIGHT - 20
#define k_SIZE self.view.frame.size

@interface ViewController ()

@property(strong,nonatomic)CALayer * landLayer;
@property(strong,nonatomic)CAShapeLayer * greenTrack;
@property(strong,nonatomic)CAShapeLayer * yellowTrack;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor lightGrayColor];
    
    //  初始化背景漸變的天空
    [self initBackgroundSky];
    //  初始化雪山
    [self initSnowberg];
    //  加載草坪
    [self initLawn];
    //  加載大地
    [self initLand];
    //  加載黃色軌道
    [self initYellowTrack];
    //  加載綠色軌道
    [self initGreenTrack];
    //  點綴小樹
    [self initTree];
    
    //  啓動雲彩
    [self initCloudAnimation];
    //  添加黃色軌道小車動畫   CACurrentMediaTime系統內建時間
    [self carAnimationWith:@"car" TrackLayer:_yellowTrack AnimationDuration:8.0 BeginTime:CACurrentMediaTime()];
    //  添加綠色軌道小車動畫
    [self carAnimationWith:@"otherCar" TrackLayer:_greenTrack AnimationDuration:5.0 BeginTime:CACurrentMediaTime()];
}

//  初始化背景天空漸變色
- (void)initBackgroundSky{
    CAGradientLayer * backgroundLayer = [[CAGradientLayer alloc] init];
    //  設置背景漸變色層的大小。要減去屏幕最下方土地那條水平線的高度
    backgroundLayer.frame = CGRectMake(0, 0, k_SIZE.width, k_LAND_BEGIN_HEIGHT);
    
    UIColor * lightColor = [UIColor colorWithRed:40.0 / 255.0 green:150.0 / 255.0 blue:200.0 / 255.0 alpha:1.0];
    UIColor * darkColor  = [UIColor colorWithRed:255.0 / 255.0 green:250.0 / 255.0 blue:250.0 / 255.0 alpha:1.0];
    backgroundLayer.colors = @[(__bridge id)lightColor.CGColor,(__bridge id)darkColor.CGColor];
    
    //  讓變色層成45度角變色
    backgroundLayer.startPoint = CGPointMake(0, 0);
    backgroundLayer.endPoint = CGPointMake(1, 1);
    
    [self.view.layer addSublayer:backgroundLayer];
}


//初始化雪山,有兩個雪山
- (void)initSnowberg {
    
    //  左邊第一座山頂,其實就是一個白色的三角形
    CAShapeLayer * leftSnowberg     = [[CAShapeLayer alloc] init];
    UIBezierPath * leftSnowbergPath = [[UIBezierPath alloc] init];
    
    //  把bezierpath的起點移動到雪山左下角
    [leftSnowbergPath moveToPoint:CGPointMake(0, k_SIZE.height - 120)];
    
    //  畫一條線到山頂
    [leftSnowbergPath addLineToPoint:CGPointMake(100, 100)];
    
    //  畫一條線到右下角->左下角->閉合
    [leftSnowbergPath addLineToPoint:CGPointMake(k_SIZE.width / 2, k_LAND_BEGIN_HEIGHT)];
    [leftSnowbergPath addLineToPoint:CGPointMake(0, k_LAND_BEGIN_HEIGHT)];
    [leftSnowbergPath closePath];
    
    leftSnowberg.path = leftSnowbergPath.CGPath;
    leftSnowberg.fillColor = [UIColor whiteColor].CGColor;
    [self.view.layer addSublayer:leftSnowberg];
    
    
    //  開始畫山體沒有被雪覆蓋的部分
    CAShapeLayer * leftSnowbergBody     = [[CAShapeLayer alloc] init];
    UIBezierPath * leftSnowbergBodyPath = [[UIBezierPath alloc] init];
    
    //  把bezierpath的起點移動到雪山左下角相同的位置
    CGPoint startPoint = CGPointMake(0, k_SIZE.height - 120);
    CGPoint endPoint = CGPointMake(100, 100);
    CGPoint firstPathPoint = [self calculateWithXValue:20 startPoint:startPoint endpoint:endPoint];
    [leftSnowbergBodyPath moveToPoint:startPoint];
    
    [leftSnowbergBodyPath addLineToPoint:firstPathPoint];
    [leftSnowbergBodyPath addLineToPoint:CGPointMake(60, firstPathPoint.y)];
    [leftSnowbergBodyPath addLineToPoint:CGPointMake(100, firstPathPoint.y + 30)];
    [leftSnowbergBodyPath addLineToPoint:CGPointMake(140, firstPathPoint.y)];
    [leftSnowbergBodyPath addLineToPoint:CGPointMake(180, firstPathPoint.y - 20)];
    
    CGPoint secondPathPoint = [self calculateWithXValue:(k_SIZE.width / 2 - 125) startPoint:endPoint endpoint:CGPointMake(k_SIZE.width / 2, k_LAND_BEGIN_HEIGHT)];
    [leftSnowbergBodyPath addLineToPoint:CGPointMake(secondPathPoint.x - 30, firstPathPoint.y)];
    
    [leftSnowbergBodyPath addLineToPoint:secondPathPoint];
    
    [leftSnowbergBodyPath addLineToPoint:CGPointMake(k_SIZE.width / 2, k_LAND_BEGIN_HEIGHT)];
    [leftSnowbergBodyPath addLineToPoint:CGPointMake(0, k_LAND_BEGIN_HEIGHT)];
    [leftSnowbergBodyPath closePath];
    
    leftSnowbergBody.path = leftSnowbergBodyPath.CGPath;
    UIColor * snowColor = [UIColor colorWithDisplayP3Red:139.0 /255.0 green:92.0 /255.0 blue:0.0 /255.0 alpha:1.0];
    leftSnowbergBody.fillColor = snowColor.CGColor;
    [self.view.layer addSublayer:leftSnowbergBody];
    
    
    //  中間的山
    CAShapeLayer * middleSnowberg     = [[CAShapeLayer alloc] init];
    UIBezierPath * middleSnowbergPath = [[UIBezierPath alloc] init];
    
    //  把bezierpath的起點移動到雪山左下角。而後畫一條線到山頂,再畫一條線到右下角,閉合。
    CGPoint middleStartPoint = CGPointMake(k_SIZE.width / 3, k_LAND_BEGIN_HEIGHT);
    CGPoint middleTopPoint = CGPointMake(k_SIZE.width /2, 200);
    CGPoint middleEndPoint = CGPointMake(k_SIZE.width / 1.2, k_LAND_BEGIN_HEIGHT);
    
    [middleSnowbergPath moveToPoint:middleStartPoint];
    [middleSnowbergPath addLineToPoint:middleTopPoint];
    [middleSnowbergPath addLineToPoint:middleEndPoint];
    
    [middleSnowbergPath closePath];
    
    middleSnowberg.path = middleSnowbergPath.CGPath;
    middleSnowberg.fillColor = [UIColor whiteColor].CGColor;
    [self.view.layer insertSublayer:middleSnowberg above:leftSnowbergBody];
    
    //  開始畫山體沒有被雪覆蓋的部分
    CAShapeLayer * middleSnowbergBody     = [[CAShapeLayer alloc] init];
    UIBezierPath * middleSnowbergBodyPath = [[UIBezierPath alloc] init];
    
    //  把bezierpath的起點移動到雪山左下角相同的位置
    [middleSnowbergBodyPath moveToPoint:middleStartPoint];
    
    CGPoint middleFirstPathPoint = [self calculateWithXValue:(middleStartPoint.x + 70) startPoint:middleStartPoint endpoint:middleTopPoint];
    
    [middleSnowbergBodyPath addLineToPoint:middleFirstPathPoint];
    [middleSnowbergBodyPath addLineToPoint:CGPointMake(middleFirstPathPoint.x + 20, middleFirstPathPoint.y)];
    [middleSnowbergBodyPath addLineToPoint:CGPointMake(middleFirstPathPoint.x + 50, middleFirstPathPoint.y + 30)];
    [middleSnowbergBodyPath addLineToPoint:CGPointMake(middleFirstPathPoint.x + 80, middleFirstPathPoint.y - 10)];
    [middleSnowbergBodyPath addLineToPoint:CGPointMake(middleFirstPathPoint.x + 120, middleFirstPathPoint.y + 20)];
    
    CGPoint middleSecondPathPoint = [self calculateWithXValue:(middleEndPoint.x - 120) startPoint:middleTopPoint endpoint:middleEndPoint];
    
    [middleSnowbergBodyPath addLineToPoint:CGPointMake(middleSecondPathPoint.x - 30, middleSecondPathPoint.y)];
    [middleSnowbergBodyPath addLineToPoint:middleSecondPathPoint];
    
    [middleSnowbergBodyPath addLineToPoint:middleEndPoint];
    
    [middleSnowbergBodyPath closePath];
    
    middleSnowbergBody.path = middleSnowbergBodyPath.CGPath;
    UIColor * middleSnowColor = [UIColor colorWithDisplayP3Red:125.0 /255.0 green:87.0 /255.0 blue:7.0 /255.0 alpha:1.0];
    middleSnowbergBody.fillColor = middleSnowColor.CGColor;
    [self.view.layer insertSublayer:middleSnowbergBody above:middleSnowberg];
    
}

//根據起始點,算出指定的x在這條線段上對應的y。返回這個point。知道兩點,根據兩點座標,求出兩點連線的斜率。y=kx+b求出點座標。
- (CGPoint)calculateWithXValue:(CGFloat)xvalue startPoint:(CGPoint)startPoint endpoint:(CGPoint)endpoint{
    //  求出兩點連線的斜率
    CGFloat k = (endpoint.y - startPoint.y) / (endpoint.x - startPoint.x);
    CGFloat b = startPoint.y - startPoint.x * k;
    CGFloat yvalue = k * xvalue + b;
    return CGPointMake(xvalue, yvalue);
}

//初始化草坪
- (void)initLawn {
    CAShapeLayer * leftLawn     = [[CAShapeLayer alloc] init];
    UIBezierPath * leftLawnPath = [[UIBezierPath alloc] init];
    
    CGPoint leftStartPoint = CGPointMake(0, k_LAND_BEGIN_HEIGHT);
    [leftLawnPath moveToPoint:leftStartPoint];
    [leftLawnPath addLineToPoint:CGPointMake(0, k_SIZE.height - 100)];
    
    //  畫一個二次貝塞爾曲線
    [leftLawnPath addQuadCurveToPoint:CGPointMake(k_SIZE.width / 3.0, k_LAND_BEGIN_HEIGHT) controlPoint:CGPointMake(k_SIZE.width / 5.0, k_SIZE.height - 100)];
    
    leftLawn.path = leftLawnPath.CGPath;
    leftLawn.fillColor = [UIColor colorWithDisplayP3Red:82.0 / 255.0 green:177.0 / 255.0 blue:52.0 / 255.0 alpha:1.0].CGColor;
    [self.view.layer addSublayer:leftLawn];
    
    CAShapeLayer * rightLawn     = [[CAShapeLayer alloc] init];
    UIBezierPath * rightLawnPath = [[UIBezierPath alloc] init];
    
    [rightLawnPath moveToPoint:leftStartPoint];
    //  畫一個二次貝塞爾曲線
    [rightLawnPath addQuadCurveToPoint:CGPointMake(k_SIZE.width, k_SIZE.height - 80) controlPoint:CGPointMake(k_SIZE.width / 2.0, k_SIZE.height - 100)];
    [rightLawnPath addLineToPoint:CGPointMake(k_SIZE.width, k_LAND_BEGIN_HEIGHT)];
    
    rightLawn.path = rightLawnPath.CGPath;
    rightLawn.fillColor = [UIColor colorWithDisplayP3Red:92.0/255.0 green:195.0/255.0 blue:52.0/255.0 alpha:1.0].CGColor;
    [self.view.layer insertSublayer:rightLawn above:leftLawn];
}

//初始化土地
- (void)initLand {
    _landLayer = [[CALayer alloc] init];
    _landLayer.frame = CGRectMake(0, k_LAND_BEGIN_HEIGHT, k_SIZE.width, 20);
    _landLayer.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ground"]].CGColor;
    [self.view.layer addSublayer:_landLayer];
}

//初始化黃色軌道
- (void)initYellowTrack {
    _yellowTrack = [[CAShapeLayer alloc] init];
    _yellowTrack.lineWidth = 5;
    _yellowTrack.strokeColor = [UIColor colorWithDisplayP3Red:210.0 / 255.0 green:179.0 / 255.0 blue:54.0 / 255.0 alpha:1.0].CGColor;
    
    UIBezierPath * trackPath = [[UIBezierPath alloc] init];
    //  畫一個三次貝塞爾曲線 + 一個二次貝塞爾曲線
    //  左側兩個拐彎的三次貝塞爾曲線
    [trackPath moveToPoint:CGPointMake(0, k_SIZE.height - 60)];
    [trackPath addCurveToPoint:CGPointMake(k_SIZE.width / 1.5, k_SIZE.height / 2.0 - 20) controlPoint1:CGPointMake(k_SIZE.width / 6.0, k_SIZE.height - 200) controlPoint2:CGPointMake(k_SIZE.width / 3.0, k_SIZE.height + 50)];
    //  右側一個彎度的二次貝塞爾曲線
    [trackPath addQuadCurveToPoint:CGPointMake(k_SIZE.width + 50, k_SIZE.height / 3.0) controlPoint:CGPointMake(k_SIZE.width - 100, 50)];

    [trackPath addLineToPoint:CGPointMake(k_SIZE.width + 10, k_SIZE.height + 10)];
    [trackPath addLineToPoint:CGPointMake(0, k_SIZE.height + 10)];

    _yellowTrack.fillColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"yellow"]].CGColor;
    _yellowTrack.path = trackPath.CGPath;
    [self.view.layer insertSublayer:_yellowTrack below:_landLayer];
    
    //  爲了可以讓弧線更好看一點,須要加入鏤空的虛線
    CAShapeLayer * trackLine = [[CAShapeLayer alloc] init];
    trackLine.lineCap = kCALineCapRound;
    trackLine.strokeColor = [UIColor whiteColor].CGColor;
    
    trackLine.lineDashPattern = @[@1.0,@6.0];  //寬度,間距
    trackLine.lineWidth = 2.5;
    trackLine.fillColor = [UIColor clearColor].CGColor;
    trackLine.path = trackPath.CGPath;
    [_yellowTrack addSublayer:trackLine];
}

//  初始化綠色軌道
- (void)initGreenTrack {
    _greenTrack = [[CAShapeLayer alloc] init];
    _greenTrack.lineWidth = 5;
    _greenTrack.strokeColor = [UIColor colorWithDisplayP3Red:0.0 / 255.0 green:147.0 / 255.0 blue:163.0 /255.0  alpha:1.0].CGColor;
    
    //  綠色鐵軌的火車從右側進入,因此從右側開始繪畫。須要畫三條曲線,右邊一條 + 中間的圓圈 + 左邊一條
    UIBezierPath * path = [[UIBezierPath alloc] init];
    [path moveToPoint:CGPointMake(k_SIZE.width + 10, k_LAND_BEGIN_HEIGHT)];
    [path addLineToPoint:CGPointMake(k_SIZE.width + 10, k_SIZE.height - 70)];
    [path addQuadCurveToPoint:CGPointMake(k_SIZE.width / 1.5, k_SIZE.height - 70) controlPoint:CGPointMake(k_SIZE.width - 150, 200)];
    
    //  畫圓圈
    [path addArcWithCenter:CGPointMake(k_SIZE.width / 1.6, k_SIZE.height - 140) radius:70 startAngle:M_PI_2 endAngle:2.5 * M_PI clockwise:YES];
    
    [path addCurveToPoint:CGPointMake(0, k_SIZE.height - 100) controlPoint1:CGPointMake(k_SIZE.width / 1.8 - 60, k_SIZE.height - 60) controlPoint2:CGPointMake(150, k_SIZE.height / 2.3)];
    
    [path addLineToPoint:CGPointMake(- 10, k_LAND_BEGIN_HEIGHT)];
    _greenTrack.path = path.CGPath;
    _greenTrack.fillColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"green"]].CGColor;
    [self.view.layer addSublayer:_greenTrack];
    
    //  爲了可以讓弧線更好看一點,須要加入鏤空的虛線
    CAShapeLayer * trackLine = [[CAShapeLayer alloc] init];
    trackLine.lineCap = kCALineCapRound;
    trackLine.strokeColor = [UIColor whiteColor].CGColor;
    
    trackLine.lineDashPattern = @[@1.0,@6.0];
    trackLine.lineWidth = 2.5;
    trackLine.fillColor = [UIColor clearColor].CGColor;
    trackLine.path = path.CGPath;
    [_greenTrack addSublayer:trackLine];
}

//  添加點綴的小樹
- (void)initTree {
    [self addTreesWithNumber:7 treeFrame:CGRectMake(0, k_LAND_BEGIN_HEIGHT - 20, 13, 23)];
    [self addTreesWithNumber:7 treeFrame:CGRectMake(0, k_LAND_BEGIN_HEIGHT - 64, 18, 32)];
    [self addTreesWithNumber:5 treeFrame:CGRectMake(0, k_LAND_BEGIN_HEIGHT - 90, 13, 23)];
}

//  添加小樹
- (void)addTreesWithNumber:(NSInteger)treesNumber treeFrame:(CGRect)frame {
    UIImage * tree = [UIImage imageNamed:@"tree"];
    for (NSInteger i = 0; i < treesNumber + 1; i++) {
        CALayer * treeLayer = [[CALayer alloc] init];
        treeLayer.contents = (__bridge id _Nullable)(tree.CGImage);
        treeLayer.frame = CGRectMake(k_SIZE.width - 50 * i * (arc4random_uniform(4) + 1), frame.origin.y, frame.size.width, frame.size.height);
        [self.view.layer insertSublayer:treeLayer above:_greenTrack];
    }
}

//  雲彩的動畫
- (void)initCloudAnimation {
    CALayer * cloud = [[CALayer alloc]init];
    cloud.contents = (__bridge id _Nullable)([UIImage imageNamed:@"cloud"].CGImage);
    cloud.frame = CGRectMake(0, 0, 63, 20);
    [self.view.layer addSublayer:cloud];
    
    UIBezierPath * cloudPath = [[UIBezierPath alloc] init];
    [cloudPath moveToPoint:CGPointMake(k_SIZE.width + 63, 50)];
    [cloudPath addLineToPoint:CGPointMake(- 63, 50)];

    CAKeyframeAnimation * ani = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    ani.path = cloudPath.CGPath;
    ani.duration = 30;
    ani.autoreverses = NO;
    ani.repeatCount = CGFLOAT_MAX;
    /*  kCAAnimationLinear calculationMode的默認值,表示當關鍵幀爲座標點的時候,關鍵幀之間直接直線相連進行插值計算
     *  kCAAnimationDiscrete 離散的,就是不進行插值計算,全部關鍵幀直接逐個進行顯示
     *  kCAAnimationPaced 使得動畫均勻進行,而不是按keyTimes設置的或者按關鍵幀平分時間,此時keyTimes和timingFunctions無效
     *  kCAAnimationCubic 對關鍵幀爲座標點的關鍵幀進行圓滑曲線相連後插值計算,對於曲線的形狀還能夠經過tensionValues,continuityValues,biasValues來進行調整自定義,這裏的數學原理是Kochanek–Bartels spline,這裏的主要目的是使得運行的軌跡變得圓滑;
     *  kCAAnimationCubicPaced 看這個名字就知道和kCAAnimationCubic有必定聯繫,其實就是在kCAAnimationCubic的基礎上使得動畫運行變得均勻,就是系統時間內運動的距離相同,此時keyTimes以及timingFunctions也是無效的.
     */
    ani.calculationMode = kCAAnimationPaced;
    
    [cloud addAnimation:ani forKey:@"position"];
}

//抽取過山車的動畫
- (CAKeyframeAnimation *)carAnimationWith:(NSString *)carImageName TrackLayer:(CAShapeLayer *)track AnimationDuration:(CFTimeInterval)duration BeginTime:(CFTimeInterval)beginTime {
    
    CALayer * car = [[CALayer alloc] init];
    car.frame = CGRectMake(0, 0, 22, 15);
    car.contents = (__bridge id _Nullable)([UIImage imageNamed:carImageName].CGImage);
    
    CAKeyframeAnimation * ani = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    ani.path = track.path;
    
    ani.duration = duration;
    ani.beginTime = beginTime;
    ani.autoreverses = NO;
    ani.repeatCount = CGFLOAT_MAX;
    ani.calculationMode = kCAAnimationPaced;
    ani.rotationMode = kCAAnimationRotateAuto;
    
    [track addSublayer:car];
    [car addAnimation:ani forKey:@"carAni"];
    
    return ani;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end
相關文章
相關標籤/搜索