CoreGraphicsAndAnimations(2)
发表于|更新于
|阅读量:
绘图(2)
图层合并及Flower生长动画
图层合并
图层的合并说起来很高大上,其实实际上在IOS里面实现非常的简单,实现原理就是,先获取当前的图片绘图上下文,将当前view的layer渲染到当前的绘图上下文,之后从当前的上下文中获取图片,最后结束当前上下文。返回获取到的图片
可能看代码更直观些。
注:这是一个view的category方法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | - (UIImage *)cy_compositedView{
 UIGraphicsBeginImageContext(self.bounds.size);
 [self.layer renderInContext:UIGraphicsGetCurrentContext()];
 UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
 
 return compositedImage;
 
 }
 
 | 
Flower 生长动画
flower的生长主要注意的地方就俩点,一个是花的位置安排,一个是花的生长动画。位置的话,需要注意的是我们希望看起来大的花和位置靠下的花,尽量能往前放。这样会有一些层次感。生长动画的话就是希望锚点能在根部,这样会有一种长起来的感觉。
还有需要注意的就是,我们这里只使用了一个Flower类,然后用这个类去生成了🌺的图片。然后采用初始化UIImageView贴图片的方式创建60朵🌺,这样能大大的节省内存空间。
代码如下
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 
 | - (void)addFlowers{
 CGFloat height = self.view.bounds.size.height;
 CGFloat width = self.view.bounds.size.width;
 
 
 CGFloat flowerHeight = height / 6;
 
 
 CGFloat flowerY = height * 0.3;
 
 
 CYFlower *flower = [[CYFlower alloc]initWithFrame:CGRectMake(0, 0, flowerHeight * .7, flowerHeight)];
 
 
 UIImage *floweimg = [flower cy_compositedView];
 
 
 for (int i = 0; i < 60; i ++) {
 int size = height / 12.f;
 CGFloat flowersize =( arc4random() % (int) size) + size;
 
 UIImageView *flowerImgView = [[UIImageView alloc]initWithFrame:CGRectMake(arc4random()%(int)width * 0.9, arc4random() % (int)flowerY + 2 * flowerY, flowersize * .7, flowersize)];
 flowerImgView.image = floweimg;
 
 
 flowerImgView.layer.zPosition = flowerImgView.frame.origin.y + flowersize;
 [self.view addSubview:flowerImgView];
 
 [self growFlowers:flowerImgView duration:arc4random() % 100 / 25 + 4];
 }
 
 }
 
 - (void)growFlowers:(UIImageView *)flowers duration:(CGFloat)duration
 {
 
 flowers.layer.anchorPoint = CGPointMake(.5, 1);
 
 
 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
 animation.duration = duration;
 animation.fromValue = [NSValue valueWithCGSize:CGSizeMake(0, 0)];
 animation.toValue = [NSValue valueWithCGSize:CGSizeMake(1, 1)];
 [flowers.layer addAnimation:animation forKey:@"grow"];
 }
 
 | 
下面附上生长动画

利用梯度layer实现太阳升起的动画
ios里面有一个layer叫CAGradientLayer,这个layer支持线性的梯度,最为关键的是,它有一个colors属性支持动画效果。我们只需要给这个colors数组里面填充颜色对象就可以了,另外我们还希望能够从起点到结束点有个过度。那么需要对startPoint属性做动画。
最后,不希望视图生成的时候就开始动画,而是在视图贴到父视图上的时候才开始动画,那么layer的动画就要加在-(void)didMoveToSuperView方法上。以下是具体实现代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 
 | + (Class)layerClass{
 return [CAGradientLayer class];
 }
 
 - (void)didMoveToSuperview
 {
 self.backgroundColor = [UIColor blackColor];
 CGColorRef blackcolor = [[UIColor blackColor]CGColor];
 UIColor *color1 = [UIColor colorWithRed:0.01 green:0.2 blue:0.8 alpha:1];
 UIColor *color2 = [UIColor colorWithRed:1 green:0.5 blue:0 alpha:1];
 UIColor *color3 = [UIColor colorWithRed:.35 green:.74 blue:.11 alpha:1];
 NSArray *colors = [NSArray arrayWithObjects:(id)[color1 CGColor],
 [color2 CGColor],
 [color3 CGColor],
 nil];
 NSNumber *location1 = [NSNumber numberWithFloat:.0];
 NSNumber *location2 = [NSNumber numberWithFloat:.4];
 NSNumber *location3 = [NSNumber numberWithFloat:.9];
 
 CAGradientLayer *layer = (CAGradientLayer *)[self layer];
 layer.colors = colors;
 layer.locations = @[location1,location2,location3];
 layer.startPoint = CGPointMake(.5, 0);
 layer.endPoint = CGPointMake(.5, 1);
 
 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"startPoint"];
 animation.duration = 6.f;
 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
 animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(.5, 1)];
 
 [layer addAnimation:animation forKey:@"start"];
 
 animation.keyPath = @"colors";
 animation.duration = 6.f;
 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
 animation.fromValue = [NSArray arrayWithObjects:(__bridge id)blackcolor, blackcolor, blackcolor, nil];
 
 [layer addAnimation:animation forKey:@"color"];
 }
 @end
 
 | 
老规矩附上一张实现的效果图

云彩飘动
接下来,我们希望实现的是开篇的时候,展示的云朵从屏幕左侧飘移到屏幕右侧的动画,这里有几个需要注意的点
实现方式的话,这里采用UIBezierPath绘制轨迹,然后内部采用梯度进行填充。云朵的投影采用云朵一半的高度绘制,然后将其设置为云朵的ShadowPath,然后利用shadowoffset属性将云朵投射到足够远的距离,形成一种投影的效果。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 
 | - (void)drawRect:(CGRect)rect{
 CGFloat height = self.bounds.size.height;
 
 UIBezierPath *path = [self createCloudPathWithHeight:height];
 [path addClip];
 
 CGGradientRef gradientRef = [self gradientWithColor:self.innerColor toColor:self.outerColor count:2];
 CGContextRef context = UIGraphicsGetCurrentContext();
 CGPoint startPoint = CGPointMake(0, 0);
 CGPoint endPoint = CGPointMake(0, height);
 CGContextDrawLinearGradient(context, gradientRef, startPoint, endPoint, 0);
 
 path.lineWidth = self.lineThickness;
 [self.strokeColor setStroke];
 [path stroke];
 
 UIBezierPath *shadowPath = [self createCloudPathWithHeight:height / 2];
 self.layer.shadowPath = shadowPath.CGPath;
 if (!self.distance) {
 self.distance = height * 1.8;
 }
 self.layer.shadowOffset = CGSizeMake(0, self.distance);
 
 self.layer.shadowOpacity = 0.4f;
 
 self.alpha = 0.9f;
 
 }
 
 - (UIBezierPath *)createCloudPathWithHeight:(CGFloat)height
 {
 CGFloat width = self.bounds.size.width;
 
 CGFloat points[] = {
 0.4,0.2,
 0.5,0.1,0.6,0.2,
 0.8,0.2,0.8,0.4,
 0.9,0.5,0.8,0.6,
 0.8,0.8,0.6,0.8,
 0.5,0.9,0.4,0.8,
 0.2,0.8,0.2,0.6,
 0.1,0.5,0.2,0.4,
 0.2,0.2,0.4,0.2,
 };
 
 CGPoint cPoint;
 CGPoint point;
 
 UIBezierPath *path = [UIBezierPath bezierPath];
 [path moveToPoint:CGPointMake(points[0] * width, points[1] * height)];
 
 for (int i = 2; i < sizeof(points) / sizeof(CGFloat); i += 4) {
 cPoint = CGPointMake(points[i] * width, points[i + 1] * height);
 point  = CGPointMake(points[i + 2] * width, points[i + 3] * height);
 [path addQuadCurveToPoint:point controlPoint:cPoint];
 }
 
 [path closePath];
 
 return path;
 }
 
 | 
实现的效果图如下

海鸥
进行到现在,跟成品图中相差的也就剩下最后一步了,那就是海鸥的绘制,海鸥这里同样还是采用Bezier曲线绘制,不同的是,我们要采用onscreen渲染的方式,绘制出多张海鸥的image,最后将image组合成一个数组,然后用UIImageView的images数组进行展示。
以下是具体绘制代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 
 | - (void)didMoveToSuperview{
 if (!self.animationImages) {
 self.animationImages = [self arrayofImages];
 }
 }
 
 - (NSArray *)arrayofImages
 {
 NSMutableArray *arr = [NSMutableArray arrayWithCapacity:COUNT];
 for (CGFloat i = LOWWING; i < HEIGING; i+=STEP) {
 [arr addObject:[self animationFrame:i]];
 }
 
 for (CGFloat i = HEIGING; i > LOWWING; i -= STEP) {
 [arr addObject:[self animationFrame:i]];
 }
 
 return arr;
 }
 
 
 - (UIImage *)animationFrame:(CGFloat)frame
 {
 CGFloat width = self.bounds.size.width;
 CGFloat height = self.bounds.size.height;
 
 UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0);
 
 UIBezierPath *path = [UIBezierPath bezierPath];
 [path moveToPoint:CGPointMake(0, frame)];
 [path addQuadCurveToPoint:CGPointMake(.5, 0.6 - frame / 3) controlPoint:CGPointMake(.25, .25)];
 [path addQuadCurveToPoint:CGPointMake(1, frame) controlPoint:CGPointMake(.75, .25)];
 
 [path applyTransform:CGAffineTransformMakeScale(width, height)];
 path.lineWidth = height / 30;
 [path stroke];
 
 UIImage *imge = UIGraphicsGetImageFromCurrentImageContext();
 
 UIGraphicsEndImageContext();
 
 return imge;
 }
 
 | 
效果图

小结
通过学习绘制图形以及给layer做动画,基本掌握了一些常用的绘制函数和动画实现方法。发现CoreGraphics确实是一个强大的图形绘制库,只要你脑洞大开就能绘制你想到的任意图形,此外图层合并能够大大节省内存空间,如果是静态的多个图层的view。可以考虑做图层渲染,渲染成图片。
附录
最后附上本项目在git上的demo,感兴趣的朋友可以下载下来看看,欢迎star。