基础知识
本书用到的主要技术有自定义属性动画与颜色渐变。
自定义属性动画在 绘图与动画系列之使用自定义属性与图像掩膜实现灯泡开关动画一文中已经讲过,这里就不再解释。
颜色渐变
Quartz提供了两种创建颜色渐变的数据类型:CGShadingRef和CGGradientRef,而渐变的种类则分为轴向和径向两种,两种数据类型均可以绘制这两种渐变。
本文中用到的是CGGradientRef,创建CGGradientRef需要指定色域、分割点(包含起点和终点)相对位置、对应颜色组成以及分割点数量。
CGGradientRef myGradient;
CGColorSpaceRef myColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 }; //起点和终点相对位置
CGFloat components[8] = { 1.0, 0.5, 0.4, 1.0, // 起点颜色
0.8, 0.8, 0.3, 1.0 }; // 终点颜色
myColorspace = CGColorSpaceCreateDeviceRGB();
myGradient = CGGradientCreateWithColorComponents (myColorspace, components,
locations, num_locations);
- 轴向渐变
在起点和终点之间画线,颜色会沿着这条线渐变,位于这条线的同一条垂线上的点颜色相同。
创建轴向渐变需要指定起点和终点的坐标,并且可以通过options设置是否绘制超出起点和终点以外的部分。
CGPoint myStartPoint, myEndPoint;
myStartPoint.x = 0.0;
myStartPoint.y = 0.0;
myEndPoint.x = 1.0;
myEndPoint.y = 1.0;
CGContextDrawLinearGradient (myContext, myGradient, myStartPoint, myEndPoint, 0);
- 径向渐变
简单来说就是两个圆环之间的渐变,包括点和圆环之间的渐变。
创建径向渐变除了起点和终点坐标以外,还需要起点和终点的半径(以绘制圆环)。
CGPoint myStartPoint, myEndPoint;
CGFloat myStartRadius, myEndRadius;
myStartPoint.x = 0.15;
myStartPoint.y = 0.15;
myEndPoint.x = 0.5;
myEndPoint.y = 0.5;
myStartRadius = 0.1;
myEndRadius = 0.25;
CGContextDrawRadialGradient (myContext, myGradient, myStartPoint,
myStartRadius, myEndPoint, myEndRadius,
kCGGradientDrawsAfterEndLocation);
引言
之前曾经看过一个色带加载的动画,大概效果是颜色从左向右循环渐变,于是在此也尝试做一个实现。
思路及算法
可以看出,动画的主体由一条渐变色带组成,色带上的颜色从左往右循环运动。
实现这种效果需要两个部分:创建渐变色带,定义一个颜色关于时间的周期函数。
本文中的色带包括五个分割点,而其颜色则在(1.0, 0.6, 0, 1.0)至(0.6, 1.0, 0, 1.0)之间变化,也就是red和green始终在0.6到1.0之间。
而颜色组成关于时间的函数则是一个周期函数:
代码实现
首先需要定义一个包含时间属性的CALayer
@interface IPZColorLoadLayer : CALayer
@property int loadTime;
@end
实现自定义属性动画、周期函数及渐变色带绘制
@implementation IPZColorLoadLayer
@dynamic loadTime;
+(BOOL)needsDisplayForKey:(NSString *)key{
if ([@"loadTime" isEqualToString:key]) {
return true;
}
return [super needsDisplayForKey:key];
}
-(id<CAAction>)actionForKey:(NSString *)event{
if ([self presentationLayer]!=nil) {
if ([@"loadTime" isEqualToString:event]) {
CABasicAnimation *animation=[CABasicAnimation animationWithKeyPath:@"loadTime"];
animation.fromValue=[[self presentationLayer] valueForKey:@"loadTime"];
return animation;
}
}
return [super actionForKey:event];
}
-(void)drawInContext:(CGContextRef)ctx{
CGGradientRef myGradient;
CGColorSpaceRef myColorspace;
size_t num_locations = 5;
CGFloat locations[5] = { 0.0,0.25,0.5,0.75, 1.0 };
CGFloat components[20] =
{[self getColor:0.6], [self getColor:1.0], 0.0, 1.0, // Start color
[self getColor:0.7], [self getColor:0.9], 0.0, 1.0, // Second color
[self getColor:0.8], [self getColor:0.8], 0.0, 1.0, // Third color
[self getColor:0.9], [self getColor:0.7], 0.0, 1.0, // Fourth color
[self getColor:1.0], [self getColor:0.6], 0.0, 1.0, // End color
};
myColorspace = CGColorSpaceCreateDeviceRGB();
myGradient = CGGradientCreateWithColorComponents (myColorspace, components,locations, num_locations);
CGPoint myStartPoint, myEndPoint;
CGRect frame=self.bounds;
myStartPoint.x = 0.0;
myStartPoint.y = frame.origin.y;
myEndPoint.x = frame.size.width;
myEndPoint.y = frame.origin.y;
CGContextDrawLinearGradient (ctx, myGradient, myStartPoint, myEndPoint, 0);
}
- (CGFloat)getColor:(CGFloat)originValue{
int offset=self.loadTime % 8 ;
int value=originValue*10+offset;
if (value>10) {
value=10-value%10;
}
if (value<6) {
int origin=originValue *10;
value=6+(value -(10-origin))%4;
}
return value/10.0f;
}
@end
在view中通过间断赋值实现动画效果。
- (void)drawRect:(CGRect)rect {
IPZColorLoadLayer *loadLayer=[IPZColorLoadLayer new];
loadLayer.frame=CGRectMake(rect.origin.x, rect.origin.y+300, rect.size.width, 2);
[self.layer addSublayer:loadLayer];
_loadLayer=loadLayer;
[NSTimer scheduledTimerWithTimeInterval:0.15 target:self selector:@selector(updateColor:) userInfo:nil repeats:true];
}
- (void)updateColor:(id)sender{
_loadTime++;
_loadLayer.loadTime=_loadTime;;
}