Flutter 三角动画元素未同时到达其所需位置

Flutter trigonometry animation elements not reaching their desired positions at the same time

动画是这样开始的,两个圆圈都在中心,但由于某种原因,一个圆圈更快地结束。我怎样才能让他们同时到达终点?我不明白为什么这段代码不起作用。任何帮助将不胜感激,谢谢!

  @override
  void paint(Canvas canvas, Size size) {
    var percentFinished = _animation.value;
    var middleY = size.height / 2;
    var middleX = size.width / 2;
    double radius = 40;

    var angles = [180, 0];

    for (var angle in angles) {
      var radians = (angle * pi) / 180;
      var endingX = middleX + cos(radians) * radius;
      var endingY = middleY - sin(radians) * radius;
      var addToY = negativeOrPositiveOrZero(sin(radians)) * percentFinished;
      var addToX = negativeOrPositiveOrZero(cos(radians)) * percentFinished;

      canvas.drawCircle(Offset(endingX * addToX + middleX, endingY * addToY + middleY), 10, Paint());
    }
  }

  int negativeOrPositiveOrZero(double a) {
    int num = a.toInt();
    if (num > 0){
      print("1" + ": " + num.toString());
      return 1;
    }
    else if (num < 0) {
      return -1;
    }
    else {
      return 0;
    }
  }

下面只是我正在谈论的一些屏幕截图。

动画是这样开始的,中间有两个球

但它以这样的状态结束,一个圆圈先于另一个圆圈到达终点。理想的行为是让他们同时到达屏幕的一侧。

我认为你的问题是你如何计算你的endingX

var endingX = middleX + cos(radians) * radius;

您的 endingX 似乎应该是 Canvas 的边与初始位置的圆的周长之间的距离。因此,两个方向都相同:

var endingX = middleX - radius;

然后,对您的代码进行一些简化:

负或正或零

您在 dart:math 中有一个 getter:sign

三角函数

我想您发布的示例比您的实际代码简单得多,此后的简化可能没有意义。

不过要注意,计算机上的近零微积分比较乱!

import 'dart:math';

void main() {
  print('   sin(0).sign: ${sin(0).sign}');
  print('   sin(0).sign: ${sin(0).sign}');
  print('------------------------------------------');
  print('   sin(180*pi/180): ${sin(180*pi/180)}');
  print('!! sin(180*pi/180).sign: ${sin(180*pi/180).sign}');
}
   sin(0).sign: 0
   sin(0).sign: 0
------------------------------------------
   sin(180*pi/180): 1.2246467991473532e-16
!! sin(180*pi/180).sign: 1

更正和简化后的完整示例

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Rolling balls',
      home: Scaffold(
        body: Center(
          child: Container(
            width: 300,
            height: 200,
            color: Colors.amber.shade300,
            child: const MyCustomPaint(),
          ),
        ),
      ),
    );
  }
}

class MyCustomPaint extends StatefulWidget {
  const MyCustomPaint({Key? key}) : super(key: key);

  @override
  _MyCustomPaintState createState() => _MyCustomPaintState();
}

class _MyCustomPaintState extends State<MyCustomPaint>
    with SingleTickerProviderStateMixin {
  late Animation<double> _animation;
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 4),
    );

    Tween<double> _animationTween = Tween(begin: 0.0, end: 1.0);

    _animation = _animationTween.animate(_controller)
      ..addListener(() => setState(() {}))
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _controller.reverse();
        } else if (status == AnimationStatus.dismissed) {
          _controller.forward();
        }
      });
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: MyCustomPainter(_animation.value),
    );
  }
}

class MyCustomPainter extends CustomPainter {
  final double percentFinished;

  MyCustomPainter(this.percentFinished);

  @override
  void paint(Canvas canvas, Size size) {
    double middleY = size.height / 2;
    double middleX = size.width / 2;
    double radius = size.width / 20;

    Paint paint = Paint()..color = Colors.black;

    for (int direction in [1, -1]) {
      var endingX = middleX - radius;
      var addToX = direction * percentFinished;
      canvas.drawCircle(
        Offset(endingX * addToX + middleX, middleY),
        radius,
        paint,
      );
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}