在AppStore中的應(yīng)用越來越重視動(dòng)畫效果的使用,一個(gè)良好動(dòng)畫效果可以讓兩個(gè)狀態(tài)之間平滑地過度,也可以利用動(dòng)畫吸引住用戶的眼球,今天我們就來實(shí)現(xiàn)一個(gè)好玩的動(dòng)畫(破碎動(dòng)畫)!
下面我們來看一下效果圖:
(__) 嘻嘻…… 好玩么,下面我們就來實(shí)現(xiàn)了哦!
首先我們要?jiǎng)?chuàng)建一個(gè)UIView的擴(kuò)展, 我把它起名叫做UIView + Boom:
#import "UIView+Boom.h"
const static NSInteger cellCount = 17;
static NSMutableArray<CALayer *> *booms;
@implementation UIView (Boom)
+(void)load{
booms = [NSMutableArray<CALayer *> array];
}
-(void)boom{
[booms removeAllObjects];
for(int i = 0 ; i < cellCount ; i++){
for(int j = 0 ; j < cellCount ; j++){
CGFloat pWidth = MIN(self.frame.size.width, self.frame.size.height)/cellCount;
CALayer *boomCell = [CALayer layer];
boomCell.backgroundColor = [self getPixelColorAtLocation:CGPointMake(i*2, j*2)].CGColor;
boomCell.cornerRadius = pWidth/2;
boomCell.frame = CGRectMake(i*pWidth, j*pWidth, pWidth, pWidth);
[self.layer.superlayer addSublayer:boomCell];
[booms addObject:boomCell];
}
}
//粉碎動(dòng)畫
[self cellAnimation];
//縮放消失
[self scaleOpacityAnimations];
}
- (void)cellAnimation {
for(CALayer *cell in booms){
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyframeAnimation.path = [self makeRandomPath:cell].CGPath;
keyframeAnimation.fillMode = kCAFillModeForwards;
keyframeAnimation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
keyframeAnimation.duration = (random()%10) * 0.05 + 0.3;
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scale.toValue = @([self makeScaleValue]);
scale.duration = keyframeAnimation.duration;
scale.removedOnCompletion = NO;
scale.fillMode = kCAFillModeForwards;
CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@"opacity"];
opacity.fromValue = @1;
opacity.toValue = @0;
opacity.duration = keyframeAnimation.duration;
opacity.removedOnCompletion = NO;
opacity.fillMode = kCAFillModeForwards;
opacity.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
animationGroup.duration = keyframeAnimation.duration;
animationGroup.removedOnCompletion = NO;
animationGroup.fillMode = kCAFillModeForwards;
animationGroup.animations = @[keyframeAnimation,scale,opacity];
animationGroup.delegate = self;
[cell addAnimation:animationGroup forKey: @"moveAnimation"];
}
}
/**
* 繪制粉碎路徑
*/
- (UIBezierPath *) makeRandomPath: (CALayer *) alayer {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:self.layer.position];
CGFloat left = -self.layer.frame.size.width * 1.3;
CGFloat maxOffset = 2 * fabs(left);
CGFloat randomNumber = random()%101;
CGFloat endPointX = self.layer.position.x + left + (randomNumber/100) * maxOffset;
CGFloat controlPointOffSetX = self.layer.position.x + (endPointX-self.layer.position.x)/2;
CGFloat controlPointOffSetY = self.layer.position.y - 0.2 * self.layer.frame.size.height - random()%(int)(1.2 * self.layer.frame.size.height);
CGFloat endPointY = self.layer.position.y + self.layer.frame.size.height/2 +random()%(int)(self.layer.frame.size.height/2);
[path addQuadCurveToPoint:CGPointMake(endPointX, endPointY) controlPoint:CGPointMake(controlPointOffSetX, controlPointOffSetY)];
return path;
}
這里呢, 我們要寫縮放透明度動(dòng)畫:
- (void)scaleOpacityAnimations {
// 縮放
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath: @"transform.scale"];
scaleAnimation.toValue = @0.01;
scaleAnimation.duration = 0.15;
scaleAnimation.fillMode = kCAFillModeForwards;
// 透明度
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath: @"opacity"];
opacityAnimation.fromValue = @1;
opacityAnimation.toValue = @0;
opacityAnimation.duration = 0.15;
opacityAnimation.fillMode = kCAFillModeForwards;
[self.layer addAnimation: scaleAnimation forKey: @"lscale"];
[self.layer addAnimation: opacityAnimation forKey: @"lopacity"];
self.layer.opacity = 0;
}
- (CGFloat)makeScaleValue {
return 1 - 0.7 * (random()%101 - 50)/50;
}
- (UIImage *)snapShot {
//根據(jù)視圖size開始圖片上下文
UIGraphicsBeginImageContext(self.frame.size);
//提供視圖當(dāng)前圖文
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
//獲取視圖當(dāng)前圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//關(guān)閉圖片上下文
UIGraphicsEndImageContext();
return image;
}
在這里, 我要說些細(xì)節(jié)知識(shí):
/*
* data 指被渲染的內(nèi)存區(qū)域 ,這個(gè)內(nèi)存區(qū)域大小應(yīng)該為(bytesPerRow * height)個(gè)字節(jié),如果對(duì)繪制操作被渲染的內(nèi)存區(qū)域并無特別的要求,那么刻意傳NULL
* width 指被渲染內(nèi)存區(qū)域的寬度
* height 指被渲染內(nèi)存區(qū)域的高度
* bitsPerComponent 指被渲染內(nèi)存區(qū)域中組件在屏幕每個(gè)像素點(diǎn)上需要使用的bits位,舉例來說,如果使用32-bit像素和RGB格式,那么RGBA顏色格式中每個(gè)組件在屏幕每一個(gè)像素點(diǎn)需要使用的bits位就是為 32/4 = 8
* bytesPerRow 指被渲染區(qū)域中每行所使用的bytes位數(shù)
* space 指被渲染內(nèi)存區(qū)域的“位圖上下文”
* bitmapInfo 指被渲染內(nèi)存區(qū)域的“視圖”是否包含一個(gè)alpha(透視)通道以及每一個(gè)像素點(diǎn)對(duì)應(yīng)的位置,除此之外還可以指定組件是浮點(diǎn)值還是整數(shù)值
CGBitmapContextCreate(void * __nullable data,
size_t width,
size_t height,
size_t bitsPerComponent,
size_t bytesPerRow,
CGColorSpaceRef __nullable space,
uint32_t bitmapInfo)
*/
接下來的, 就在代碼里看注釋就好了:
- (CGContextRef) createARGBBitmapContextFromImage:(CGImageRef) inImage {
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
//內(nèi)存空間的指針,該內(nèi)存空間的大小等于圖像使用RGB通道所占用的字節(jié)數(shù)
void *bitmapData;
unsigned long bitmapByteCount;
unsigned long bitmapBytePerRow;
size_t pixelsWide = CGImageGetWidth(inImage);//獲取橫向的像素點(diǎn)的個(gè)數(shù)
size_t pixelsHeight = CGImageGetHeight(inImage);
//每一行的像素點(diǎn)占用的字節(jié)數(shù),每個(gè)像素點(diǎn)的ARGB四個(gè)通道各占8個(gè)bit(0-255)的空間
bitmapBytePerRow = pixelsWide * 4;
//計(jì)算整張圖占用的字節(jié)數(shù)
bitmapByteCount = bitmapBytePerRow * pixelsHeight;
//創(chuàng)建依賴于設(shè)備的RGB通道
colorSpace = CGColorSpaceCreateDeviceRGB();
//分配足夠容納圖片字節(jié)數(shù)的內(nèi)存空間
bitmapData = malloc(bitmapByteCount);
//創(chuàng)建CoreGraphic的圖形上下文,該上下文描述了bitmaData指向的內(nèi)存空間需要繪制的圖像的一些繪制參數(shù)
context = CGBitmapContextCreate(bitmapData, pixelsWide, pixelsHeight, 8, bitmapBytePerRow, colorSpace, kCGImageAlphaPremultipliedFirst);
//Core Foundation中通過含有Create、Alloc的方法名字創(chuàng)建的指針,需要使用CFRelease()函數(shù)釋放
CGColorSpaceRelease(colorSpace);
return context;
}
/**
* 根據(jù)點(diǎn),取得對(duì)應(yīng)顏色
* point 坐標(biāo)點(diǎn)
* @return 顏色
*/
- (UIColor *)getPixelColorAtLocation:(CGPoint)point {
//拿到放大后的圖片
CGImageRef inImage = [self scaleImageToSize:CGSizeMake(cellCount*2, cellCount*2)].CGImage;
//使用上面的方法(createARGBBitmapContextFromImage:)創(chuàng)建上下文
CGContextRef cgctx = [self createARGBBitmapContextFromImage:inImage];
//圖片的寬高
size_t w = CGImageGetWidth(inImage);
size_t h = CGImageGetHeight(inImage);
//rect
CGRect rect = CGRectMake(0, 0, w, h);
//將目標(biāo)圖像繪制到指定的上下文,實(shí)際為上下文內(nèi)的bitmapData。
CGContextDrawImage(cgctx, rect, inImage);
//取色
unsigned char *bitmapData = CGBitmapContextGetData(cgctx);
int pixelInfo = 4*((w*round(point.y))+round(point.x));
CGFloat a = bitmapData[pixelInfo]/255.0;
CGFloat r = bitmapData[pixelInfo+1]/255.0;
CGFloat g = bitmapData[pixelInfo+2]/255.0;
CGFloat b = bitmapData[pixelInfo+3]/255.0;
//釋放上面的函數(shù)創(chuàng)建的上下文
CGContextRelease(cgctx);
return [UIColor colorWithRed:r green:g blue:b alpha:a];
}
/**
* 縮放圖片
*
* @param size 縮放大小
*
* @return 圖片
*/
- (UIImage *)scaleImageToSize:(CGSize)size {
UIGraphicsBeginImageContext(size);
[[self snapShot] drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *res = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return res;
}
寫到這里我們UIView的拓展就寫完了呢, 要到VC里調(diào)用了哦!這里需要用到多線程GCD:
#我們要?jiǎng)?chuàng)建幾張圖片, 然后寫一個(gè)Button來點(diǎn)擊控制動(dòng)畫效果呢!#
#這里就不多說了, 下面來說一下多線程GCD#
-(void)GCDMethod:(UIView *)myView afterTime:(NSTimeInterval)interval{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[myView boom];
});
}