基于svga+uniapp的微信小程序动画组件开发指南

lottie动画指南

效果

概述

本项目使用 svgaplayer.weapp.js 库来实现 SVGA 动画播放功能,支持在微信小程序、H5 等多端环境下播放高质量的矢量动画。SVGA 是一种跨平台的开源动画格式,具有文件小、渲染性能高的特点。

技术栈

  • 核心库: svgaplayer.weapp.js (专为小程序优化的 SVGA 播放器)
  • 框架: Vue 3 + Composition API
  • 平台: 支持微信小程序、H5 等多端

(svgaplayer.weapp二次封装)

项目结构

src/
├── components/
│   ├── common/
│   │   └── svgaPlayer.vue           # SVGA播放器核心组件
│   └── AnimationTemplate/
│       ├── index.vue                # 简化版动画模板组件
│       └── AnimationTemplate.vue   # 高级动画模板组件
├── static/
│   └── js/
│       └── svgaplayer.weapp.js     # SVGA播放器库文件
└── pages/
    └── sale-entry/
        └── index.vue               # 使用示例页面

核心组件

1. SvgaPlayer 组件 (src/components/common/svgaPlayer.vue)

这是 SVGA 播放的核心组件,负责加载、解析和播放 SVGA 动画文件。

主要特性:

  • 支持本地和远程 SVGA 文件加载
  • 内置缓存机制,避免重复加载
  • 支持循环播放、播放控制
  • 提供丰富的回调函数
  • 自动适配不同平台的 Canvas API

2. AnimationTemplate 组件

基于 SvgaPlayer 的封装组件,提供了更易用的动画展示模板。

主要特性:

  • 全屏遮罩展示
  • 支持多种位置布局(居中、顶部、底部)
  • 自动播放完成后隐藏
  • 支持手动关闭按钮

使用方法

基础使用

  1. 引入组件
<template>
  <view>
    <!-- 基础使用 -->
    <SvgaPlayer
      ref="svgaPlayerRef"
      :url="animationUrl"
      :width="300"
      :height="300"
      :canvas-id="'myCanvas'"
      :loops="1"
      :clears-after-stop="true"
    />

    <!-- 模板组件使用 -->
    <AnimationTemplate
      ref="animationTemplateRef"
      :animation-url="animationUrl"
      :width="400"
      :height="400"
      :loops="1"
      :show-close-button="true"
      @close="handleAnimationClose"
      @animationStart="handleAnimationStart"
      @animationEnd="handleAnimationEnd"
    />
  </view>
</template>
<script setup>
import SvgaPlayer from '@/components/common/svgaPlayer.vue';
import AnimationTemplate from '@/components/AnimationTemplate/AnimationTemplate.vue';

const svgaPlayerRef = ref(null);
const animationTemplateRef = ref(null);
const animationUrl = 'https://xxx.***.com.cn/frontEnd/files/common/aidfinal1.svga';
</script>

  1. 程序化控制
// 开始播放动画
const startAnimation = () => {
  if (animationTemplateRef.value) {
    // 设置回调函数
    setupAnimationCallbacks();
    // 开始播放
    animationTemplateRef.value.startAnimation();
  }
};

// 设置动画回调
const setupAnimationCallbacks = () => {
  if (animationTemplateRef.value) {
    // 动画完成回调
    animationTemplateRef.value.setOnFinished(() => {
      console.log('动画播放完成');
      animationTemplateRef.value.hide();
    });

    // 帧回调
    animationTemplateRef.value.setOnFrame((frame) => {
      console.log('当前帧:', frame);
    });

    // 进度回调
    animationTemplateRef.value.setOnPercentage((percentage) => {
      console.log('播放进度:', Math.round(percentage * 100) + '%');
    });
  }
};

API 接口

SvgaPlayer Props

参数类型默认值说明
urlString必传SVGA 文件的 URL 地址
widthString/Number300动画宽度
heightString/Number300动画高度
canvasIdString‘svgaCanvas’Canvas 元素的 ID
loopsNumber1循环播放次数,0 表示无限循环
clearsAfterStopBooleanfalse播放完成后是否清除画布

SvgaPlayer Methods

方法名参数说明
initPlayer()-初始化播放器
preload(url, callback)url: 预加载的 URL <br>callback: 回调函数预加载 SVGA 文件
showSvga()-显示并播放动画
startAnimation()-开始播放动画
stopAnimation(clear)clear: 是否清除画布停止播放动画
pauseAnimation()-暂停播放动画
setOnFinished(callback)callback: 回调函数设置播放完成回调
setOnFrame(callback)callback: 回调函数设置帧回调
setOnPercentage(callback)callback: 回调函数设置进度回调

AnimationTemplate Props

参数类型默认值说明
animationUrlString必传SVGA 文件的 URL 地址
widthString/Number300动画宽度
heightString/Number300动画高度
canvasIdString‘animationTemplateCanvas’Canvas 元素的 ID
loopsNumber1循环播放次数
clearsAfterStopBooleanfalse播放完成后是否清除画布
showCloseButtonBooleantrue是否显示关闭按钮
backgroundOpacityNumber0.5背景遮罩透明度
positionString‘center’动画位置:‘center’, ‘top’, ‘bottom’
zIndexNumber9999层级索引

AnimationTemplate Methods

方法名参数说明
show()-显示动画
hide()-隐藏动画
startAnimation()-开始播放动画
stopAnimation()-停止播放动画
preloadAnimation()-预加载动画
setOnFinished(callback)callback: 回调函数设置播放完成回调
setOnFrame(callback)callback: 回调函数设置帧回调
setOnPercentage(callback)callback: 回调函数设置进度回调

AnimationTemplate Events

事件名参数说明
close-动画关闭时触发
animationStart-动画开始播放时触发
animationEnd-动画播放结束时触发

配置参数

SVGA 库配置

SVGA 播放器支持多种路径配置,组件会自动尝试以下路径:

// 相对路径(推荐)
require('../../static/js/svgaplayer.weapp.js');

// 根目录路径
require('/static/js/svgaplayer.weapp.js');

// 当前目录路径
require('./svgaplayer.weapp.js');

Canvas 配置

  • 类型: type="2d" (推荐使用 2D Canvas)
  • 像素比: 自动适配设备像素比
  • 大小: 支持响应式尺寸设置

最佳实践

1. 性能优化

// 预加载动画文件
const preloadAnimations = async () => {
  if (svgaPlayerRef.value) {
    await svgaPlayerRef.value.preload(animationUrl);
  }
};

// 使用缓存避免重复加载
const svgaCollection = {}; // 在组件内部已实现缓存

2. 错误处理

// 监听加载失败
const handleAnimationError = () => {
  console.error('SVGA动画加载失败');
  // 显示备用方案或错误提示
};

3. 内存管理

// 组件销毁时清理资源
onUnmounted(() => {
  if (animationTemplateRef.value) {
    animationTemplateRef.value.stopAnimation();
  }
});

4. 平台兼容

// 检查平台支持
const checkSVGASupport = () => {
  // 组件内部已处理平台兼容性
  return typeof SVGA !== 'undefined';
};

常见问题

Q1: 动画播放不显示或报错?

A1: 检查以下几点:

  • 确认 SVGA 文件 URL 是否正确且可访问
  • 检查 Canvas 元素是否正确渲染
  • 查看控制台是否有 SVGA 库加载错误

Q2: 动画播放卡顿或性能问题?

A2: 优化建议:

  • 使用预加载功能提前加载动画文件
  • 控制动画尺寸,避免过大的 Canvas
  • 及时清理不需要的动画实例

Q3: 在不同平台表现不一致?

A3: 处理方案:

  • 组件已内置多平台适配逻辑
  • 如遇问题,检查是否使用了平台特定的 API
  • 确保 SVGA 文件格式符合标准

Q4: 动画播放完成后如何处理?

A4: 使用回调函数:

animationRef.value.setOnFinished(() => {
  // 播放完成后的处理逻辑
  console.log('动画播放完成');
});

示例代码

完整使用示例

<template>
  <view class="animation-demo">
    <!-- 触发按钮 -->
    <button @click="startAnimation">开始播放动画</button>
    <!-- SVGA动画组件 -->
    <AnimationTemplate
      ref="animationTemplateRef"
      :animation-url="animationUrl"
      :width="400"
      :height="400"
      :loops="1"
      :show-close-button="true"
      position="center"
      @close="handleAnimationClose"
      @animationStart="handleAnimationStart"
      @animationEnd="handleAnimationEnd"
    />
  </view>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import AnimationTemplate from '@/components/AnimationTemplate/AnimationTemplate.vue';

// 组件引用
const animationTemplateRef = ref(null);

// 动画配置
const animationUrl = 'https://static.wxb.com.cn/frontEnd/files/common/aidfinal1.svga';

// 开始播放动画
const startAnimation = () => {
  if (animationTemplateRef.value) {
    setupAnimationCallbacks();
    animationTemplateRef.value.startAnimation();
  }
};

// 设置动画回调
const setupAnimationCallbacks = () => {
  if (animationTemplateRef.value) {
    // 播放完成回调
    animationTemplateRef.value.setOnFinished(() => {
      console.log('SVGA动画播放完成');
      animationTemplateRef.value.hide();

      uni.showToast({
        title: '动画播放完成',
        icon: 'success',
        duration: 1500,
      });
    });

    // 帧回调(可选)
    animationTemplateRef.value.setOnFrame((frame) => {
      console.log('当前帧:', frame);
    });

    // 进度回调(可选)
    animationTemplateRef.value.setOnPercentage((percentage) => {
      console.log('播放进度:', Math.round(percentage * 100) + '%');
    });
  }
};

// 事件处理
const handleAnimationClose = () => {
  console.log('动画被手动关闭');
};

const handleAnimationStart = () => {
  console.log('动画开始播放');
};

const handleAnimationEnd = () => {
  console.log('动画播放结束');
};

// 生命周期
onMounted(() => {
  // 可以在这里预加载动画
  if (animationTemplateRef.value) {
    animationTemplateRef.value.preloadAnimation();
  }
});

onUnmounted(() => {
  // 清理资源
  if (animationTemplateRef.value) {
    animationTemplateRef.value.stopAnimation();
  }
});
</script>
<style lang="scss" scoped>
.animation-demo {
  padding: 20rpx;
}
</style>

基础播放器示例

<template>
  <view class="basic-player">
    <SvgaPlayer
      ref="svgaPlayerRef"
      :url="animationUrl"
      :width="300"
      :height="300"
      :canvas-id="'basicCanvas'"
      :loops="0"
      :clears-after-stop="false"
    />

    <view class="controls">
      <button @click="play">播放</button>
      <button @click="pause">暂停</button>
      <button @click="stop">停止</button>
    </view>
  </view>
</template>
<script setup>
import { ref } from 'vue';
import SvgaPlayer from '@/components/common/svgaPlayer.vue';

const svgaPlayerRef = ref(null);
const animationUrl = 'https://static.wxb.com.cn/frontEnd/files/common/aidfinal1.svga';

const play = () => {
  svgaPlayerRef.value?.startAnimation();
};

const pause = () => {
  svgaPlayerRef.value?.pauseAnimation();
};

const stop = () => {
  svgaPlayerRef.value?.stopAnimation(true);
};
</script>

AnimationTemplate.vue

<template>
  <view 
    class="animation-overlay" 
    v-show="isVisible"
    :style="overlayStyle"
  >
    <view class="animation-container" :style="containerStyle">
      <SvgaPlayer 
        ref="svgaPlayerRef"
        :url="animationUrl"
        :width="width"
        :height="height"
        :canvas-id="dynamicCanvasId"
        :loops="loops"
        :clears-after-stop="clearsAfterStop"
      />
      <!-- 关闭按钮 -->
      <view 
        v-if="showCloseButton" 
        class="close-button" 
        @click="handleClose"
      >
        ×
      </view>
    </view>
  </view>
</template>

<script setup name="AnimationTemplate">
  import { ref, computed, defineProps, defineEmits, defineExpose, onUnmounted } from 'vue';
  import SvgaPlayer from '@/components/common/svgaPlayer.vue';

  // 定义props
  const props = defineProps({
    // 动画URL
    animationUrl: {
      type: String,
      default: 'https://static.wxb.com.cn/frontEnd/files/common/aidfinal1.svga'
    },
    // 动画尺寸
    width: {
      type: [String, Number],
      default: 300
    },
    height: {
      type: [String, Number],
      default: 300
    },
    // canvas ID
    canvasId: {
      type: String,
      default: 'animationTemplateCanvas'
    },
    // 循环次数
    loops: {
      type: Number,
      default: 1
    },
    // 播放完成后是否清除
    clearsAfterStop: {
      type: Boolean,
      default: false
    },
    // 是否显示关闭按钮
    showCloseButton: {
      type: Boolean,
      default: true
    },
    // 背景透明度
    backgroundOpacity: {
      type: Number,
      default: 0.5
    },
    // 动画位置
    position: {
      type: String,
      default: 'center', // center, top, bottom
      validator: (value) => ['center', 'top', 'bottom'].includes(value)
    },
    // z-index
    zIndex: {
      type: Number,
      default: 9999
    }
  });

  // 定义事件
  const emit = defineEmits(['close', 'animationStart', 'animationEnd']);

  // 响应式数据
  const isVisible = ref(false);
  const svgaPlayerRef = ref(null);
  const playCount = ref(0); // 播放次数计数器,用于生成唯一的 canvas ID
  const currentPlayer = ref(null); // 当前播放器实例引用

  // 计算动态的 canvas ID
  const dynamicCanvasId = computed(() => {
    return `${props.canvasId}_${playCount.value}`;
  });

  // 计算样式
  const overlayStyle = computed(() => ({
    backgroundColor: `rgba(0, 0, 0, ${props.backgroundOpacity})`,
    zIndex: props.zIndex
  }));

  const containerStyle = computed(() => {
    const positionStyles = {
      center: {
        top: '50%',
        left: '50%',
        transform: 'translate(-50%, -50%)'
      },
      top: {
        top: '20%',
        left: '50%',
        transform: 'translateX(-50%)'
      },
      bottom: {
        bottom: '20%',
        left: '50%',
        transform: 'translateX(-50%)'
      }
    };
    
    return {
      ...positionStyles[props.position]
    };
  });

  // 销毁canvas和播放器
  const destroyCanvas = () => {
    try {
      // 如果有当前播放器实例,先停止并清理
      if (currentPlayer.value) {
        // 停止动画
        if (currentPlayer.value.stopAnimation) {
          currentPlayer.value.stopAnimation();
        }
        // 清理播放器
        if (currentPlayer.value.clear) {
          currentPlayer.value.clear();
        }
        currentPlayer.value = null;
      }
      
      // 清理canvas元素 - 适配不同平台
      try {
        // #ifdef H5
        const canvasElement = document.getElementById(dynamicCanvasId.value);
        if (canvasElement) {
          const ctx = canvasElement.getContext('2d');
          if (ctx) {
            ctx.clearRect(0, 0, props.width, props.height);
          }
        }
        // #endif
        
        // #ifdef MP-WEIXIN
        const wxCtx = uni.createCanvasContext(dynamicCanvasId.value);
        if (wxCtx) {
          wxCtx.clearRect(0, 0, props.width, props.height);
          wxCtx.draw(true, () => {
            console.log('微信小程序canvas已清空:', dynamicCanvasId.value);
          });
        }
        // #endif
        
        // #ifdef APP-PLUS
        const appCtx = uni.createCanvasContext(dynamicCanvasId.value);
        if (appCtx) {
          appCtx.clearRect(0, 0, props.width, props.height);
          appCtx.draw();
        }
        // #endif
      } catch (canvasError) {
        console.warn('清理canvas内容失败:', canvasError);
      }
      
      console.log('Canvas已销毁:', dynamicCanvasId.value);
    } catch (error) {
      console.error('销毁Canvas失败:', error);
    }
  };

  // 显示动画
  const show = () => {
    isVisible.value = true;
    emit('animationStart');
  };

  // 隐藏动画
  const hide = () => {
    // 先销毁canvas
    destroyCanvas();
    isVisible.value = false;
    emit('close');
  };

  // 开始播放动画
  const startAnimation = () => {
    if (svgaPlayerRef.value) {
      // 先销毁之前的canvas(如果存在)
      destroyCanvas();
      
      // 每次播放时增加计数器,生成新的 canvas ID
      playCount.value++;
      
      show();
      // 稍微延迟确保DOM渲染完成
      setTimeout(() => {
        console.log('开始播放动画,canvas ID:', dynamicCanvasId.value);
        
        // 使用 showSvga 方法并传入动态的 canvas ID
        svgaPlayerRef.value.showSvga(
          props.animationUrl,
          props.loops,
          () => {
            // 动画播放完成后的回调
            console.log('动画播放完成');
            if (props.clearsAfterStop) {
              setTimeout(() => {
                hide();
                emit('animationEnd');
              }, 300);
            } else {
              // 如果不自动清除,也要触发动画结束事件
              emit('animationEnd');
            }
          },
          () => {
            // 动画播放失败后的回调
            console.error('动画播放失败');
            hide();
          },
          props.clearsAfterStop,
          dynamicCanvasId.value // 传入动态的 canvas ID
        );
      }, 200);
    }
  };

  // 停止动画
  const stopAnimation = () => {
    destroyCanvas();
    hide();
    emit('animationEnd');
  };

  // 处理关闭按钮点击
  const handleClose = () => {
    stopAnimation();
  };

  // 预加载动画
  const preloadAnimation = () => {
    if (svgaPlayerRef.value) {
      svgaPlayerRef.value.preload(props.animationUrl);
    }
  };

  // 暴露方法给父组件
  defineExpose({
    show,
    hide,
    startAnimation,
    stopAnimation,
    destroyCanvas,
    preloadAnimation,
    isVisible: () => isVisible.value,
    // 新增:设置回调函数的方法
    setOnFinished: (callback) => {
      if (svgaPlayerRef.value) {
        svgaPlayerRef.value.setOnFinished(callback);
      }
    },
    setOnFrame: (callback) => {
      if (svgaPlayerRef.value) {
        svgaPlayerRef.value.setOnFrame(callback);
      }
    },
    setOnPercentage: (callback) => {
      if (svgaPlayerRef.value) {
        svgaPlayerRef.value.setOnPercentage(callback);
      }
    },
    // 获取播放器实例
    getPlayer: () => {
      if (svgaPlayerRef.value) {
        return svgaPlayerRef.value.getPlayer();
      }
      return null;
    }
  });

  // 组件卸载时清理资源
  onUnmounted(() => {
    destroyCanvas();
  });
</script>

<style lang="scss" scoped>
.animation-overlay {
  position: relative;
  /* 移除注释的样式,保持简洁 */
}

.animation-container {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
}

.close-button {
  position: absolute;
  top: -20rpx;
  right: -20rpx;
  width: 60rpx;
  height: 60rpx;
  background: rgba(0, 0, 0, 0.6);
  color: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36rpx;
  cursor: pointer;
  z-index: 1;
  
  &:active {
    background: rgba(0, 0, 0, 0.8);
  }
}
</style>

SvgPlayer.vue

<template>
  <canvas
    type="2d"
    :style="canvasStyle"
    :id="canvasId"
    :canvas-id="canvasId"
  />
</template>

<script setup>
  import { ref, onMounted, getCurrentInstance, defineProps, computed, defineExpose, nextTick } from 'vue';

  // 定义props
  const props = defineProps({
    url: {
      type: String,
      required: true,
      default: 'https://static.wxb.com.cn/frontEnd/files/common/aidfinal1.svga'
    },
    width: {
      type: [String, Number],
      default: 300
    },
    height: {
      type: [String, Number],
      default: 300
    },
    canvasId: {
      type: String,
      default: 'svgaCanvas'
    },
    loops: {
      type: Number,
      default: 1
    },
    clearsAfterStop: {
      type: Boolean,
      default: false
    }
  });

  const instance = getCurrentInstance();

  // SVGA 缓存集合
  const svgaCollection = {};
  
  // 播放器实例
  let playerInstance = null;
  
  // 回调函数存储
  const callbacks = {
    onFinished: null,
    onFrame: null,
    onPercentage: null
  };

  // 尝试多种方式引入本地 SVGA 库文件
  let SVGA;
  try {
    // 尝试相对路径
    SVGA = require('../../static/js/svgaplayer.weapp.js');
  } catch (e1) {
    try {
      // 尝试从根目录
      SVGA = require('/static/js/svgaplayer.weapp.js');
    } catch (e2) {
      try {
        // 尝试其他可能的路径
        SVGA = require('./svgaplayer.weapp.js');
      } catch (e3) {
        console.warn('SVGA 库加载失败,所有路径都不可用:', { e1, e2, e3 });
        // 如果都失败,可以考虑动态加载或者跳过动画
      }
    }
  }

  // 计算canvas样式
  const canvasStyle = computed(() => {
    const widthStr = typeof props.width === 'number' ? `${props.width}px` : props.width;
    const heightStr = typeof props.height === 'number' ? `${props.height}px` : props.height;
    return `width: ${widthStr}; height: ${heightStr}; `;
  });

  /**
   * 预下载Svga图片
   * @param {string} svgaUrl - SVGA文件URL
   * @param {function} callback - 回调函数
   */
  const preLoadSvga = async (svgaUrl = '', callback = undefined) => {
    if (!SVGA) return;
    
    let parser = new SVGA.Parser();
    let localSvga = svgaCollection[svgaUrl];

    if (!localSvga) {
      try {
        localSvga = await parser.load(svgaUrl);
        svgaCollection[svgaUrl] = localSvga;
      } catch (error) {
        console.error('SVGA加载失败:', error);
        return;
      }
    }

    if (callback) callback(localSvga);
  };

  /**
   * 显示Svga到指定的canvas组件上
   * @param {string} canvasName - canvas ID
   * @param {string} svgaUrl - SVGA文件URL
   * @param {number} loopTimes - 循环次数
   * @param {function} succback - 成功回调
   * @param {function} failback - 失败回调
   * @param {boolean} clearsAfterStop - 播放完成后是否清除
   */
  const showSvga = (canvasName, svgaUrl, loopTimes = 10, succback, failback, clearsAfterStop = false) => {
    if (!SVGA) {
      console.warn('SVGA 库未加载');
      if (failback) failback();
      return;
    }

    // 使用定时器延迟执行,给予足够的时间让 Canvas 完全渲染
    let timer = setTimeout(() => {
      clearTimeout(timer);
      
      // 使用立即执行的异步函数来确保异步操作能够正确执行
      (async () => {
        try {
          console.log('开始加载 SVGA:', svgaUrl);
          
          // 1. 先创建解析器
          let parser = new SVGA.Parser();
          
          // 2. 先下载 SVGA 文件再设置 Canvas (减小错误概率)
          let mySvga;
          
          // 尝试从缓存获取,如果没有则下载
          if (svgaCollection[svgaUrl]) {
            console.log('使用缓存的 SVGA');
            mySvga = svgaCollection[svgaUrl];
          } else {
            console.log('下载 SVGA 文件');
            mySvga = await parser.load(svgaUrl);
            svgaCollection[svgaUrl] = mySvga; // 缓存起来
          }
          
          // 3. 创建播放器并设置 Canvas(使用链式调用方式)
          let player = new SVGA.Player();
          playerInstance = player; // 保存播放器实例
          
          player.setCanvas(`#${canvasName}`, instance)
            .then(() => {
              // 4. 设置参数
              if (loopTimes) player.loops = loopTimes;
              player.clearsAfterStop = clearsAfterStop;
              
              // 5. 设置回调函数
              if (callbacks.onFinished) {
                player.onFinished(callbacks.onFinished);
              } else {
                player.onFinished(() => {
                  console.log('动画播放完成123123');
                  if (succback) succback();
                });
              }
              
              if (callbacks.onFrame) {
                player.onFrame(callbacks.onFrame);
              }
              
              if (callbacks.onPercentage) {
                player.onPercentage(callbacks.onPercentage);
              }
              
              // 6. 设置视频并播放
              player.setVideoItem(mySvga);
              player.startAnimation();
            })
            .catch(error => {
              console.error('SVGA Canvas设置失败:', error);
              if (failback) failback();
            });
        } catch (error) {
          console.error('SVGA播放器设置失败:', error);
          if (failback) failback();
        }
      })();
    }, 1000); // 定时器延迟时间可根据设备性能调整
  };

  // 初始化SVGA播放器
  const initSvgaPlayer = async () => {
    if (!SVGA) {
      console.warn('SVGA 库未加载,跳过动画初始化');
      return;
    }

    try {
      console.log('开始初始化 SVGA 播放器...');
      
      showSvga(
        props.canvasId,
        props.url,
        props.loops,
        () => {
          console.log('SVGA 动画播放成功完成');
        },
        () => {
          console.error('SVGA 动画播放失败');
        },
        props.clearsAfterStop
      );
    } catch (error) {
      console.error('SVGA 播放器初始化失败:', error);
    }
  };

  // 暴露方法给父组件调用
  defineExpose({
    initPlayer: initSvgaPlayer,
    preload: preLoadSvga,
    showSvga: (svgaUrl, loopTimes, succback, failback, clearsAfterStop, customCanvasId) => {
      const canvasId = customCanvasId || props.canvasId;
      showSvga(canvasId, svgaUrl, loopTimes, succback, failback, clearsAfterStop);
    },
    // 设置回调函数的方法
    setOnFinished: (callback) => {
      callbacks.onFinished = callback;
      // 如果播放器已经存在,直接设置
      if (playerInstance) {
        playerInstance.onFinished(callback);
      }
    },
    setOnFrame: (callback) => {
      callbacks.onFrame = callback;
      if (playerInstance) {
        playerInstance.onFrame(callback);
      }
    },
    setOnPercentage: (callback) => {
      callbacks.onPercentage = callback;
      if (playerInstance) {
        playerInstance.onPercentage(callback);
      }
    },
    // 获取播放器实例
    getPlayer: () => playerInstance,
    // 播放控制方法
    startAnimation: () => {
      if (playerInstance) {
        playerInstance.startAnimation();
      }
    },
    stopAnimation: (clear) => {
      if (playerInstance) {
        playerInstance.stopAnimation(clear);
      }
    },
    pauseAnimation: () => {
      if (playerInstance) {
        playerInstance.pauseAnimation();
      }
    }
  });

//   onMounted(async () => {
//     await nextTick();
//     // 稍微延迟一下,确保DOM完全渲染
//     setTimeout(initSvgaPlayer, 500);
//   });
</script>

<style lang="scss" scoped>

</style>

总结

本 SVGA 动画插件为项目提供了完整的矢量动画播放解决方案,具有以下优势:

  1. 高性能: 基于 Canvas 2D 渲染,支持硬件加速
  2. 跨平台: 支持微信小程序、H5 等多端环境
  3. 易用性: 提供了简单易用的组件 API
  4. 功能完整: 支持播放控制、回调监听、预加载等功能
  5. 稳定性: 内置错误处理和资源管理机制

通过合理使用这些组件和 API,可以在项目中轻松实现高质量的动画效果,提升用户体验。


### 微信小程序中的动画播放实现方法 #### 创建动画容器与准备帧图片 为了在微信小程序中实现动画,首先要创建一个用于承载动画的容器组件。该容器不仅负责显示动画内容,还承担着管理动画生命周期的任务[^1]。 ```html <view class="animation-container"> <!-- 帧图片或其他动画元素 --> </view> ``` #### 设置样式与定义关键帧 对于简单的过渡效果或是较为复杂的路径运动,可以通过`wxss`文件内的CSS `@keyframes`规则来定义动画的关键帧序列。这允许开发者精确控制动画过程中各个时间点上的状态变化[^2]。 ```css /* wxss */ @keyframes slideIn { from { transform: translateX(100%); } to { transform: translateX(-100%); } } .animation-element { animation-name: slideIn; animation-duration: 1s; /* 更多属性... */ } ``` #### 使用 JavaScript 控制动画流程 除了依赖 CSS 进行动画描述外,JavaScript 提供了一种更为灵活的方式来操控动画行为。例如,通过监听特定事件触发下一阶段的动作,从而形成连续性的动画队列执行逻辑。 ```javascript Page({ onLoad() { // 初始化页面数据... const query = wx.createSelectorQuery(); query.select('.animated-item').boundingClientRect((rect) => { console.log('Element position:', rect); // 可在此处启动首次动画 }).exec(); }, onAnimationEnd(e) { // 处理单次动画结束后的回调 this.startNextAnimation(); // 开始下一个动画 }, startNextAnimation() { // 更新视图并触发动画链路中的下一项 } }); ``` #### 性能优化建议 考虑到微信小程序运行环境的特点,在设计动画方案时应特别注意性能调优措施。减少不必要的重绘和布局计算,合理规划资源加载时机,并尽可能采用硬件加速机制以保障流畅度[^3]。 - **懒加载**:仅当元素即将进入可视区域之前才开始预取所需素材; - **缓存复用**:重复使用的图像或片段应当存储起来以便快速访问而不必每次都重新请求网络; - **分层绘制**:将频繁变动的部分单独隔离成一层处理,降低整个DOM树更新频率; #### SVGA 格式动画的支持 针对某些特殊需求场景,如直播间礼物特效等场合,则可能涉及到对SVGA这类高级多媒体格式的支持。相较于传统GIF/SVG等形式而言,它能够提供更加细腻逼真的视觉呈现效果的同时保持较小体积优势[^4]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值