前言
最近闲来无事,把自己之前做的一款跑马灯效果做个总结,也算温习了一下相关的知识。
效果
实现过程
- 设置一个背景ViewA,背景的左右两端加上正方形的颜色渐变的图层。
- 设置一个上面有gif 和 要显示的文字label的ViewB。
- 把 ViewB加载在 VIewA上,给ViewB的layer 绘制动画轨迹(贝塞尔曲线),ViewB的layer 添加关键帧动画即可
过程详解
在两段添加渐变图层是为了字体出现、消失时的颜渐隐效果。
- 渐变图层
#以下是渐变图层的实现方法
CAGradientLayer *la = [[CAGradientLayer alloc]init];
la.frame = gradientView.bounds;
[gradientView.layer addSublayer:la];
NSMutableArray *marray = [NSMutableArray array];
#colorArray 里是两个颜色 :第一个颜色为背景色,透明度为1,第二个颜色为背景色,透明度为0.这里转化为 CGColor。
#必须为背景颜色,否则会有效果问题。
for (UIColor *color in colorArray) {
[marray addObject:(__bridge id)color.CGColor];
}
la.colors = marray;
#// 设置渐变颜色方向,左上点为(0,0), 右下点为(1,1)
la.startPoint = CGPointMake(0, 0.5);
la.endPoint = CGPointMake(1, 0.5);
- 移动的文字Label
#根据字符串长短,获取跟字符串等长的宽度,设置Label的宽度。默认的是一行铺开的长度。
_marqueeLbl.font = fnt;
CGSize msgSize = [_marqueeLbl.text sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:fnt,NSFontAttributeName, nil]];
_marqueeLbl.frame = CGRectMake(0, 0, msgSize.width, h);
- 移动操作
#使用CAKeyframeAnimation,push一个新的VC之后,pop返回时动画停止了,
#只有repeatCount设置为CGFLOAT_MAX, removedOnCompletion属性为 NO 这两个属性一起使用,就可以不出现这样 POP回来动画停止的现象了。
- (void)moveAction
{
CGRect fr = self.marqueeLbl.frame;
fr.origin.x = self.frame.size.width;
self.marqueeLbl.frame = fr;
CGPoint fromPoint = CGPointMake(self.frame.size.width + self.marqueeLbl.frame.size.width/2, self.frame.size.height/2);
#绘制贝塞尔曲线轨迹,移动前viewB左侧在屏幕的右边,移动后,viewB右侧在屏幕的左边。
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:fromPoint];
[movePath addLineToPoint:CGPointMake(-self.marqueeLbl.frame.size.width/2, self.frame.size.height/2)];
#设置 “平移”动画
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
moveAnim.path = movePath.CGPath;
#1.2设置动画执行完毕后,不删除动画
moveAnim.removedOnCompletion = NO;
#设置动画的重复次数(设置为最大值)
moveAnim.repeatCount=MAXFLOAT;
#设置动画执行时间
moveAnim.duration = self.marqueeLbl.frame.size.width * self.speedLevel * 0.01;
# 对图层添加关键帧动画
[self.marqueeLbl.layer addAnimation:moveAnim forKey:nil];
}
- 暂停、重新开始动画
#CALayer通过CAMediaTiming协议实现了一个有层级关系的时间系统.
#除了CALayer,CAAnimation也采纳了此协议,用来实现动画的时间系统.
#在CA中,有一个Absolute Time(绝对时间)的概念,可以通过CACurrentMediaTime()获得,
#就和座标存在相对座标一样,不同的实现了CAMediaTiming协议的存在层级关系的对象也存在相对时间,经常需要进行时间的转换,
#CALayer提供了两个时间转换的方法:
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
# beginTime: 如果图层中的动画的beginTime为0,则beginTime会被设定为当前图层的当前时间,使得动画立即开始.
#如果你想某个直接加入图层的动画稍后执行,可以通过手动设置这个动画的beginTime,
#但需要注意的是这个beginTime需要为 CACurrentMediaTime()+延迟的秒数,因为beginTime是指其父级对象的时间线上的某个时间,
# timeOffset :则是active local time的偏移量. 你将一个动画看作一个环,timeOffset改变的其实是动画在环内的起点,比如一个duration为5秒的动画,将timeOffset设置为2(或者7,模5为2),那么动画的运行则是从原来的2秒开始到5秒,
# speed属性 :用于设置当前对象的时间流相对于父级对象时间流的流逝速度, speed越大则说明时间流逝速度越快,那动画也就越快。
-(void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
-(void)resumeLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = layer.timeOffset;
layer.speed = 1.0;
layer.timeOffset = 0.0;
#不设置的话,不会从上次暂停的位置开始。
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
layer时间详解