iOS-Lottie源码解析

Lottie 是 Airbnb 开发的一款能够为原生应用添加动画效果的开源工具。Lottie 目前提供了 iOS, Android, 和 React Native 版本,能够实时渲染 After Effects 动画特效。

Lottie支持ios8以上系统,Lottie动画可以加载Bundled JSON文件或URL。目前,Lottie支持路径修剪,蒙版、遮盖等操作。

LOTAnimationView

加载LOTAnimationView动画.h

//加载本地json文件
+ (instancetype)animationNamed:(NSString *)animationName NS_SWIFT_NAME(init(name:));
+ (instancetype)animationNamed:(NSString *)animationName inBundle:(NSBundle *)bundle NS_SWIFT_NAME(init(name:bundle:));
+ (instancetype)animationFromJSON:(NSDictionary *)animationJSON NS_SWIFT_NAME(init(json:));
//加载远程文件
- (instancetype)initWithContentsOfURL:(NSURL *)url;

@property (nonatomic, readonly) BOOL isAnimationPlaying;
@property (nonatomic, assign) BOOL loopAnimation;
@property (nonatomic, assign) CGFloat animationProgress;
@property (nonatomic, assign) CGFloat animationSpeed;
@property (nonatomic, readonly) CGFloat animationDuration;

- (void)playWithCompletion:(LOTAnimationCompletionBlock)completion;
- (void)play;
- (void)pause;

- (void)addSubview:(LOTView *)view
      toLayerNamed:(NSString *)layer;

执行layer动画

+ (instancetype)animationNamed:(NSString *)animationName inBundle:(NSBundle *)bundle {
  NSArray *components = [animationName componentsSeparatedByString:@"."];
  animationName = components.firstObject;

  LOTComposition *comp = [[LOTAnimationCache sharedCache] animationForKey:animationName];
  if (comp) {
    return [[LOTAnimationView alloc] initWithModel:comp];
  }

  NSError *error;
  NSString *filePath = [bundle pathForResource:animationName ofType:@"json"];
  NSData *jsonData = [[NSData alloc] initWithContentsOfFile:filePath];
  NSDictionary  *JSONObject = jsonData ? [NSJSONSerialization JSONObjectWithData:jsonData
                                                                         options:0 error:&error] : nil;
  if (JSONObject && !error) {
    LOTComposition *laScene = [[LOTComposition alloc] initWithJSON:JSONObject];
    [[LOTAnimationCache sharedCache] addAnimation:laScene forKey:animationName];
    return [[LOTAnimationView alloc] initWithModel:laScene];
  }

  NSException* resourceNotFoundException = [NSException exceptionWithName:@"ResourceNotFoundException"
                                                                   reason:[error localizedDescription]
                                                                 userInfo:nil];
  @throw resourceNotFoundException;
}

LOTComposition(动画数据)

用于解析动画的json文件,获取整个动画数据

- (void)_mapFromJSON:(NSDictionary *)jsonDictionary {
  NSNumber *width = jsonDictionary[@"w"];  //动画的宽度
  NSNumber *height = jsonDictionary[@"h"];  //动画的高度
  if (width && height) {
    CGRect bounds = CGRectMake(0, 0, width.floatValue, height.floatValue);
    _compBounds = bounds;  // 动画的bounds
  }

  _startFrame = [jsonDictionary[@"ip"] copy];  //动画的起始大小
  _endFrame = [jsonDictionary[@"op"] copy];  //动画的结束大小
  _framerate = [jsonDictionary[@"fr"] copy];  //动画的变化比率  
  if (_startFrame && _endFrame && _framerate) {
    //通过动画起始和结束的大小和变化比率计算出动画时间
    NSInteger frameDuration = _endFrame.integerValue - _startFrame.integerValue;
    NSTimeInterval timeDuration = frameDuration / _framerate.floatValue;
    _timeDuration = timeDuration;
  }

  NSArray *assetArray = jsonDictionary[@"assets"];  //动画的资产,一般为空
  if (assetArray.count) {
    _assetGroup = [[LOTAssetGroup alloc] initWithJSON:assetArray];
  }

  NSArray *layersJSON = jsonDictionary[@"layers"];  //动画的layer层
  if (layersJSON) {
    _layerGroup = [[LOTLayerGroup alloc] initWithLayerJSON:layersJSON
                                                withBounds:_compBounds
                                             withFramerate:_framerate
                                            withAssetGroup:_assetGroup];
  }
  [_assetGroup finalizeInitialization];
}

LOTLayerGroup(Layer动画组)

解析json文件中的"layers" 层的数据返回所有LOTLayer 集合

- (instancetype)initWithLayerJSON:(NSArray *)layersJSON
                       withBounds:(CGRect)bounds
                    withFramerate:(NSNumber *)framerate
                   withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup {
  self = [super init];
  if (self) {
    _framerate = framerate;
    _bounds = bounds;
    [self _mapFromJSON:layersJSON withAssetGroup:assetGroup];
  }
  return self;
}

- (void)_mapFromJSON:(NSArray *)layersJSON withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup {
  NSMutableArray *layers = [NSMutableArray array];
  NSMutableDictionary *modelMap = [NSMutableDictionary dictionary];
  NSMutableDictionary *referenceMap = [NSMutableDictionary dictionary];

  for (NSDictionary *layerJSON in layersJSON) {
    LOTLayer *layer = [[LOTLayer alloc] initWithJSON:layerJSON
                                      withCompBounds:_bounds
                                       withFramerate:_framerate
                                      withAssetGroup:assetGroup];
    [layers addObject:layer];
    modelMap[layer.layerID] = layer;
    if (layer.referenceID) {
      referenceMap[layer.referenceID] = layer;
    }
  }

  _referenceIDMap = referenceMap;
  _modelMap = modelMap;
  _layers = layers;
}
// 根据layerID获取LOTLayer
- (LOTLayer *)layerModelForID:(NSNumber *)layerID {
  return _modelMap[layerID];
}
// 根据referenceID获取LOTLayer
- (LOTLayer *)layerForReferenceID:(NSString *)referenceID {
  return _referenceIDMap[referenceID];
}

LOTLayer

解析获得json文件中的每一个layer数据具体详情

- (void)_mapFromJSON:(NSDictionary *)jsonDictionary
      withCompBounds:(CGRect)compBounds
       withFramerate:(NSNumber *)framerate
      withAssetGroup:(LOTAssetGroup *)assetGroup{

  _parentCompBounds = compBounds;
  _layerName = [jsonDictionary[@"nm"] copy];  // 名称
  _layerID = [jsonDictionary[@"ind"] copy];  // ID

  NSNumber *layerType = jsonDictionary[@"ty"]; // 类型
  _layerType = layerType.integerValue;

  if (jsonDictionary[@"refId"]) { // referenceID
    _referenceID = [jsonDictionary[@"refId"] copy];
  }

  _parentID = [jsonDictionary[@"parent"] copy]; // 父类ID

  _inFrame = [jsonDictionary[@"ip"] copy];  // 起始大小
  _outFrame = [jsonDictionary[@"op"] copy];  // 结束大小
  _framerate = framerate;  // 变化比率

  // layer类型
  if (_layerType == LOTLayerTypePrecomp) { // 预补偿
    _layerHeight = [jsonDictionary[@"h"] copy];
    _layerWidth = [jsonDictionary[@"w"] copy];
    [assetGroup buildAssetNamed:_referenceID
                     withBounds:CGRectMake(0, 0, _layerWidth.floatValue, _layerHeight.floatValue)
                   andFramerate:_framerate];
  } else if (_layerType == LOTLayerTypeImage) {  // 图片
    [assetGroup buildAssetNamed:_referenceID
                     withBounds:CGRectZero
                   andFramerate:_framerate];
    _imageAsset = [assetGroup assetModelForID:_referenceID];
    _layerWidth = [_imageAsset.assetWidth copy];
    _layerHeight = [_imageAsset.assetHeight copy];
  } else if (_layerType == LOTLayerTypeSolid) {  // 立方体
    _layerWidth = jsonDictionary[@"sw"];
    _layerHeight = jsonDictionary[@"sh"];
    NSString *solidColor = jsonDictionary[@"sc"];
    _solidColor = [UIColor LOT_colorWithHexString:solidColor];
  } else {   // 其他
    _layerWidth = @(compBounds.size.width);
    _layerHeight = @(compBounds.size.height);
  }

  _layerBounds = CGRectMake(0, 0, _layerWidth.floatValue, _layerHeight.floatValue);  // 获取layer的bounds

  //帧动画属性 
  NSDictionary *ks = jsonDictionary[@"ks"];

  NSDictionary *opacity = ks[@"o"];  // 透明度变化
  if (opacity) {
    _opacity = [[LOTAnimatableNumberValue alloc] initWithNumberValues:opacity frameRate:_framerate];
    [_opacity remapValuesFromMin:@0 fromMax:@100 toMin:@0 toMax:@1];
  }

  NSDictionary *rotation = ks[@"r"];  // 旋转
  if (rotation == nil) {
    rotation = ks[@"rz"];
  }
  if (rotation) {
    _rotation = [[LOTAnimatableNumberValue alloc] initWithNumberValues:rotation frameRate:_framerate];
    [_rotation remapValueWithBlock:^CGFloat(CGFloat inValue) {
      return LOT_DegreesToRadians(inValue);
    }];
  }

  NSDictionary *position = ks[@"p"];  // 位置
  if ([position[@"s"] boolValue]) {
    // Separate dimensions
    _positionX = [[LOTAnimatableNumberValue alloc] initWithNumberValues:position[@"x"] frameRate:_framerate];
    _positionY = [[LOTAnimatableNumberValue alloc] initWithNumberValues:position[@"y"] frameRate:_framerate];
  } else {
    _position = [[LOTAnimatablePointValue alloc] initWithPointValues:position frameRate:_framerate];
  }

  NSDictionary *anchor = ks[@"a"];  // 锚点
  if (anchor) {
    _anchor = [[LOTAnimatablePointValue alloc] initWithPointValues:anchor frameRate:_framerate];
    [_anchor remapPointsFromBounds:_layerBounds toBounds:CGRectMake(0, 0, 1, 1)];
    _anchor.usePathAnimation = NO;
  }

  NSDictionary *scale = ks[@"s"];  // 缩放
  if (scale) {
    _scale = [[LOTAnimatableScaleValue alloc] initWithScaleValues:scale frameRate:_framerate];
  }

  _matteType = [jsonDictionary[@"tt"] integerValue];  // 磨砂

  // 遮罩
  NSMutableArray *masks = [NSMutableArray array];
  for (NSDictionary *maskJSON in jsonDictionary[@"masksProperties"]) {
    LOTMask *mask = [[LOTMask alloc] initWithJSON:maskJSON frameRate:_framerate];
    [masks addObject:mask];
  }
  _masks = masks.count ? masks : nil;

  // 形变
  NSMutableArray *shapes = [NSMutableArray array];
  for (NSDictionary *shapeJSON in jsonDictionary[@"shapes"]) {
    id shapeItem = [LOTShapeGroup shapeItemWithJSON:shapeJSON frameRate:_framerate compBounds:_layerBounds];
    if (shapeItem) {
      [shapes addObject:shapeItem];
    }
  }
  _shapes = shapes;

  // 其他特效
  NSArray *effects = jsonDictionary[@"ef"];
  if (effects.count > 0) {

    NSDictionary *effectNames = @{ @0: @"slider",
                                   @1: @"angle",
                                   @2: @"color",
                                   @3: @"point",
                                   @4: @"checkbox",
                                   @5: @"group",
                                   @6: @"noValue",
                                   @7: @"dropDown",
                                   @9: @"customValue",
                                   @10: @"layerIndex",
                                   @20: @"tint",
                                   @21: @"fill" };

    for (NSDictionary *effect in effects) {
      NSNumber *typeNumber = effect[@"ty"];
      NSString *name = effect[@"nm"];
      NSString *internalName = effect[@"mn"];
      NSString *typeString = effectNames[typeNumber];
      if (typeString) {
        NSLog(@"%s: Warning: %@ effect not supported: %@ / %@", __PRETTY_FUNCTION__, typeString, internalName, name);
      }
    }
  }
  _hasInAnimation = _inFrame.integerValue > 0;
  NSMutableArray *keys = [NSMutableArray array];
  NSMutableArray *keyTimes = [NSMutableArray array];
  CGFloat layerLength = _outFrame.integerValue;
  _layerDuration = (layerLength / _framerate.floatValue);

  if (_hasInAnimation) {
    [keys addObject:@1];
    [keyTimes addObject:@0];
    [keys addObject:@0];
    CGFloat inTime = _inFrame.floatValue / layerLength;
    [keyTimes addObject:@(inTime)];
  } else {
    [keys addObject:@0];
    [keyTimes addObject:@0];
  }
  [keys addObject:@1];
  [keyTimes addObject:@1];
  _inOutKeyTimes = keyTimes;
  _inOutKeyframes = keys;
}

获取到layer动画的数据后,根据数据创建动画的layer层(LOTCompositionLayer)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值