CoreGraphicsAndAnimations(2)
发表于|更新于
|阅读量:
绘图(2)
图层合并及Flower生长动画
图层合并
图层的合并说起来很高大上,其实实际上在IOS里面实现非常的简单,实现原理就是,先获取当前的图片绘图上下文,将当前view的layer渲染到当前的绘图上下文,之后从当前的上下文中获取图片,最后结束当前上下文。返回获取到的图片
可能看代码更直观些。
注:这是一个view的category方法
1 2 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朵🌺,这样能大大的节省内存空间。
代码如下
1 2 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方法上。以下是具体实现代码
1 2 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属性将云朵投射到足够远的距离,形成一种投影的效果。
1 2 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数组进行展示。
以下是具体绘制代码
1 2 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。