【Flutter】操作结果动画实现

一、动画代码

1.成功动画

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

/*成功*/
class TickSuccess extends StatefulWidget {
  final double width;
  final Color color;
  final double lineWidth;
  final int animationPeriod; // 动画播放周期(影响动画速度)
  TickSuccess({
    Key key,
    this.width = 200,
    this.color,
    this.lineWidth,
    this.animationPeriod = 2000,
  }) : super(key: key);

  @override
  _TickSuccessState createState() => _TickSuccessState();
}

class _TickSuccessState extends State<TickSuccess>
    with TickerProviderStateMixin {
  AnimationController _animationController;
  Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    /// AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字,总时长
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: widget.animationPeriod),
    );

    Animation curveAnimation =
        new CurvedAnimation(parent: _animationController, curve: Curves.ease);

    /// 设置输出范围值,这里分两个动画步骤,0 -> 1表示画圆, 1 -> 1.5 表示画钩
    _animation = Tween<double>(begin: 0.0, end: 1.5).animate(curveAnimation);

    _animationController.addListener(() {
      setState(() {});
    });

    /// 开始动画
    _animationController.forward();
  }

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

  @override
  Widget build(BuildContext context) {
    return Container(
      width: widget.width,
      height: widget.width,
      child: CustomPaint(
        painter: _TickSuccessPainter(_animation.value,
            color: widget.color, lineWidth: widget.lineWidth),
      ),
    );
  }
}

class _TickSuccessPainter extends CustomPainter {
  Paint viewPaint;
  Path path;
  final double progress;
  final Color color;
  final double lineWidth;

  _TickSuccessPainter(this.progress, {this.color, this.lineWidth}) {
    viewPaint = Paint()
      ..strokeCap = StrokeCap.round
      ..isAntiAlias = true
      ..color = color ?? Colors.green
      ..style = PaintingStyle.stroke
      ..strokeWidth = lineWidth ?? 5.0;

    path = Path();
  }

  // Method to convert degree to radians
  num degToRad(num deg) => deg * (Math.pi / 180.0);

  @override
  void paint(Canvas canvas, Size size) {
    /// 原点x,y坐标
    double x = size.width / 2;
    double y = size.width / 2;
    double radius = size.width / 3;
    double len = 1 * (-radius / 6) + radius / 2;

    path.moveTo(x, y);

    /// 背景圆
    path.addArc(
        Rect.fromCenter(
          center: Offset(x, y),
          width: radius * 2,
          height: radius * 2,
        ),
        degToRad(0),
        degToRad(360));

    /// 打钩
    path.moveTo(x - radius / 2, y);
    path.lineTo(x - radius / 6, y + len);
    path.lineTo(x + radius * 1 / 2, y - radius * 1 / 3);

    /// 路径动画
    PathMetrics pathMetrics = path.computeMetrics();

    List list = pathMetrics.toList(); //数组长度与执行moveTo次数相关

    /// 画圆
    PathMetric circularPathMetric = list[0];
    Path circularExtractPath = circularPathMetric.extractPath(
      0.0,
      circularPathMetric.length * progress,
    );
    canvas.drawPath(circularExtractPath, viewPaint);

    /// 画钩
    PathMetric tickPathMetric = list[1];
    Path tickExtractPath = tickPathMetric.extractPath(
      0.0,
      tickPathMetric.length * (progress - 0.5),
    );
    canvas.drawPath(tickExtractPath, viewPaint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

2.失败动画

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

/*失败*/
class CrossFail extends StatefulWidget {
  final double width;
  final Color color;
  final double lineWidth;
  final int animationPeriod; // 动画播放周期(影响动画速度)
  CrossFail(
      {Key key,
      this.width = 200,
      this.color,
      this.lineWidth,
      this.animationPeriod = 2000})
      : super(key: key);

  @override
  _CrossFailState createState() => _CrossFailState();
}

class _CrossFailState extends State<CrossFail> with TickerProviderStateMixin {
  AnimationController _animationController;
  Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    /// AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字,总时长
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: widget.animationPeriod),
    );

    Animation curveAnimation =
        new CurvedAnimation(parent: _animationController, curve: Curves.ease);

    /// 设置输出范围值,这里分两个动画步骤,0 -> 1表示画圆, 1 -> 1.5 表示画叉
    _animation = Tween<double>(begin: 0.0, end: 1.5).animate(curveAnimation);

    _animationController.addListener(() {
      setState(() {});
    });

    /// 开始动画
    _animationController.forward();
  }

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

  @override
  Widget build(BuildContext context) {
    return Container(
        width: widget.width,
        height: widget.width,
        child: CustomPaint(
          painter: _CrossFailPainter(_animation.value,
              color: widget.color, lineWidth: widget.lineWidth),
        ));
  }
}

class _CrossFailPainter extends CustomPainter {
  Paint viewPaint;
  Path path;
  final double progress;
  final Color color;
  final double lineWidth;

  _CrossFailPainter(this.progress, {this.color, this.lineWidth}) {
    viewPaint = Paint()
      ..strokeCap = StrokeCap.round
      ..isAntiAlias = true
      ..color = color ?? Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = lineWidth ?? 5.0;

    path = Path();
  }

  // Method to convert degree to radians
  num degToRad(num deg) => deg * (Math.pi / 180.0);

  @override
  void paint(Canvas canvas, Size size) {
    /// 原点x,y坐标
    double x = size.width / 2;
    double y = size.width / 2;
    double radius = size.width / 3;

    path.moveTo(x, y);

    /// 背景圆
    path.addArc(
        Rect.fromCenter(
          center: Offset(x, y),
          width: radius * 2,
          height: radius * 2,
        ),
        degToRad(0),
        degToRad(360));

    path.moveTo(x - radius / 2, x - radius / 2);
    path.lineTo(x + radius / 2, x + radius / 2);

    path.moveTo(x + radius / 2, x - radius / 2);
    path.lineTo(x - radius / 2, x + radius / 2);

    /// 路径动画
    PathMetrics pathMetrics = path.computeMetrics();

    List list = pathMetrics.toList();

    /// 画圆
    PathMetric circularPathMetric = list[0];
    Path circularExtractPath = circularPathMetric.extractPath(
      0.0,
      circularPathMetric.length * progress,
    );
    canvas.drawPath(circularExtractPath, viewPaint);

    /// 画叉
    PathMetric tickPathMetric = list[1];
    Path tickExtractPath = tickPathMetric.extractPath(
      0.0,
      tickPathMetric.length * (progress - 0.5),
    );
    canvas.drawPath(tickExtractPath, viewPaint);

    PathMetric linePathMetric = list[2];
    Path lineExtractPath = linePathMetric.extractPath(
      0.0,
      linePathMetric.length * progress,
    );
    canvas.drawPath(lineExtractPath, viewPaint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

二、使用

import 'package:flutter/material.dart';
import '../../compontents/animation/cross_fail.dart';
import '../../compontents/animation/tick_success.dart';

/*组件演示*/
class Demo extends StatefulWidget {
  Demo({Key key}) : super(key: key);

  @override
  State<Demo> createState() => _DemoState();
}

class _DemoState extends State<Demo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
      margin: EdgeInsets.all(40),
      child: Wrap(
          spacing: 20,
          crossAxisAlignment: WrapCrossAlignment.center,
          children: <Widget>[
            TickSuccess(width: 40),
            TickSuccess(
              width: 80,
              color: Colors.green[700],
              animationPeriod: 3000,
            ),
            TickSuccess(
              width: 120,
              color: Colors.green[900],
              lineWidth: 10,
              animationPeriod: 4000,
            ),
            CrossFail(width: 40),
            CrossFail(
              width: 80,
              color: Colors.red[700],
              animationPeriod: 3000,
            ),
            CrossFail(
              width: 120,
              color: Colors.red[900],
              lineWidth: 10,
              animationPeriod: 4000,
            ),
          ]),
    ));
  }
}

三、动画演示

猜你喜欢

转载自blog.csdn.net/weixin_45731252/article/details/126608932
今日推荐