Flutter svga 播放及图片替换

前言

SVGA是一种动画格式,可以兼容安卓、ios和web,可以实现很多复杂的动画,这样开发就不用头疼canvas来实现动画时的卡顿优化问题了,那么接下来简单介绍一下再Flutter中怎么使用svga

引入

首先目前的官方SVGA插件不支持Dart 3x 版本,想要在最新版本使用SVGA需要自己去封装插件实现或者降低Dart版本。

导入地址:pub.dev/packages/sv...

csharp 复制代码
flutter pub add svgaplayer_flutter
// 或者在pubspec.yaml文件中添加
svgaplayer_flutter: ^2.2.0

实现效果展示

如上所示需要播放炸弹动画并且改变动画内部的值,如果按照一般的方案去实现的话,在动图上方放一个文本区域写文本,那么炸弹的膨胀和伸缩过程并不会同步到文字上,需要文字也有效果的话,那么就需要使用svga的更换图层的功能

代码实现

1. 引入svga

arduino 复制代码
import 'package:svgaplayer_flutter/svgaplayer_flutter.dart';

2. 初始化svga controller

less 复制代码
// 使用svga需要添加控制器并且需要再当前的类中注入mixin
class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  // 引入svga controller
  late SVGAAnimationController controller;
  int num = 20;
  @override
  void initState() {
    controller = SVGAAnimationController(vsync: this);
    // 加载svga
    loadAnimation();
    super.initState();
  }
  ......
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        // svga区域
        child: SizedBox(
          width: 300,
          height: 300,
          child: SVGAImage(controller),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

3. 引入svga资源

ini 复制代码
void loadAnimation() async {
  final videoItem = await SVGAParser.shared.decodeFromAssets("assets/1.svga");
  controller.videoItem = videoItem;
  controller.repeat();
}

到此,svga已经可以展示在页面上了,如下图所示,整个svga的接入已经完成了,想要实现动态替换上面的文字,

需要使用官方提供的方法controller.videoItem?.dynamicItem.setImage去实现

4. 分析svga资源

需要动态替换文字,实际上是不停的进行切图,但是我们需要切换的是百分比数字,我们不可能切100张图片然后去检索替换,这样的方案需要UI小姐姐去切100张图,如果真这么做了,那么UI小姐姐应该已经开始磨刀霍霍了。因此,我们需要自己去生成对应的svga替换图片

4.1. 确认图片规格

首先我们需要和制作svga素材的同学预定好需要替换的图层,需要帮我们留好替换的位置图层,及相应的名称,这里推荐我们使用官方推出的预览网站,去查看需要替换的图层名称和规格:
svga.dev/svga-previe...

我们可以看到我们的小姐姐已经帮我们留好了名为zhadan的图片,并且这个预览网站也告诉了我们图片的大小规格,当然这里也需要和UI小姐姐确认一下最终规格。

4.2. 将文字生成图片流

图片的生成我们使用ui.PictureRecorder()来借助canvas绘制来完成,具体代码如下:

less 复制代码
Future<ui.Image> randerImageByText(
    {required String content,
    required double fontSize,
    Color? color = Colors.white,
    required double width,
    required double height,
    TextAlign textAlign = TextAlign.justify}) async {
  var recorder = ui.PictureRecorder();
  Canvas canvas = Canvas(recorder);
  Paint paint = Paint()..color = Colors.transparent;
  TextPainter textPainter = TextPainter(
    textAlign: textAlign,
    text: TextSpan(
        text: content,
        style: TextStyle(
          color: color,
          fontSize: fontSize,
          // fontFamily: 'DingTalk',
        )),
    textDirection: TextDirection.rtl,
    textWidthBasis: TextWidthBasis.longestLine,
    ellipsis: '...',
    maxLines: 1,
  )..layout(maxWidth: width, minWidth: width);

  double top = (height - textPainter.height) / 2;
  canvas.drawRect(
      Rect.fromLTRB(0, top, 0 + width, textPainter.height), paint);
  // 可以传入minWidth,maxWidth来限制它的宽度,如不传,文字会绘制在一行
  textPainter.paint(canvas, Offset(0, top));
  Picture picture = recorder.endRecording();
  return await picture.toImage(width.toInt(), height.toInt());
}

4.3. 获取到文件流替换图片

我们将获取到的图片流使用setImage方法更换,这样就形成了我们上面看到的效果

php 复制代码
ui.Image? image = await randerImageByText(
    content: "$num%",
    color: Colors.white,
    width: double.parse("90"),
    height: double.parse("38"),
    fontSize: double.parse("30"),
    textAlign: TextAlign.center);
controller.videoItem?.dynamicItem.setImage(image, "zhadan");

全代码展示:

arduino 复制代码
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:svgaplayer_flutter/svgaplayer_flutter.dart';
import 'dart:ui' as ui;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late SVGAAnimationController controller;
  int num = 20;
  @override
  void initState() {
    controller = SVGAAnimationController(vsync: this);
    // myLog("===========${widget.giftUrl}");
    loadAnimation();
    super.initState();
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  void loadAnimation() async {
    final videoItem = await SVGAParser.shared.decodeFromAssets("assets/1.svga");
    controller.videoItem = videoItem;
    ui.Image? image = await randerImageByText(
        content: "$num%",
        color: Colors.white,
        width: double.parse("90"),
        height: double.parse("38"),
        fontSize: double.parse("30"),
        textAlign: TextAlign.center);
    controller.videoItem?.dynamicItem.setImage(image, "zhadan");
    controller.repeat();
  }

  void _incrementCounter() async {
    setState(() {
      num++;
    });
    ui.Image? image = await randerImageByText(
        content: "$num%",
        color: Colors.white,
        width: double.parse("90"),
        height: double.parse("38"),
        fontSize: double.parse("30"),
        textAlign: TextAlign.center);
    controller.videoItem?.dynamicItem.setImage(image, "zhadan");
  }

  // 根据文字生成图片流
  Future<ui.Image> randerImageByText(
      {required String content,
      required double fontSize,
      Color? color = Colors.white,
      required double width,
      required double height,
      TextAlign textAlign = TextAlign.justify}) async {
    var recorder = ui.PictureRecorder();
    Canvas canvas = Canvas(recorder);
    Paint paint = Paint()..color = Colors.transparent;
    TextPainter textPainter = TextPainter(
      textAlign: textAlign,
      text: TextSpan(
          text: content,
          style: TextStyle(
            color: color,
            fontSize: fontSize,
            // fontFamily: 'DingTalk',
          )),
      textDirection: TextDirection.rtl,
      textWidthBasis: TextWidthBasis.longestLine,
      ellipsis: '...',
      maxLines: 1,
    )..layout(maxWidth: width, minWidth: width);

    double top = (height - textPainter.height) / 2;
    canvas.drawRect(
        Rect.fromLTRB(0, top, 0 + width, textPainter.height), paint);
// 可以传入minWidth,maxWidth来限制它的宽度,如不传,文字会绘制在一行
    textPainter.paint(canvas, Offset(0, top));
    Picture picture = recorder.endRecording();
    return await picture.toImage(width.toInt(), height.toInt());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: SizedBox(
          width: 300,
          height: 300,
          child: SVGAImage(controller),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

以上便是我在进行项目开发中的一些svga播放的问题处理,希望能够帮到大家。如果各位有更好的方案或者更高级的写法欢迎多多指教

相关推荐
AiFlutter13 小时前
Flutter 获取手机连接的Wifi信息
flutter
LinXunFeng13 小时前
Flutter - 轻松搞定炫酷视差(Parallax)效果
前端·flutter·开源
刘志辉1 天前
vue传参方法
android·vue.js·flutter
去伪存真1 天前
聊聊Flutter与原生平台通信方式(一)
前端·flutter
Python私教1 天前
macOS 中搭建 Flutter 开发环境
flutter·macos
明似水2 天前
掌握 Flutter 中的 `Overlay` 和 `OverlayEntry`:弹窗管理的艺术
javascript·flutter
Flutter社区2 天前
使用 Flutter 3.19 更高效地开发
flutter·dart
Forever不止如此2 天前
【CustomPainter】绘制圆环
flutter·custompainter·圆环
wills7772 天前
Flutter Error: Type ‘UnmodifiableUint8ListView‘ not found
flutter