如何将自拍动画添加到CustomPainter?

时间:2020-07-01 17:54:51

标签: flutter animation dart fling

我有一个用于检测手势的容器,这样我就可以像我这样用自定义绘画工具创建的图像滚动浏览

class CustomScroller extends StatefulWidget {
  @override
  _CustomScrollerState createState() => _CustomScrollerState();
}

class _CustomScrollerState extends State<CustomScroller>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  double dx = 0;
  Offset velocity = Offset(0, 0);
  double currentLocation = 0;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));

    super.initState();
  }

  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    double width = MediaQuery.of(context).size.width;
    return GestureDetector(
      onHorizontalDragUpdate: (DragUpdateDetails dragUpdate) {
        dx = dragUpdate.delta.dx;
        currentLocation =
            currentLocation + (dx * 10000) / (width * _absoluteRatio);
        setState(() {});
      },
      onHorizontalDragEnd: (DragEndDetails dragUpdate) {
        velocity = dragUpdate.velocity.pixelsPerSecond;
        //_runAnimation(velocity, size);
      },
      child: Container(
        width: width,
        height: 50,
        child: Stack(
          children: <Widget>[
            CustomPaint(
                painter: NewScrollerPainter(1, true, currentLocation),
                size: Size.fromWidth(width)),

          ],
        ),
      ),
    );
  }
}

我已经编写了一些代码,以便在拖动时,指针的dx值用于计算currentLocation,customPainter'NewScrollerPaint'使用该值确定需要绘制的区域。 我本质上是想要一种动画效果,从而使自定义画家“ NewScrollerPainter”从速度拖动中释放出来,将继续滚动直到没有动量。我相信这需要从速度计算出currentLocation的值,然后返回以更新NewScrollerPainter,但是我花了几个小时浏览Flutter的动画文档并使用开放源代码,但我仍然没有确定我将如何去做。有人可以阐明这个问题吗?

1 个答案:

答案 0 :(得分:1)

class CustomScroller extends StatefulWidget {
  @override
  _CustomScrollerState createState() => _CustomScrollerState();
}

class _CustomScrollerState extends State<CustomScroller>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));

    super.initState();
  }

  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
    return GestureDetector(
      onHorizontalDragUpdate: (DragUpdateDetails dragUpdate) {
        _controller.value += dragUpdate.primaryDelta / width;
      },
      onHorizontalDragEnd: (DragEndDetails dragEnd) {
        final double flingVelocity = dragEnd.primaryVelocity / width;
        const spring = SpringDescription(
          mass: 30,
          stiffness: 1,
          damping: 1,
        );
        final simulation = SpringSimulation(spring, _controller.value, 0, flingVelocity); //if you want to use a spring simulation returning to the origin
        final friction = FrictionSimulation(1.5, _controller.value, -(flingVelocity.abs())); //if you want to use a friction simulation returning to the origin
        //_controller.animateWith(friction);
        //_controller.animateWith(simulation);
        _controller.fling(velocity: -(flingVelocity.abs())); //a regular fling based on the velocity you were dragging your widget
      },
      child: Stack(
          children: [
            SlideTransition(
              position: Tween<Offset>(begin: Offset.zero, end: const Offset(1, 0))
          .animate(_controller),
              child: Align(
                alignment: Alignment.centerLeft,
                child: CustomPaint(
                  painter: NewScrollerPainter(),
                  size: Size.fromWidth(width),
                  child: SizedBox(width: width, height: 50)
                ),
              )
            )
          ]
        )
    );
  }
}

我看到您正在尝试仅在X轴上移动(在onHorizo​​ntalDragUpdate和End上使用),因此您可以使用primaryDelta和primaryVelocity值,这些值已经为您提供了在主轴上的位置和速度的计算(在这种情况下为水平)。

在onHorizo​​ntalDragUpdate中,您可以通过添加控制器的值来添加拖动的位置(正数表示您正远离第一次轻按的位置,负数表示您正向后移,0表示您尚未移动或拖动)。

在onHorizo​​ntalDragEnd中,您可以使用primaryVelocity和相同的逻辑来计算速度(正数表示您要移开,负数表示要返回,0表示没有速度,基本上是您在拖动结束之前停止了移动)。您可以使用模拟,也可以仅使用fling方法,并传递具有的速度(正值表示要结束动画,负值表示要消除动画的速度,而0表示不想移动)。

最后,您唯一需要做的就是将您无法与控制器一起移动的小部件包装在SlideTransition中,其中带有由控制器动画的Tween动画开始为零,然后在您要结束的位置结束(在我的情况下,想要从左向右移动