React8+taro开发微信小程序,实现lottie动画

安装核心依赖

javascript 复制代码
npm install lottie-miniprogram @tarojs/plugin-html --save

修改 Taro 配置 (config/index.js)

javascript 复制代码
const config = {
  plugins: [
    '@tarojs/plugin-html',
    // 其他插件...
  ],
  mini: {
  	canvas: true,
    webpackChain(chain) {
      chain.merge({
        module: {
          rule: {
            'lottie-loader': {
              test: /\.json$/,
              use: {
                loader: 'lottie-miniprogram/webpack-loader',
                options: {
                  limit: 10240 // 10KB以下文件直接内联
                }
              }
            }
          }
        }
      })
    }
  }
}

app.config.js里添加配置

javascript 复制代码
export default defineAppConfig({
  // 其他代码...
  requiredBackgroundModes: ['canvas'],
    // 其他代码...
  })

封装组件commonLottie

javascript 复制代码
import React, { useEffect, useRef, useState } from 'react'
import Taro, { createSelectorQuery } from '@tarojs/taro'
import { View, Canvas } from '@tarojs/components'

const CommonLottie = React.forwardRef(
  (
    {
      animationData,
      width = 24,
      height = 24,
      loop = false,
      autoplay = true,
      canvasId = 'lottie-canvas' // 新增动态 ID 支持
    },
    ref
  ) => {
    const canvasRef = useRef(null)
    const lottieInstance = useRef(null)
    const animationDuration = useRef(1500) // 固定1秒时长

    // 暴露方法给父组件
    React.useImperativeHandle(ref, () => ({
      play: () => {
        if (lottieInstance.current) {
          // 先重置再播放
          lottieInstance.current.goToAndStop(0, true)
          lottieInstance.current.play()
        }
      },
      pause: () => lottieInstance.current?.pause()
    }))

    const calculateSizes = () => {
      //   const systemInfo = Taro.getSystemInfoSync()
      const dpr = 1

      // 逻辑尺寸 → 物理像素尺寸
      const physicalWidth = width * dpr
      const physicalHeight = height * dpr

      // Lottie 内部使用逻辑单位,需要反向缩放
      const lottieWidth = width / dpr
      const lottieHeight = height / dpr

      return {
        physicalWidth,
        physicalHeight,
        lottieWidth,
        lottieHeight,
        dpr
      }
    }
    // 使用重构后的尺寸配置
    const { physicalWidth, physicalHeight, lottieWidth, lottieHeight, dpr } =
      calculateSizes()

    // 微信小程序专属初始化逻辑
    const initWechatCanvas = async () => {
      try {
        // 使用 Taro.nextTick 确保 DOM 更新完成
        await new Promise((resolve) => Taro.nextTick(resolve))

        // 增加重试次数和间隔
        let retryCount = 0
        const MAX_RETRY = 5
        const getNode = () =>
          new Promise((resolve, reject) => {
            createSelectorQuery()
              .select(`#${canvasId}`)
              .fields({ node: true, size: true })
              .exec((res) => {
                if (res[0]?.node) resolve(res[0].node)
                else if (retryCount < MAX_RETRY) {
                  retryCount++
                  setTimeout(
                    () => getNode().then(resolve).catch(reject),
                    200 * retryCount
                  )
                } else {
                  reject(new Error(`Canvas 节点未找到 (ID: ${canvasId})`))
                }
              })
          })

        const node = await getNode()

        //动态加载 Lottie
        const Lottie = await import('lottie-miniprogram')
        // 计算播放速度
        const originalDuration =
          ((animationData.op - animationData.ip) / animationData.fr) * 1000
        const playSpeed = originalDuration / animationDuration.current
        //创建动画实例
        lottieInstance.current = Lottie.loadAnimation({
          canvas: node,
          renderer: 'canvas',
          animationData,
          loop,
          autoplay,
          rendererSettings: {
            context: node.getContext('2d'),
            dpr: 1,
            scaleMode: 2, // 1: 按比例填充容器,2: 完全填充
            preserveAspectRatio: 'xMidYMid meet' // 保持宽高比
          }
        })

        // 新增适配逻辑
        const animation = lottieInstance.current

        animation.resize(lottieWidth, lottieHeight) // 强制刷新尺寸
        animation.setSubframe(false) // 关闭子帧优化
        // 设置播放速度
        animation.setSpeed(playSpeed)

        // 自动销毁
        // animation.addEventListener('complete', () => {
        //   animation.destroy()
        // })
      } catch (err) {
        console.error('初始化失败:', err)
      }
    }

    useEffect(() => {
      if (process.env.TARO_ENV !== 'weapp') return
      if (!animationData) return

      const timer = setTimeout(() => {
        initWechatCanvas()
      }, 300) // 增加初始化延迟

      return () => {
        clearTimeout(timer)
        lottieInstance.current?.destroy()
      }
    }, [animationData])

    return (
      <View
        style={{
          width: `${width}px`,
          height: `${height}px`,
          overflow: 'visible',
          // 修复定位问题
          position: 'relative'
        }}
      >
        <Canvas
          id={canvasId}
          canvasId={canvasId} // 必须同时设置 id 和 canvasId
          type="2d"
          style={{
            width: `${physicalWidth}px`,
            height: `${physicalHeight}px`,
            transform: `translateZ(0)`, // 强制启用 GPU 加速
            transformOrigin: '0 0',
            imageSmoothingQuality: 'high' // 高清渲染
          }}
          ref={canvasRef}
        />
      </View>
    )
  }
)

export default CommonLottie

页面中使用

javascript 复制代码
import { View, Button, Image, Text, WebView } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { useState, useEffect, useRef } from 'react'
import './index.module.less'
import styles from './index.module.less'
import animationData from '@/assets/doc/tab_2_on.json'
import CommonLottie from '@/components/commonLottie'

definePageConfig({
  navigationStyle: 'custom',
  backgroundColor: '#191919'
})

export default function ExperiencePage() {
  const lottieRef = useRef(null)
  const play = () => {
    lottieRef.current.play()
    console.log(' lottieRef.current', lottieRef.current)
  }
  return (
    <View className={styles['experience-wrap']}>
      <Button onClick={play}>按钮</Button>
      <CommonLottie
        ref={lottieRef}
        canvasId={'lottie-canvas-test'}
        animationData={animationData}
        width={36}
        height={24}
      />
    </View>
  )
}
相关推荐
『 时光荏苒 』3 小时前
微信小程序实时日志记录-接口监控
微信小程序·小程序·微信小程序日志·日志抓取
老李不敲代码3 小时前
榕壹云外卖跑腿系统:基于Spring Boot+MySQL+UniApp的智慧生活服务平台
spring boot·mysql·微信小程序·uni-app·软件需求
社会底层无业大学生5 小时前
微信小程序跳
微信小程序·小程序·notepad++
HyaCinth8 小时前
Taro 数字滚动组件
javascript·react.js·taro
老李不敲代码8 小时前
榕壹云在线商城系统:基于THinkPHP+ Mysql+UniApp全端适配、高效部署的电商解决方案
mysql·微信小程序·小程序·uni-app·软件需求
专业系统开发老赵10 小时前
小程序租赁系统源码功能分享
小程序
小咕聊编程12 小时前
【含文档+PPT+源码】基于微信小程序的卫生院预约挂号管理系统的设计与实现
微信小程序·小程序
夜猫的兔子14 小时前
微信小程序中使用ECharts 并且动态设置数据
微信小程序·小程序·echarts