国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Lottie動畫雙狀態切換的漸進式優化實踐

開發 前端
每次狀態切換時,都會移除當前的?animationView,重新創建一個新的?LottieAnimationView?實例,并加載對應的動畫資源;這種方式不僅會導致主線程卡頓,還會頻繁地創建和銷毀視圖對象,進一步增加性能開銷。

引言

在移動應用中,雙狀態動畫切換是最常見的交互模式之一:

  • TabBar圖標的聚焦/失焦狀態
  • 按鈕的選中/未選中狀態
  • 開關的開啟/關閉狀態

當使用Lottie實現這類需求時,傳統方案面臨兩大痛點:

  • 啟動阻塞:同步加載動畫資源導致主線程卡頓
  • 切換卡頓:狀態變化時重復解析JSON文件

本文將揭示如何通過三次漸進式優化,構建高性能的雙狀態動畫解決方案。

1.第一階段:基礎方案(同步阻塞模式)

原始實現方案

在初始實現中,我們直接在主線程同步加載動畫資源。以下是代碼實現:

class DualStateLottieView: UIView {
    privatevar animationView: LottieAnimationView!
    
    init(activePath: String, inactivePath: String) {
        // 同步加載失焦狀態動畫(阻塞主線程)
        animationView = LottieAnimationView(filePath: inactivePath)
        super.init(frame: .zero)
        addSubview(animationView)
    }
    
    func setActive(_ isActive: Bool) {
        let path = isActive ? activePath : inactivePath
        
        // 每次切換都重新加載(性能黑洞!)
        animationView.removeFromSuperview()
        animationView = LottieAnimationView(filePath: path)
        addSubview(animationView)
        animationView.play()
    }
}
  • 初始化動畫視圖:在 init 方法中,我們直接通過 LottieAnimationView(filePath:) 同步加載失焦狀態的動畫資源;這種方式會阻塞主線程,直到動畫資源加載完成。如果資源較大或網絡延遲,會導致明顯的卡頓。
  • 狀態切換邏輯:在 setActive(_:) 方法中,根據傳入的布爾值 isActive,選擇對應的動畫路徑;每次狀態切換時,都會移除當前的 animationView,重新創建一個新的 LottieAnimationView 實例,并加載對應的動畫資源;

這種方式不僅會導致主線程卡頓,還會頻繁地創建和銷毀視圖對象,進一步增加性能開銷。

執行流程分析

以下是狀態切換的執行流程圖:

圖片圖片

性能瓶頸分析

圖片圖片

通過分析可以得出以下幾點性能瓶頸:

  1. 主線程阻塞:在初始化和狀態切換時,LottieAnimationView(filePath:) 的調用會同步加載動畫資源,這會阻塞主線程;如果動畫資源較大或加載路徑較慢(如從網絡加載),會導致明顯的卡頓。
  2. 重復解析 JSON 文件:每次狀態切換時,都會重新加載和解析 JSON 文件。這不僅增加了 I/O 開銷,還導致了不必要的重復計算。
  3. 資源加載與視圖渲染強耦合:動畫資源的加載和視圖的渲染緊密耦合,導致每次狀態切換都需要重新加載資源并重新渲染視圖;這種方式在高頻操作時會導致性能急劇下降,用戶體驗極差。

核心缺陷:資源加載與視圖渲染強耦合,導致高頻操作時性能急劇下降

2.第二階段:異步加載與緩存(性能優化)

架構改造方案

為了優化性能,我們對代碼進行了架構改造,引入了異步加載和緩存機制。以下是改造后的代碼實現:

class DualStateLottieView: UIView {
    // 動畫數據緩存
    privatevar activeAnimation: LottieAnimation?
    privatevar inactiveAnimation: LottieAnimation?
    
    // 視圖實例
    privatelet animationView = LottieAnimationView()
    
    func loadResources() {
        // 異步加載主動畫
        DispatchQueue.global().async {
            let anim = LottieAnimation.filepath(activePath)
            DispatchQueue.main.async {
                self.activeAnimation = anim
            }
        }
        
        // 異步加載被動畫...
    }
    
    func setActive(_ isActive: Bool) {
        animationView.animation = isActive ? activeAnimation : inactiveAnimation
        animationView.play()
    }
}
  • 動畫數據緩存:引入了兩個變量 activeAnimation 和 inactiveAnimation,分別用于緩存主動畫和被動畫的數據;這樣可以避免每次狀態切換時重新加載和解析動畫資源。
  • 異步加載資源:在 init 方法中,使用 DispatchQueue.global().async 在后臺線程中加載動畫資源;加載完成后,通過 DispatchQueue.main.async 將動畫數據更新到主線程的緩存變量中;這種方式將文件 I/O 和 JSON 解析操作移出主線程,避免了主線程的阻塞。
  • 狀態切換邏輯:在 setActive(_:) 方法中,直接從緩存中獲取對應的動畫數據,并設置給 animationView;這樣可以快速切換動畫狀態,而無需重新加載資源。

性能優化點

  • 主線程零阻塞:初始化時僅創建輕量級的 animationView 容器視圖,耗時小于 1ms,不會阻塞主線程;動畫資源的加載和解析都在后臺線程完成,不會影響主線程的響應速度。
  • 資源異步加載:通過后臺線程加載動畫資源,避免了主線程的 I/O 操作和 JSON 解析,顯著提升了性能。
  • 動畫數據復用:使用 LottieAnimation 對象緩存動畫數據,避免了重復解析 JSON 文件,減少了不必要的計算開銷。

但是這種方案并不完善,產生了新的問題。

新問題浮現

盡管引入了異步加載和緩存機制,但在測試中發現了一個新問題:

測試發現:快速切換時出現狀態丟失,動畫不響應,這是為什么呢?——狀態切換失敗:

  • 當用戶快速切換狀態時,可能會出現動畫數據尚未加載完成的情況;
  • 例如,用戶調用 setActive(true) 時,activeAnimation 可能還沒有加載完成,導致 animationView.animation 被設置為 nil,動畫無法正常播放。

通過引入異步加載和緩存機制,我們顯著提升了動畫切換的性能,消除了主線程的阻塞問題。然而,快速切換時的狀態丟失問題仍然需要進一步優化。下一階段將通過狀態機和 Pending 機制來解決這一問題。

3.第三階段:狀態機與Pending機制(健壯性增強)

狀態機設計

為了處理動畫加載和狀態切換的時序問題,我們引入了狀態機和Pending機制。以下是狀態機的設計:

enum AnimationState {
    case active
    case inactive
    case pendingActive  // 新增中間狀態
    case pendingInactive
}

private var currentState: AnimationState = .inactive
  1. 狀態定義:active:當前顯示主動畫;inactive:當前顯示被動畫;pendingActive:正在加載主動畫,但尚未完成;pendingInactive:正在加載被動畫,但尚未完成。
  2. 狀態管理:通過 currentState 變量記錄當前的狀態,確保狀態切換的邏輯清晰且可控。

Pending機制實現

func setActive(_ isActive: Bool) {
    let targetState: AnimationState = isActive ? .active : .inactive
    
    switch (targetState, activeAnimation, inactiveAnimation) {
    case (.active, let anim?, _):
        play(animation: anim) // 立即執行
    case (.active, nil, _):
        currentState = .pendingActive // 掛起請求
    // 其他狀態處理...
    }
}

// 動畫加載完成回調
privatefunc handleActiveLoaded() {
    ifcase .pendingActive = currentState {
        play(animation: activeAnimation!)
        currentState = .active
    }
}
  • 狀態切換邏輯:在 setActive(_:) 方法中,根據目標狀態和當前緩存的動畫數據,決定是否立即播放動畫或進入掛起狀態;如果目標動畫已經加載完成(activeAnimation 或 inactiveAnimation 不為 nil),則直接播放動畫;如果目標動畫尚未加載完成,則將當前狀態設置為 pendingActive 或 pendingInactive,并等待加載完成。
  • 加載完成回調:在動畫加載完成的回調方法中(handleActiveLoaded() 和 handleInactiveLoaded()),檢查當前狀態是否為掛起狀態;如果是掛起狀態,則立即播放對應的動畫,并將狀態更新為目標狀態。

生命周期兜底

為了確保視圖在掛載時能夠正確處理掛起狀態,我們在 didMoveToWindow 方法中添加了生命周期兜底邏輯:

override func didMoveToWindow() {
    super.didMoveToWindow()
    guard window != nil else { return }
    
    // 檢查并執行掛起操作
    switch currentState {
    case .pendingActive where activeAnimation != nil:
        play(animation: activeAnimation!)
        currentState = .active
    // 其他狀態處理...
    }
}
  • 在 didMoveToWindow 方法中,檢查視圖是否已經掛載到窗口(window != nil);
  • 如果視圖已經掛載,且當前狀態為掛起狀態(pendingActive 或 pendingInactive),則檢查對應的動畫是否已經加載完成;
  • 如果動畫已經加載完成,則立即播放動畫,并將狀態更新為目標狀態。

資源加載流程優化

圖片圖片

通過引入狀態機和Pending機制,我們解決了以下問題:

  • 資源未就緒時的狀態丟失問題:在動畫資源尚未加載完成時,記錄當前狀態為掛起狀態,確保在資源加載完成后能夠正確切換狀態。
  • 確保最終一致性:通過生命周期兜底邏輯,確保視圖在掛載時能夠處理掛起狀態,避免因加載時序問題導致的狀態不一致。

第四階段:多資源管理(生產級方案)

Lottie動畫與圖片

Lottie 的 json 文件分為兩種情況:

  • 純 json 文件,所有資源(包括圖片)都內嵌在 json 里(base64),這種情況下,Lottie 只需要加載 json 文件本身即可,動畫和圖片都能正常顯示;
  • json 文件 + 外部 images 目錄(圖片分離),這種情況下,Lottie 需要能訪問到 json 文件旁邊的 images 目錄,才能正確加載圖片資源。如果找不到圖片,動畫會顯示不出來或圖片部分缺失。

現在的異步加載方式

let animation = LottieAnimation.filepath(path)

這種方式只傳入了 json 文件路徑,沒有告訴 Lottie 去哪里找 images 目錄。

Lottie 的底層實現會嘗試用 json 路徑的同級目錄下的 images 文件夾,但如果你用的是沙盒緩存路徑、或者 images 目錄和 json 不在同一目錄,或者 images 目錄沒有被正確拷貝,Lottie 就找不到圖片,結果動畫就不會被正常顯示出來。

那么如何解決呢?

圖片資源隔離方案

Lottie 支持自定義圖片加載方式,可以用 FilepathImageProvider 指定 images 目錄。

當你切換 animation 屬性時,如果新動畫的圖片資源目錄和上一個動畫不同,必須同步切換 imageProvider,否則會出現圖片丟失或顯示異常。

// 初始化時創建獨立ImageProvider
let activeProvider = FilepathImageProvider(
    filepath: URL(fileURLWithPath: activePath)
        .deletingLastPathComponent()
        .appendingPathComponent("images")
        .path
)

// 狀態切換時同步更新
func play(animation: LottieAnimation, provider: AnimationImageProvider) {
    animationView.imageProvider = provider // 先切換資源
    animationView.animation = animation    // 再切換動畫數據
    animationView.play()
}

完整架構圖

圖片圖片

  • DualStateLottieView:主類,負責管理雙狀態動畫的加載、切換和渲染;包含動畫數據緩存(activeAnimation 和 inactiveAnimation)和圖片資源提供者(activeImageProvider 和 inactiveImageProvider);使用狀態機管理動畫狀態的變化。
  • AnimationLoader:負責異步加載動畫資源;提供 loadAnimation(path:) 方法,返回加載完成的 LottieAnimation 對象。
  • StateMachine:負責處理狀態變化的邏輯;提供 handleStateChange() 方法,確保狀態切換的正確性和一致性。

關鍵優化點總結

優化階段

核心技術

解決問題

異步加載

全局隊列+主線程回調

消除主線程阻塞

狀態機

Pending狀態管理

處理加載時序問題

資源隔離

獨立ImageProvider

解決多資源沖突

生命周期

didMoveToWindow

視圖掛載兜底

性能對比數據

針對同一個Lottie動畫,JOSN大小4KB,含7張1KB-800KB圖片,內存占用0.7MB

啟動耗時測試(ms)

原始方案

最終方案

優化幅度

89.38

2.27

94.8%

狀態切換性能

指標

原始方案

最終方案

優化幅度

首次切換

6.16ms

4.57ms

25%

二次切換

6.91ms

0.04ms

99%

N次切換

6.91ms

0.04ms

99%

內存波動

高頻分配

零分配

100%

結論:99%的主線程阻塞被消除,切換性能大幅提升

最佳實踐指南

1. 資源規范

推薦目錄結構:
├── tab_animations
│   ├── home_active
│   │   ├── active.json
│   │   ├── images/  # 獨立圖片目錄
│   ├── home_inactive
│   │   ├── inactive.json
│   │   └── images/

2. 預加載策略

// 在應用空閑時預加載
func preloadAnimations() {
    let preloadQueue = OperationQueue()
    preloadQueue.qualityOfService = .utility
    
    for path in criticalAnimationPaths {
        preloadQueue.addOperation {
            _ = LottieAnimation.filepath(path) // 觸發緩存
        }
    }
}

3. 降級方案

func safePlay(animation: LottieAnimation?) {
    guardlet anim = animation else {
        showPlaceholder() // 降級為靜態圖片
        return
    }
    
    animationView.animation = anim
    animationView.play { [weakself] success in
        if !success {
            self?.animationView.currentProgress = 1// 顯示最后一幀
        }
    }
}

結語

通過三次關鍵迭代:

  1. 異步解耦:解決主線程阻塞
  2. 狀態補全:處理資源未就緒場景
  3. 資源隔離:保障復雜資源正確性

我們最終實現了:

  • ?? 啟動加速:主線程接近零耗時
  • ?? 切換流暢:60fps穩定運行
  • ?? 通用性強:適配任意雙狀態場景

優化本質在于解耦三個關注點:

  1. 資源加載(異步)
  2. 狀態管理(狀態機)
  3. 視圖渲染(輕量)

在本次實踐中,我們通過一系列漸進式優化,成功解決了 Lottie 動畫雙狀態切換中的性能瓶頸,實現了高性能、高可靠性的動畫交互體驗。

責任編輯:武曉燕 來源: 搜狐技術產品
相關推薦

2024-11-04 16:04:06

2014-12-16 13:51:55

華為eSpace UC統一通信

2010-04-27 13:41:42

云計算

2021-07-16 06:40:19

Argo RollouAnalysis云原生

2023-04-11 07:59:56

Kruise漸進式交付

2022-08-22 10:40:40

Kubernete部署分析運行

2023-09-28 07:34:33

2021-02-02 10:22:48

Web應用程序架構

2021-12-21 11:01:30

自動駕駛數據人工智能

2022-03-24 10:15:39

PingCAPTiDB數據庫

2021-01-13 13:49:29

漸進式網頁應用應用程序開發

2013-01-21 12:48:46

交互設計UI設計產品設計

2021-06-22 10:07:20

漸進式創新顛覆性創新二元方法

2013-09-23 10:00:33

5G4G5G研究

2024-11-20 09:39:56

漸進式遷移云策略云支出

2011-05-19 09:21:37

互聯網信息化

2016-01-05 16:07:17

2021-07-22 09:00:00

SPAPWAWeb

2024-12-25 16:42:18

點贊
收藏

51CTO技術棧公眾號

在线免费福利| 中文字幕一区二区视频| 97视频在线免费观看| 男人的天堂网av| 成人听书哪个软件好| 韩国一区二区三区美女美女秀| 久久午夜影院| 精品国产一区二区三区久久久| 中文字幕日本在线观看| 一区二区三区在线观看网站| 在线视频不卡一区二区| 亚洲国产裸拍裸体视频在线观看乱了中文 | 牛牛精品成人免费视频| 欧美日本一区二区三区四区| 嫩草影院在线观看网站成人| 精品91久久久久| 色综合久久88色综合天天看泰| av资源网在线播放| 91精品国产综合久久婷婷香蕉| 一个人看的免费视频色| 玉足女爽爽91| 无罩大乳的熟妇正在播放| 日本在线不卡视频| 久久福利电影| 黄色亚洲在线| 久久久久久九九| 亚洲精品男同| 国产精品嫩草影院一区二区| 女海盗2成人h版中文字幕| 精品三级av在线| 高清中文字幕一区二区三区| 亚洲图片欧美色图| 手机视频在线观看| 国产麻豆91精品| 久久久久久久久久久久久9999| 午夜综合激情| 99re视频在线播放| 国产中文字幕一区二区三区| 亚洲系列中文字幕| 日本精品600av| 亚洲国产精品系列| 99久久亚洲国产日韩美女| 久久久国产精品亚洲一区| 国产高清中文字幕在线| 日本精品一区二区三区高清| 日韩av免费观影| 欧美精品第1页| 国产99在线| 在线激情影院一区| 日本一区二区三区播放| 97精品在线视频| 中文视频一区| 欧美一级二级三级九九九| 国产高清在线精品| 欧美日韩一区二区在线免费观看| 国产精品美女久久久久久| 福利视频网站| 欧美色综合天天久久综合精品| 国产美女高潮在线观看| 欧美黄色片在线观看| 91高清一区| 日本不卡久久| 国产91色综合久久免费分享| 国产精品免费区二区三区观看| ccyy激情综合| 亚洲精品一区二区三区不| 久久综合久久色| 久久av最新网址| 久久久久久久久久久久久国产| 国产成人精品亚洲777人妖| 国产精品久久中文| 成人av资源网址| 亚洲国产精品va在线| 国产精品美女xx| 尤物tv在线精品| 亚洲天堂一区二区三区| 日本免费一区二区三区视频观看| 丝袜国产免费观看| av在线播放一区二区三区| 久久久亚洲精华液精华液精华液 | 青青国产在线| 欧美一区二视频| wwwxxx在线观看| 中文字幕一区日韩电影| 日韩影片在线观看| 草草久久久无码国产专区| 久久影院视频免费| 精品亚洲一区二区三区四区| 亚洲日本青草视频在线怡红院| 91精品专区| 欧美男女性生活在线直播观看| 精品国产欧美日韩一区二区三区| 精品国产一区二区三区四区在线观看 | 成人性生交xxxxx网站| 精品一区欧美| 国产精品∨欧美精品v日韩精品| 三级精品视频| 欧美在线视频一区二区三区| 国产中文字幕精品| 麻豆网在线观看| 波多野结衣久久精品| 欧美福利在线| 大胆人体一区二区| 黑人久久a级毛片免费观看| 精品综合免费视频观看| 日本道免费精品一区二区三区| 欧美激情va永久在线播放| 国产亚洲欧美一区| 日韩欧美aaaaaa| 国产欧美一区二区三区在线 | 亚洲视频tv| 欧美精美视频| 久久影院午夜论| 亚洲一区二区黄| 日韩高清av| 日本中文字幕电影在线免费观看| 日韩毛片视频| 国产精品久久久久精k8| 久久九九全国免费精品观看| 午夜探花在线观看| 麻豆传媒在线免费| 日韩精品一区二区三区不卡| 中文字幕免费在线观看| av综合网址| 91在线视频在线| 亚洲片国产一区一级在线观看| 欧美婷婷久久| 日本在线免费看| 黄色亚洲在线| 在线观看免费亚洲| 亚洲一区二区三区香蕉| 美国成人av| 欧美一级二级三级视频| 91蜜桃免费观看视频| 中文字幕一区电影| 亚洲国产精品国自产拍av| 亚洲香蕉成视频在线观看| 亚洲国产一区二区精品视频| 黄色片免费在线观看| 亚洲精华国产欧美| 欧美日韩综合不卡| 成人精品一二区| 韩国福利在线| 精品999成人| 欧美色男人天堂| 日韩伦理一区二区| 狠狠网亚洲精品| 亚洲精品国产精品国自产在线| 久久精品中文字幕一区二区三区| jizz在线观看中文| 好看的av在线不卡观看| 欧美综合久久久| 国产精品视频免费一区| 国产h在线观看| 精品动漫av| 在线成人免费观看| 欧美亚州在线观看| 日本在线视频中文有码| 麻豆成人91精品二区三区| 亚洲电影免费观看高清完整版在线| 亚洲精品国产精品国自产| 丁香高清在线观看完整电影视频| 97人人在线| 黄色精品视频| 99精品在线免费| xxxxx成人.com| 男人操女人免费| 给我免费播放日韩视频| 亚洲婷婷国产精品电影人久久| 日韩av免费一区| 在线观看免费av网| 伊人成人网在线看| 欧美va亚洲va在线观看蝴蝶网| 中文字幕一区综合| 免费一级欧美在线观看视频| 久久久久久久久久久久久夜| 97碰在线观看| 欧美精品一区视频| 欧美视频观看一区| 中文在线а√在线8| 成人午夜精品在线| 九九热视频这里只有精品| 激情亚洲色图| 忘忧草精品久久久久久久高清| 欧美日韩免费观看一区二区三区| 欧美一区二区三区四区夜夜大片| 九色porny丨入口在线| 99久久婷婷国产精品综合| 97在线视频免费| 青春有你2免费观看完整版在线播放高清 | 精品国产一区二区三区麻豆免费观看完整版| 日本中文字幕在线播放| 日韩精品一级中文字幕精品视频免费观看 | 国产91在线精品| 国产精品素人一区二区| 国产精品久久久久久五月尺| 91精品专区| 国产黑丝在线一区二区三区| 久久久久久久久91| 精品视频在线看|