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

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天

前言

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

hupuMedal.gif

思路

在這邊大致列幾個關鍵點

  • 基本運動
  • 邊界碰撞
  • 相互碰撞
  • 球體自轉

基本運動

使用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;
  }
}

ballgif.gif size

後話

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

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

參考文章

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY69Qjd5' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片