如何展示从一个点到另一个点的连线?

5
我使用canvas.drawLine来显示一条线,但我希望用户能够看到它从一个点绘制到另一个点,并且如果可能的话,控制动画的持续时间。(类似于进度条但这是我的自定义小部件)
2个回答

13

您可以使用AnimationController来控制动画的持续时间。

为了“逐步”绘制线条,您可以使用Tween(在开始值和结束值之间进行线性插值)。

然后,您只需要将当前进度传递给您的线条绘制器,并在每个paint()调用canvas.drawLine时计算新的宽度/高度。

工作示例:

import 'package:flutter/material.dart';

class Line extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _LineState();
}

class _LineState extends State<Line> with SingleTickerProviderStateMixin {
  double _progress = 0.0;
  Animation<double> animation;

  @override
  void initState() {
    super.initState();
    var controller = AnimationController(duration: Duration(milliseconds: 3000), vsync: this);

    animation = Tween(begin: 1.0, end: 0.0).animate(controller)
      ..addListener(() {
        setState(() {
          _progress = animation.value;
        });
      });

    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(painter: LinePainter(_progress));
  }
}

class LinePainter extends CustomPainter {
  Paint _paint;
  double _progress;

  LinePainter(this._progress) {
    _paint = Paint()
      ..color = Colors.green
      ..strokeWidth = 8.0;
  }

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawLine(Offset(0.0, 0.0), Offset(size.width - size.width * _progress, size.height - size.height * _progress), _paint);
  }

  @override
  bool shouldRepaint(LinePainter oldDelegate) {
    return oldDelegate._progress != _progress;
  }
}

然后像这样使用它:

import 'package:flutter/material.dart';

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomeState();
  }
}

class _HomeState extends State<Home> {
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text('Line animation'),
        leading: new Icon(Icons.insert_emoticon),
      ),
      backgroundColor: Colors.white,
      body: SizedBox(height: 200, width: 200, child: Line()),
    );
  }
}

这条线将会在大小为200x200的盒子中从坐标点(0,0)绘制,并在3秒内完成。

结果:

图片描述


感谢您的评论@pskink,我已经将Line变成了一个小部件,但这当然不是必须的。我不是Flutter专家,我提出了一种解决方案,也许它没有使用最少的代码,但我认为它易于理解、易于阅读和高效。如果您认为可以改进我的答案并使社区受益,请随时发布另一个答案 :) - Yann39
用户如果认为你的答案更好,仍然可以在之后更改接受的答案。 - Yann39
太好了!感谢你的回答! - dpapadopoulos

9

使用自定义的 CustomPainter 类来传递 Animationsuper 构造函数 - 这样,CustomPainter#paint() 方法会在每个动画的“帧”上自动调用:

class MyCustomPainter extends CustomPainter {
  List points;
  Paint linePaint;
  Animation anim;
  Size size = Size.zero;

  MyCustomPainter(Animation anim) : super(repaint: anim) {
    linePaint = Paint()
      ..style = PaintingStyle.stroke
      ..color = Colors.red
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 16;
    this.anim = anim;
  }

  @override
  void paint(Canvas canvas, Size size) {
    if (size != this.size) {
      print('new size $size');
      this.size = size;
      Rect r = (Offset.zero & size).deflate(linePaint.strokeWidth * 1.5);
      points = [
        [r.topLeft, r.bottomLeft], // begins
        [r.bottomLeft, r.topRight], // ends
      ].map((o) => anim.drive(Tween(begin: o[0], end: o[1]))).toList();
    }
    canvas.drawLine(points[0].value, points[1].value, linePaint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

完整的工作代码如下所示:
import 'package:flutter/material.dart';

void main() {
  runApp(AnimatedPainterTest());
}

class AnimatedPainterTest extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Builder(
          builder: (BuildContext context) {
            AnimationController controller = AnimationController(
              duration: Duration(milliseconds: 500),
              vsync: Scaffold.of(context),
            );
            return Column(
              children: <Widget>[
                RaisedButton(
                  onPressed: () => controller.forward(from: 0.0),
                  child: Text('press me to start the animation'),
                ),
                Expanded(
                  child: SizedBox.expand(
                    child: CustomPaint(
                      painter: MyCustomPainter(controller),
                    ),
                  ),
                )
              ],
            );
          },
        ),
      ),
    );
  }
}

而结果是:

在此输入图像描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接