📜  Flutter – 动画中的物理模拟

📅  最后修改于: 2021-09-23 06:22:33             🧑  作者: Mango

物理模拟Flutter是一个美丽的方式来flutter应用的动画部件,使其看起来更加逼真和互动。这些可用于创建一系列动画,例如由于重力而使容器看起来附着在弹簧上的坠落物体。在本文中,我们将通过构建一个简单的应用程序来探索相同的内容。

按照以下步骤在 Widget 中创建一个简单的物理模拟:

  • 开发一个动画控制器。
  • 使用手势进行移动。
  • 显示动画。
  • 使用速度来模拟弹跳运动。

让我们详细讨论它们:

开发动画控制器:

要创建动画控制器,请创建一个名为 DraggableCard 的 StatefulWidget,如下所示:

Dart
import 'package:flutter/material.dart';
  
main() {
  runApp(MaterialApp(home: PhysicsCardDragDemo()));
}
  
class PhysicsCardDragDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: DraggableCard(
        child: FlutterLogo(
          size: 128,
        ),
      ),
    );
  }
}
  
class DraggableCard extends StatefulWidget {
  final Widget child;
  DraggableCard({this.child});
  
  @override
  _DraggableCardState createState() => _DraggableCardState();
}
  
class _DraggableCardState extends State {
  @override
  void initState() {
    super.initState();
  }
  
  @override
  void dispose() {
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Align(
      child: Card(
        child: widget.child,
      ),
    );
  }
}


Dart
@override
Widget build(BuildContext context) {
  var size = MediaQuery.of(context).size;
  return GestureDetector(
    onPanDown: (details) {},
    onPanUpdate: (details) {
      setState(() {
        _dragAlignment += Alignment(
          details.delta.dx / (size.width / 2),
          details.delta.dy / (size.height / 2),
        );
      });
    },
    onPanEnd: (details) {},
    child: Align(
      alignment: _dragAlignment,
      child: Card(
        child: widget.child,
      ),
    ),
  );
}


Dart
Animation _animation;
 
void _runAnimation() {
  _animation = _controller.drive(
    AlignmentTween(
      begin: _dragAlignment,
      end: Alignment.center,
    ),
  );
 _controller.reset();
 _controller.forward();
}


Dart
@override
void initState() {
  super.initState();
  _controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
  _controller.addListener(() {
    setState(() {
      _dragAlignment = _animation.value;
    });
  });
}


Dart
child: Align(
  alignment: _dragAlignment,
  child: Card(
    child: widget.child,
  ),
),


Dart
onPanDown: (details) {
 _controller.stop();
},
onPanUpdate: (details) {
 setState(() {
   _dragAlignment += Alignment(
     details.delta.dx / (size.width / 2),
     details.delta.dy / (size.height / 2),
   );
 });
},
onPanEnd: (details) {
 _runAnimation();
},


Dart
void _runAnimation(Offset pixelsPerSecond, Size size) {
  _animation = _controller.drive(
    AlignmentTween(
      begin: _dragAlignment,
      end: Alignment.center,
    ),
  );
  
  final unitsPerSecondX = pixelsPerSecond.dx / size.width;
  final unitsPerSecondY = pixelsPerSecond.dy / size.height;
  final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
  final unitVelocity = unitsPerSecond.distance;
  
  const spring = SpringDescription(
    mass: 30,
    stiffness: 1,
    damping: 1,
  );
  
  final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
  
  _controller.animateWith(simulation);
}


Dart
onPanEnd: (details) {
  _runAnimation(details.velocity.pixelsPerSecond, size);
},


Dart
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
  
main() {
  runApp(MaterialApp(home: PhysicsCardDragDemo()));
}
  
class PhysicsCardDragDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GeeksForGeeks'),
        backgroundColor: Colors.green,
      ),
      body: DraggableCard(
        child: Container(
          width: 140,
          height: 140,
          decoration: BoxDecoration(
            color: Colors.green
          ),
        )
      ),
    );
  }
}
  
class DraggableCard extends StatefulWidget {
  final Widget child;
  DraggableCard({this.child});
  
  @override
  _DraggableCardState createState() => _DraggableCardState();
}
  
class _DraggableCardState extends State
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  
  
  Alignment _dragAlignment = Alignment.center;
  
  Animation _animation;
  
  void _runAnimation(Offset pixelsPerSecond, Size size) {
    _animation = _controller.drive(
      AlignmentTween(
        begin: _dragAlignment,
        end: Alignment.center,
      ),
    );
    // evaluating velocity
    final unitsPerSecondX = pixelsPerSecond.dx / size.width;
    final unitsPerSecondY = pixelsPerSecond.dy / size.height;
    final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
    final unitVelocity = unitsPerSecond.distance;
  
    const spring = SpringDescription(
      mass: 30,
      stiffness: 1,
      damping: 1,
    );
  
    final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
  
    _controller.animateWith(simulation);
  }
  
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
  
    _controller.addListener(() {
      setState(() {
        _dragAlignment = _animation.value;
      });
    });
  }
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return GestureDetector(
      onPanDown: (details) {
        _controller.stop();
      },
      onPanUpdate: (details) {
        setState(() {
          _dragAlignment += Alignment(
            details.delta.dx / (size.width / 2),
            details.delta.dy / (size.height / 2),
          );
        });
      },
      onPanEnd: (details) {
        _runAnimation(details.velocity.pixelsPerSecond, size);
      },
      child: Align(
        alignment: _dragAlignment,
        child: Card(
          child: widget.child,
        ),
      ),
    );
  }
}


使用手势进行移动:

在这里,我们将使小部件在向任何方向拖动时移动。可以使用处理onPanEndonPanUpdateonPanDown的 GestureDetector 来映射移动,如下所示:

Dart

@override
Widget build(BuildContext context) {
  var size = MediaQuery.of(context).size;
  return GestureDetector(
    onPanDown: (details) {},
    onPanUpdate: (details) {
      setState(() {
        _dragAlignment += Alignment(
          details.delta.dx / (size.width / 2),
          details.delta.dy / (size.height / 2),
        );
      });
    },
    onPanEnd: (details) {},
    child: Align(
      alignment: _dragAlignment,
      child: Card(
        child: widget.child,
      ),
    ),
  );
}

显示动画:

使用 Animation 字段和_runAnimation方法产生类似弹簧的效果,如下图所示以显示动画:

Dart

Animation _animation;
 
void _runAnimation() {
  _animation = _controller.drive(
    AlignmentTween(
      begin: _dragAlignment,
      end: Alignment.center,
    ),
  );
 _controller.reset();
 _controller.forward();
}

现在,每当动画控制器产生一个值更新 _dragAlingment 字段,如下所示:

Dart

@override
void initState() {
  super.initState();
  _controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
  _controller.addListener(() {
    setState(() {
      _dragAlignment = _animation.value;
    });
  });
}

现在,使用 _dragAlingment 字段来对齐小部件,如下所示:

Dart

child: Align(
  alignment: _dragAlignment,
  child: Card(
    child: widget.child,
  ),
),

最后使用 GestureDetector 管理动画,如下所示:

Dart

onPanDown: (details) {
 _controller.stop();
},
onPanUpdate: (details) {
 setState(() {
   _dragAlignment += Alignment(
     details.delta.dx / (size.width / 2),
     details.delta.dy / (size.height / 2),
   );
 });
},
onPanEnd: (details) {
 _runAnimation();
},

使用速度来模拟弹跳运动:

首先,导入 Physics 包,如下所示:

import 'package:flutter/physics.dart';

现在使用AnimationControlleranimateWith()方法创建一个类似弹簧的 效果如下图:

Dart

void _runAnimation(Offset pixelsPerSecond, Size size) {
  _animation = _controller.drive(
    AlignmentTween(
      begin: _dragAlignment,
      end: Alignment.center,
    ),
  );
  
  final unitsPerSecondX = pixelsPerSecond.dx / size.width;
  final unitsPerSecondY = pixelsPerSecond.dy / size.height;
  final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
  final unitVelocity = unitsPerSecond.distance;
  
  const spring = SpringDescription(
    mass: 30,
    stiffness: 1,
    damping: 1,
  );
  
  final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
  
  _controller.animateWith(simulation);
}

最后,以速度和大小为参数调用_runAnimation()方法,如下所示:

Dart

onPanEnd: (details) {
  _runAnimation(details.velocity.pixelsPerSecond, size);
},

完整的源代码:

Dart

import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
  
main() {
  runApp(MaterialApp(home: PhysicsCardDragDemo()));
}
  
class PhysicsCardDragDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GeeksForGeeks'),
        backgroundColor: Colors.green,
      ),
      body: DraggableCard(
        child: Container(
          width: 140,
          height: 140,
          decoration: BoxDecoration(
            color: Colors.green
          ),
        )
      ),
    );
  }
}
  
class DraggableCard extends StatefulWidget {
  final Widget child;
  DraggableCard({this.child});
  
  @override
  _DraggableCardState createState() => _DraggableCardState();
}
  
class _DraggableCardState extends State
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  
  
  Alignment _dragAlignment = Alignment.center;
  
  Animation _animation;
  
  void _runAnimation(Offset pixelsPerSecond, Size size) {
    _animation = _controller.drive(
      AlignmentTween(
        begin: _dragAlignment,
        end: Alignment.center,
      ),
    );
    // evaluating velocity
    final unitsPerSecondX = pixelsPerSecond.dx / size.width;
    final unitsPerSecondY = pixelsPerSecond.dy / size.height;
    final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
    final unitVelocity = unitsPerSecond.distance;
  
    const spring = SpringDescription(
      mass: 30,
      stiffness: 1,
      damping: 1,
    );
  
    final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
  
    _controller.animateWith(simulation);
  }
  
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
  
    _controller.addListener(() {
      setState(() {
        _dragAlignment = _animation.value;
      });
    });
  }
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return GestureDetector(
      onPanDown: (details) {
        _controller.stop();
      },
      onPanUpdate: (details) {
        setState(() {
          _dragAlignment += Alignment(
            details.delta.dx / (size.width / 2),
            details.delta.dy / (size.height / 2),
          );
        });
      },
      onPanEnd: (details) {
        _runAnimation(details.velocity.pixelsPerSecond, size);
      },
      child: Align(
        alignment: _dragAlignment,
        child: Card(
          child: widget.child,
        ),
      ),
    );
  }
}

输出:

想要一个更快节奏和更具竞争力的环境来学习 Android 的基础知识吗?
单击此处前往由我们的专家精心策划的指南,旨在让您立即做好行业准备!