【问题标题】:How to add fling animation to CustomPainter?如何向 CustomPainter 添加投掷动画?
【发布时间】:2020-07-01 17:54:51
【问题描述】:

我有一个容器,我在其上检测手势,这样我就可以滚动浏览我使用自定义画家创建的图像,就像这样

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 的动画文档并玩弄开源代码,但我仍然没有确定我将如何去做。谁能解释一下这个问题?

【问题讨论】:

    标签: flutter animation dart fling


    【解决方案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 轴上移动(您使用的是水平拖动更新和结束),因此您可以使用值 primaryDelta 和 primaryVelocity,这已经为您提供了主轴上位置和速度的计算(在这种情况下是水平的)。

    在 onHorizo​​ntalDragUpdate 中,您将控制器值添加到拖动的位置(正数表示您正在远离您第一次点击的位置,负数表示您正在向后移动,0 表示您没有移动或拖动)。

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

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

    【讨论】:

    • 这不是我想要的效果,但我已经想通了。我在这里重新提出了我的问题:stackoverflow.com/questions/62713997/… 并且我注意到您之前回答了一个类似的问题,所以我希望您也可以在这个问题上帮助我。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2014-04-27
    • 2014-02-19
    • 1970-01-01
    相关资源
    最近更新 更多