实践-跑马灯效果及实现过程解析

前言

最近闲来无事,把自己之前做的一款跑马灯效果做个总结,也算温习了一下相关的知识。


效果

跑.gif

实现过程

  • 设置一个背景ViewA,背景的左右两端加上正方形的颜色渐变的图层。
  • 设置一个上面有gif 和 要显示的文字label的ViewB。
  • 把 ViewB加载在 VIewA上,给ViewB的layer 绘制动画轨迹(贝塞尔曲线),ViewB的layer 添加关键帧动画即可
实践-跑马灯效果及实现过程解析_第1张图片
Paste_Image.png

过程详解

在两段添加渐变图层是为了字体出现、消失时的颜渐隐效果。

  • 渐变图层
 #以下是渐变图层的实现方法
 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时间详解

你可能感兴趣的:(实践-跑马灯效果及实现过程解析)