zoukankan      html  css  js  c++  java
  • CAShapeLayer(UIBezierPath)、CAGradientLayer绘制动态小车

    看到一个大神写的代码,引用过来让大家看看!

    //  1、CAShapeLayer是一种特殊的层,可以在上面渲染图形。

    //  2、CAShapeLayer继承自CALayer,可使用CALayer的所有属性。

    //  3、CAShapeLayer需要和贝塞尔曲线配合使用才有意义,贝塞尔曲线为其提供渲染的图形。

    //  4、使用CAShapeLayer与贝塞尔曲线可以实现不再view的drawRect方法中画出一些想要的图形。

    //  关于CAShapeLayer和drawRect的比较:

    //  在drawRect中绘制图形调用CoreGraphics框架中得方法,占用CPU,消耗性能大;

    //  CAShapeLayer属于CoreAnimation框架,通过GPU来渲染图形,节省性能。动画渲染直接提交给GPU,不消耗内存。

    //  1、CAGradientLayer是一种特殊的层,用于渲染渐变效果。

    //  2、CAGradientLayer继承自CALayer,可使用CALayer所有的属性。

    //  3、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
  • 相关阅读:
    第6 章 : 应用编排与管理:Deployment
    第5 章 : 应用编排与管理:核心原理
    第4 章 : 理解 Pod 和容器设计模式
    第3 章 : Kubernetes 核心概念
    第2 章 : 容器基本概念
    第1 章 : 第一堂“云原生”课
    阿里云原生技术公开课程-讲师记录及视频链接
    Shell中的(),{}几种语法用法-单独总结
    折腾kubernetes各种问题汇总-<1>
    Kubernetes中Deployment部署故障排除
  • 原文地址:https://www.cnblogs.com/fengmin/p/8134722.html
Copyright © 2011-2022 走看看