Как добавить анимацию переброски в CustomPainter?

У меня есть контейнер, в котором я обнаруживаю жесты, чтобы я мог прокручивать изображение, которое я создаю с помощью настраиваемого художника, например

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 (вы используете onHorizontalDragUpdate и End), поэтому вы можете использовать значения primaryDelta и primaryVelocity, которые уже дают вам вычисления положения и скорости по первичной оси (горизонтальной в этом кейс).

В onHorizontalDragUpdate вы перемещаете значение контроллера, добавляя себе позицию перетаскивания (положительный момент: вы удаляетесь от места, на которое впервые коснулись, отрицательно - назад, 0 вы не перемещали и не перетаскивали).

В onHorizontalDragEnd вы вычисляете скорость с помощью primaryVelocity и той же логики (положительный результат вы уходит, отрицательный - возвращаетесь, 0 скорости нет, в основном вы перестали двигаться до того, как закончилось перетаскивание). Вы можете использовать симуляции или просто метод метания и передать имеющуюся скорость (положительный момент - вы хотите завершить анимацию, отрицательный - скорость, при которой вы хотите ее отклонить, и 0 - вы не хотите двигаться).

В конце единственное, что вам нужно сделать, это обернуть свой виджет, который вы не можете перемещать с помощью контроллера, в SlideTransition с анимацией Tween, анимированной контроллером, начиная с нуля и заканчивая тем местом, где вы хотите его закончить (в моем случае я хочу переместить это слева направо)

Другие вопросы по тегам