安装核心依赖
npm install lottie-miniprogram @tarojs/plugin-html --save
修改 Taro 配置 (config/index.js)
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 }}}}}})}}
}
app.config.js里添加配置
export default defineAppConfig({requiredBackgroundModes: ['canvas'],})
封装组件commonLottie
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' },ref) => {const canvasRef = useRef(null)const lottieInstance = useRef(null)const animationDuration = useRef(1500) React.useImperativeHandle(ref, () => ({play: () => {if (lottieInstance.current) {lottieInstance.current.goToAndStop(0, true)lottieInstance.current.play()}},pause: () => lottieInstance.current?.pause()}))const calculateSizes = () => {const dpr = 1const physicalWidth = width * dprconst physicalHeight = height * dprconst lottieWidth = width / dprconst lottieHeight = height / dprreturn {physicalWidth,physicalHeight,lottieWidth,lottieHeight,dpr}}const { physicalWidth, physicalHeight, lottieWidth, lottieHeight, dpr } =calculateSizes()const initWechatCanvas = async () => {try {await new Promise((resolve) => Taro.nextTick(resolve))let retryCount = 0const MAX_RETRY = 5const 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()const Lottie = await import('lottie-miniprogram')const originalDuration =((animationData.op - animationData.ip) / animationData.fr) * 1000const playSpeed = originalDuration / animationDuration.currentlottieInstance.current = Lottie.loadAnimation({canvas: node,renderer: 'canvas',animationData,loop,autoplay,rendererSettings: {context: node.getContext('2d'),dpr: 1,scaleMode: 2, preserveAspectRatio: 'xMidYMid meet' }})const animation = lottieInstance.currentanimation.resize(lottieWidth, lottieHeight) animation.setSubframe(false) animation.setSpeed(playSpeed)} catch (err) {console.error('初始化失败:', err)}}useEffect(() => {if (process.env.TARO_ENV !== 'weapp') returnif (!animationData) returnconst timer = setTimeout(() => {initWechatCanvas()}, 300) return () => {clearTimeout(timer)lottieInstance.current?.destroy()}}, [animationData])return (<Viewstyle={{width: `${width}px`,height: `${height}px`,overflow: 'visible',position: 'relative'}}><Canvasid={canvasId}canvasId={canvasId} type="2d"style={{width: `${physicalWidth}px`,height: `${physicalHeight}px`,transform: `translateZ(0)`, transformOrigin: '0 0',imageSmoothingQuality: 'high' }}ref={canvasRef}/></View>)}
)export default CommonLottie
页面中使用
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><CommonLottieref={lottieRef}canvasId={'lottie-canvas-test'}animationData={animationData}width={36}height={24}/></View>)
}