小程式中使用 lottie 動畫 | 踩坑經驗分享

粥里有勺糖發表於2024-04-29

最近被拉去支援緊急需求(趕在五一節假日前上線的,雙休需要加班😱),參與到專案中才知道,開發的專案是微信小程式技術棧的。由於是臨時支援,筆者也很久沒開發過微信小程式了,所以挑選了相對獨立,業務屬性相對輕薄的模組參與。

其中有個營銷活動(領紅包🧧😁)的彈窗動畫就要用到 lottie 動畫。

本文就分享一下在小程式中使用 lottie 過程中遇到的問題與解決辦法。

關於 lottie

lottie 是 Airbnb 開源的一個動畫庫,用於在端上直接播放 AE ( Adobe After Effects)動畫。

透過 bodymovin AE 外掛將動畫檔案匯出為 json 檔案,lottie SDK 透過可以透過 JSON 檔案直接播放動畫。

具體 demos 效果可以上 LottieFiles 網站檢視。

如何使用 AE 匯出動畫需要的JSON檔案

完成 AE 軟體安裝後,參照 Lottie Web GitHub 官方文件 完成 bodymovin 外掛的安裝。

開啟動畫檔案後,只需簡單幾步操作

① window 中選擇 Bodymovie

② 選擇需要匯出的動畫資源

③ 匯出配置(小程式相關)

點選對應動畫的設定

勾選 Glyphs 將用到的文字+字型匯出為圖形。

小程式裡渲染不支援載入外部字型。

這個就會有 tree shake的效果,如果動畫裡沒有用到的文字,做動態替換的時候就會不顯示,後面會詳細介紹到

勾選 Convert expressions to keyframes 將表示式轉為關鍵幀,因為小程式裡不支援使用 eval 等動態執行指令碼的能力。

修改完成後點選Save儲存配置。

④ 渲染匯出 JSON 檔案

最後點選 Render 按鈕,匯出 JSON 檔案。

匯出檔案如下,data.json 檔案就是我們需要的 JSON 檔案,images 裡儲存的就是播放要用到的圖片檔案。

小程式中使用

可以使用小程式官方封裝的 lottie-miniprogram 庫。

快速驗證的話可以開啟微信開發者工具,在點選👉🏻 demo程式碼片段 進行建立。

① 安裝依賴

npm install --save lottie-miniprogram

② 使用

tip:開發者工具中驗證的話,渲染模式需要選擇 webview ,Skyline 目前還不支援除錯 canvas

index.wxml

<canvas id="lottie-canvas" type="2d"></canvas>

index.js

import lottie from 'lottie-miniprogram'

Page({
  onReady() {
    this.createSelectorQuery().select('#lottie-canvas').node((res) => {
      // 取得 canvas 節點
      const canvas = res[0].node

      // 設定 cavnas 畫布尺寸
      canvas.width = 600
      canvas.height = 600

      lottie.setup(canvas)

      const context = canvas.getContext('2d')
      const lottieInstance = lottie.loadAnimation({
        loop: true, // 迴圈播放
        autoplay: true, // 自動播放
        // 本地使用 http-server 啟動服務後,指定本地資源地址
        path: 'http://127.0.0.1:8080/lottie-demo-sources/data.json', // 透過http 制定json資源路徑

        // 也可以用下面這種方式,直接傳入 lottie json內容
        // (需要動態替換文案就需要用到這種方式)
        // animationData: {/* lottie json 格式內容 */},
        // 靜態資源目錄,通常與 animationData 配合使用
        // assetsPath: 'http://127.0.0.1:8080/lottie-demo-sources/images/',

        rendererSettings: {
          context,
        },
      })
    }).exec()
  }
})

我這個 demo 的效果(網上找的動畫素材)如下。

問題&解決

下面介紹在實際業務接入使用中遇到的一些問題和解決辦法。

expression 表示式

報錯資訊如下,這是遇到的第一個問題(也是上面匯出配置中有特別說明的)。

細看了一下文件,有特別說明,expression 表示式特性是不支援的,因此需要再匯出 JSON 檔案時禁用相關特性。

解決辦法:匯出JSON檔案時,禁用掉表示式特性即可。

當然禁用後,JSON 檔案大小會有所增加。

比如我這個 demo 從 40kb 增加到了 240kb(當然動畫不一樣,增長的大小會有所不同。有些前後可能只有1-2kb的變化)。

模糊

由於需要全屏展示,動畫檔案的尺寸不確定,手動只設定 canvas 尺寸會有模糊的問題。

這個透過掘金搜尋了一下就得到了 lottie動畫模糊問題的解決方法

微調一下上面的程式碼,就可以解決模糊問題。

const canvas = res[0].node
canvas.width = 600
canvas.height = 600

// 下面是新增的程式碼
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = canvas.width * dpr
canvas.height = canvas.height * dpr
context.scale(dpr, dpr)

lottie.setup(canvas)

全屏動畫

彈窗的動畫需要全屏展示,因此需要設定 canvas 寬高為頁面寬高。

index.wxss

#lottie-canvas{
    position: fixed;
    left: 0;
    top: 0;
    width: 100vw;
    height: 100vh;
}

index.js,使用 wx.getSystemInfoSync 獲取裝置的資訊

const { windowWidth, windowHeight, pixelRatio } = wx.getSystemInfoSync()
canvas.width = windowWidth * pixelRatio
canvas.height = windowHeight * pixelRatio

動態文案

由於是紅包,需要動態展示金額(當然也可能是不固定內容的動態標題)。

思路可以參考這篇文章知乎: 動態修改 Lottie 中的文字

可以使用固定格式的文字 ${文字} 進行替換

// 虛擬碼
get('sourceUrl').then((res) => {
  const jsonText = res.data
  const animationData = JSON.parse(jsonText.replace('${金額}', '目標金額'))
})

比如我在 demo 里加一個文字

  • 需要展示的文字里放入 ${num} 用於替換匹配
  • 在新增一個文字藏在看不見的地方,裡面寫入替換後需要用到的文字(確保和上面的文字為同一種字型)

接著匯出 JSON 檔案。

呼叫方法如下

// 拉取JSON檔案內容
const jsonData = await new Promise((resolve) => {
  wx.request({
    url: 'http://127.0.0.1:8080/json/text-replace/data.json',
    success: (res) => {
      resolve(res.data)
    }
  })
})

// 隨機生成1-100元的數字,保留兩位小數
const num = (Math.random() * 100).toFixed(2)
// 替換內容
const animationData = JSON.parse(
  JSON.stringify(jsonData)
    .replace(/\$\{num\}/g, `${num}元`)
)

lottie.loadAnimation({
  // 指定json內容
  animationData,
  // 設定依賴的圖片資源位置
  assetsPath: 'http://127.0.0.1:8080/json/text-replace/images/',
  // ...省略其它配置
})

效果如下

style 引發的渲染錯誤

在 canvas 標籤上設定 display控制顯隱,偶現會提示渲染層錯誤。

<canvas style="display:{{show?'block':'none'}}" id="c1" type="2d"></canvas>

解決辦法,給套了一層 view,用wx:if控制咯。

<view  wx:if="{{show}}">
  <canvas id="c1" type="2d"></canvas>
</view>

iOS 播放閃退問題

現象是,非冷啟動小程式的時候,動畫還沒播放完畢就提前結束了。

看程式碼log,3s的動畫,播放不到 1s 就觸發了 complete 事件,看現象就是一閃而逝。

const ani = lottie.loadAnimation({
  // 3s 的動畫
  animationData,
  // ...省略其它配置
})

ani.addEventListener('complete', () => {
  console.log('動畫播放結束')
})

解決辦法

TODO:未完待續

最後

時間匆忙,介紹的不是非常的詳細,感興趣的同學可以評論區交流。

demo 完整原始碼見 GitHub:lottie-demo

相關文章