如何向 CustomPainter 添加投掷动画?

Posted

技术标签:

【中文标题】如何向 CustomPainter 添加投掷动画?【英文标题】:How to add fling animation to 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 的动画文档并玩弄开源代码,但我仍然没有确定我将如何去做。谁能解释一下这个问题?

【问题讨论】:

【参考方案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 从零开始并在您想要结束的位置结束(在我的情况下,我想从左向右移动)

【讨论】:

这不是我想要的效果,但我已经想通了。我在这里重新提出了我的问题:***.com/questions/62713997/… 并且我注意到您之前回答了一个类似的问题,所以我希望您也可以在这个问题上帮助我。谢谢

以上是关于如何向 CustomPainter 添加投掷动画?的主要内容,如果未能解决你的问题,请参考以下文章

如何向滑动 SpriteKit 的方向投掷精灵

制作骰子动画&指定投掷数字

投掷或尝试接住

如何通过 swift 3 的 snapkit 向任何 UI 添加动画?

如何向TabBar添加ColorTween动画?

Flutter:如何将 Canvas/CustomPainter 保存到图像文件中?