锻炼吃饭的家伙

iOS框架使用:Lottie 动画特效

2022-07-11  本文已影响0人  时光啊混蛋_97boy

原创:有趣知识点摸索型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录


一、Lottie 介绍

Lottie 是一个可应用于Andriod和iOS的动画库,它通过bodymovin插件来解析Adobe After Effects 动画并导出为json文件,通过手机端原生的方式或者通过React Native的方式渲染出矢量动画。

这是前所未有的方式,设计师可以创作并且运行优美的动画而不需要工程师煞费苦心地通过手动调整的方式来重现动画。由于动画是通过json来加载的,使得动画源文件只需占用极小的空间就能完成相当复杂的效果!Lottie可以用于播放动画、调整尺寸、循环播放、加速、减速、甚至是精致的交互。

换句话说,你也可以通过设计器直接把JSON文件放入Xcode project,让Lottie帮你下载动画。当然别误会,你还是需要为你的动画写一些代码,但是你会发现Lottie的确将为你的动画编码节省大量的时间。

替补方案

二、Lottie 使用

资源加载方式

最基本的方式是用AnimationView来使用它:

// someJSONFileName 指的就是用 AE 导出的动画本地 JSON 文件名
let animationView = AnimationView(name: "someJSONFileName")
    
// 可以使用 frame 也可以使用自动布局
animationView.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
    
view.addSubview(animationView)
    
animationView.play { (isFinished) in
    
    // 动画执行完成后的回调
    // Do Something
}

如果你使用到了跨bundleJSON文件,你可以这么做:

let animationView = AnimationView(name: "someJSONFileName", bundle: YOUR_BUNDLE)

从本地支持的JSON文件加载Lottie动画的完整方法是:

/**
- Parameter name: JSON文件名.
- Parameter bundle: 动画所在的包.
- Parameter imageProvider: 加载动画需要的图片资源(有些动画需要图片配合【可以是本地图片资源,也可以是网络图片资源,实现该协议返回对应的CGImage】).
- Parameter animationCache: 缓存机制【需要自己实现缓存机制,Lottie本身不支持】).
*/
convenience init(name: String,
                      bundle: Bundle = Bundle.main,
                      imageProvider: AnimationImageProvider? = nil,
                      animationCache: AnimationCacheProvider? = LRUAnimationCache.sharedCache) { ... }

或者你可以使用磁盘路径和从服务器加载:

// 从磁盘路径加载动画
convenience init(filePath: String,
                          imageProvider: AnimationImageProvider? = nil,
                          animationCache: AnimationCacheProvider? = LRUAnimationCache.sharedCache) { ... }
// 从网络加载
convenience init(url: URL,
                  imageProvider: AnimationImageProvider? = nil,
                  closure: @escaping AnimationView.DownloadClosure,
                  animationCache: AnimationCacheProvider? = LRUAnimationCache.sharedCache) { ... }

填充模式

Lottie 支持iOS中的UIView.ContentModescaleAspectFit, scaleAspectFillscaleToFill这些属性。

let animationView = AnimationView(name: "someJSONFileName")
animationView.contentMode = .scaleToFill
...

播放控制

Lottie 动画的播放控制,除了常规的控制,还支持进度播放、帧播放。

播放、暂停、停止:

let animationView = AnimationView(name: "someJSONFileName")

// 从上一次的动画位置开始播放
animationView.play()
// 暂停动画播放
animationView.pause()
// 停止动画播放,此时动画进度重置为0
animationView.stop() 

控制进度播放:参考DEMO中的 FrameAnimationViewController

/**
 播放动画,进度(0 ~ 1).
 
 - Parameter fromProgress: 动画的开始进度。 如果是'nil`,动画将从当前进度开始。
 - Parameter toProgress: 动画的结束进度。
 - Parameter loopMode: 动画的循环行为。 如果是`nil`,将使用视图的`loopMode`属性。默认是 .playOnce
 - Parameter completion: 动画停止时要调用的可选完成闭包。
 */
public func play(fromProgress: AnimationProgressTime? = nil,
                 toProgress: AnimationProgressTime,
                 loopMode: LottieLoopMode? = nil,
                 completion: LottieCompletionBlock? = nil)
animationView.play(fromProgress: 0, toProgress: 1, loopMode: .playOnce) { (isFinished) in
    // 播放完成后的回调闭包
}
// 设置当前进度
animationView.currentProgress = 0.5

控制帧播放:参考DEMO中的 FrameAnimationViewController

/**
 使用帧的方式播放动画
 
 - Parameter fromProgress: 动画的开始进度。 如果是'nil`,动画将从当前进度开始。
 - Parameter toProgress: 动画的结束进度
 - Parameter loopMode: 动画的循环行为。 如果是`nil`,将使用视图的`loopMode`属性。
 - Parameter completion: 动画停止时要调用的可选完成闭包。
 */
public func play(fromFrame: AnimationFrameTime? = nil,
                 toFrame: AnimationFrameTime,
                 loopMode: LottieLoopMode? = nil,
                 completion: LottieCompletionBlock? = nil)
animationView.play(fromFrame: 50, toFrame: 80, loopMode: .loop) { (isFinished) in
    // 播放完成后的回调闭包
}
// 设置当前帧
animationView.currentFrame = 65

动画的循环模式。设置play调用的循环行为, 默认为playOnce

public enum ottieLoopMode {
  /// 动画播放一次然后停止。
  case playOnce
  /// 动画将从头到尾循环直到停止。
  case loop
  /// 动画将向前播放,然后向后播放并循环直至停止。
  case autoReverse
}

// 循环模式
animationView.loopMode = .playOnce

动画到后台的行为模式。到后台时AnimationView的行为,默认为暂停。 回调会以false调用完成。

public enum LottieBackgroundBehavior {
    /// 停止动画并将其重置为当前播放时间的开头。 调用完成回调。
    case stop
    /// 暂停动画,回调会以“false”调用完成。
    case pause
    /// 暂停动画并在应到前台时重新启动它,在动画完成时调用回调
    case pauseAndRestore
}
        
// 到后台的行为模式
animationView.backgroundBehavior = .pause

编辑某帧的动画对象的属性:

let redValueProvider = ColorValueProvider(Color(r: 1, g: 0.2, b: 0.3, a: 1))
animationView.setValueProvider(redValueProvider, keypath: AnimationKeypath(keypath: "BG-On.Group 1.Fill 1.Color"))
    
let otherValueProvider = ColorValueProvider(Color(r: 0.3, g: 0.2, b: 0.3, a: 1))
animationView.setValueProvider(otherValueProvider, keypath: AnimationKeypath(keypath: "BG-Off.Group 1.Fill 1.Color"))

AnimationView常用的属性和方法:

/// 动画属性
public var animation: Animation? { ... }
/// 程序到后台动画的行为,上面有详细解释
public var backgroundBehavior: LottieBackgroundBehavior = .pause
/// 如果动画需要图片资源的支持,需要设定该协议
public var imageProvider: AnimationImageProvider { ... }
/// 动画是否正在播放
public var isAnimationPlaying: Bool { ... }
/// 循环模式,上面有详细解释
public var loopMode: LottieLoopMode = .playOnce { ... }
/// 当前的播放进度(取值范围 0 ~ 1)
public var currentProgress: AnimationProgressTime { ... }
/// 当前的播放时间
public var currentTime: TimeInterval { ... }
/// 当前帧数
public var currentFrame: AnimationFrameTime { ... }
/// 动画的播放速度
public var animationSpeed: CGFloat = 1 { ... }
/// 判断是否正在播放动画
public var isAnimationPlaying: Bool { get }
/// 播放方法,带可选完成回调
public func play(completion: LottieCompletionBlock? = nil) { ... }

/// 从一个进度到另一个进度,上面有详细解释
public func play(fromProgress: AnimationProgressTime? = nil,
                   toProgress: AnimationProgressTime,
                   loopMode: LottieLoopMode? = nil,
                   completion: LottieCompletionBlock? = nil) { ... }

/// 从一个帧到另一个帧,上面有详细解释                   
public func play(fromFrame: AnimationFrameTime? = nil,
                   toFrame: AnimationFrameTime,
                   loopMode: LottieLoopMode? = nil,
                   completion: LottieCompletionBlock? = nil) { ... }  
/// 停止
public func stop() 
/// 暂停
public func pause()  
/// 打印所有的层级属性
public func logHierarchyKeypaths()   
/// 强制AnimationView重绘其内容
public func forceDisplayUpdate() { ... }  

三、Lottie 优势


四、Lottie 适用场景


五、Lottie 使用注意

Lottie是基于CALayer的动画, 所有的路径预先在AE中计算好, 转换为Json文件, 然后自动转换为Layer的动画, 所以性能理论上是非常不错的。

有些需要展示动画的AnimationView,可能也会有点击事件,如果需要添加点击事件的话,就需要一个个地加,比较麻烦。

/// 关联属性 key
private var kUIViewTouchEventKey = "kUIViewTouchEventKey"

/// 点击事件闭包
public typealias UIViewTouchEvent = (AnyObject) -> ()

extension UIView {
    ......
}
private var touchEvent: UIViewTouchEvent? {
    set {
        objc_setAssociatedObject(self, &kUIViewTouchEventKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
    }
    get {
        if let event = objc_getAssociatedObject(self, &kUIViewTouchEventKey) as? UIViewTouchEvent {
            return event
        }
        return nil
    }
}

添加点击事件:

func addTouchEvent(event: @escaping UIViewTouchEvent) {
    self.touchEvent = event
    // 先判断当前是否有交互事件
    // 所有gesture的交互事件都会被添加进gestureRecognizers中
    if (self.gestureRecognizers == nil) {
        self.isUserInteractionEnabled = true
        // 添加单击事件
        let tapEvent = UITapGestureRecognizer.init(target: self, action: #selector(touchedAciton))
        self.addGestureRecognizer(tapEvent)
    }
}

点击事件处理:

@objc private func touchedAciton() {
    guard let touchEvent = self.touchEvent else {
        return
    }
    touchEvent(self)
}

参考文献

上一篇下一篇

猜你喜欢

热点阅读