携手创造,共同成长!这是我参与「日新方案 8 月更文应战」的第1天

前语

运用虎撲APP時,注意到個人頁面上的勳章展示區,勳章會隨手機的轉動而移動坠落。很喜歡這個作用,也很猎奇是怎麼實現的,便想在flutter上嘗試重現出來,當作練習項目。

Flutter 模仿虎撲APP勳章效果(一)勳章基本運動

思路

在這邊大致列幾個關鍵點

  • 根本運動
  • 邊界磕碰
  • 彼此磕碰
  • 球體自轉

根本運動

运用sensors_plus

我這裡選用加速度感測的監聽,ValueNotifier來觸發改写替代setstate。

import 'package:sensors_plus/sensors_plus.dart';
ValueNotifier<List<double>> phoneXY = ValueNotifier([0, 0]);
StreamSubscription sensor = accelerometerEvents.listen((AccelerometerEvent event) {
  phoneXY.value = [event.x, event.y];
});

由於sensors_plus在安卓端適用SensorManager默認 SENSOR_DELAY_NORMAL靈敏度不高,能够手動修正套件的本地緩存,或是把sensors_plus colne下來改成私有專案(以下是兩種引证私有庫方式)。

sensors_plus:
  git:
    url: https://github.com/xxx/sensors_plus.git
sensors_plus:
  path: xxx/sensors_plus

定義小球

class Ball {
  //位置
  Offset position;
  //顏色
  Color color;
  //半徑
  double radius;
  //速度
  double speedX = 0;
  double speedY = 0;
  Ball({
    //給定默認色
    this.color = Colors.lightGreen,
    required this.position,
    required this.radius,
  });
}

繪製小球

先用List放入兩個小球(徽章)

List<Ball> balls = [];
balls.add(
      Ball(
        radius: 10,
        position: const Offset(100, 100),
        color: Colors.pink,
      ),
    );
balls.add(
      Ball(
        radius: 20,
        position: const Offset(100, 100),
      ),
    );

我的榜首個想法是用canvas繪製,而ValueListenable能够只改写畫布而不重構widget。 我將每次改写都當作一個瞬間,大致把模擬移動簡化成下述公式,這樣就有了小球歲加速度計墜落移動的作用。因為每次執行paint都是一個瞬間,就能够把d時間 dt當作1來忽略。

新速度=舊速度+加速度∗d時間新速度 = 舊速度 + 加速度 * d時間

d位移=新速度∗d時間d位移 = 新速度 * d時間

CustomPaint(
            size: size,
            painter: BallPainter(balls: balls, phoneXY: phoneXY),
),
class BallPainter extends CustomPainter {
  final List<Ball> balls;
  final ValueListenable<List<double>> phoneXY;
  BallPainter({
    required this.balls,
    required this.phoneXY,
  }) : super(repaint: phoneXY);
  final Paint _paint = Paint()..isAntiAlias = true;
  @override
  void paint(Canvas canvas, Size size) {
    for (var ball in balls) {
      //更新速度
      ball.speedX -= phoneXY.value[0] / 20;
      ball.speedY += phoneXY.value[1] / 20;
      //更新位移
      double dx = ball.speedX;
      double dy = ball.speedY;
      //位置
      ball.position += Offset(dx, dy);
      _paint.color = ball.color;
      canvas.drawCircle(ball.position, ball.radius, _paint);
    }
  }
  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    //要能夠改写
    return true;
  }
}
Flutter 模仿虎撲APP勳章效果(一)勳章基本運動

後話

根本的運動到這邊就結束了,這些還是比較基礎的內容共享,如果文中有任何錯誤歡迎大佬們指正。

後面幾個關鍵點我還尚未解決,渐渐處理渐渐上傳,有任何想法也歡迎共享,互相參考互相學習。

參考文章

  • 拿去吧你!Flutter 仿自若 App 裸眼 3D 作用| 8月更文应战 @Nayuta
  • Flutter 雪花飘落的作用-深夜创造 @早上的年轻人