Flutter 中的 3D 运动动画

3D movement animation in Flutter

我正在使用 Transform 在卡片上进行 3D 运动。使用 onPanUpdate 它会一直移动直到完全倾斜。当您触摸靠近边缘的任何点时,我如何限制或控制运动最多进行一半的方式。 这是我应用 Transform:

的方式
    Transform(
                      transform: Matrix4.identity()
                        ..setEntry(3, 2, 0.001) // perspective
                        ..rotateX(0.001 * _offset.dy) // changed
                        ..rotateY(-0.001 * _offset.dx), // changed
                      alignment: FractionalOffset.center,
                      child: GestureDetector(
                        onPanUpdate: (details) => setState(() =>
                                // Offset(-0.1, -0.1) < details.delta ||
                                //         details.delta < Offset(0.1, 0.1)
                                // ?
                                _offset += details.delta
                            // : _offset = _offset
                            ),
                        child: ReusableCard(),
                      ),
                    ),

我使用 物理模拟 概念解决了这个问题。我将 onPanStartonPanEnd 添加到 GestureDetector 并且还定义了一个 controller听变化控制动作流畅

class ReusableCard extends StatefulWidget {

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

class _ReusableCardState extends State<ReusableCard>
    with TickerProviderStateMixin {
  AnimationController _offsetController;
  Animation<Offset> _offsetAnimation;
  Offset _offset = Offset.zero;
  static const double limitParallaxOffset = 350;

  void _runOffsetAnimation() {
    _offsetAnimation = _offsetController.drive(Tween<Offset>(
      begin: _offset,
      end: Offset.zero,
    ));
    _offsetController.reset();
    _offsetController.forward();
  }

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

    _offsetController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 300));
    _offsetController.addListener(() {
      setState(() {
        _offset = _offsetAnimation.value;
      });
    });
  }

  @override
  void dispose() {
    super.dispose();
    _offsetController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Transform(
      transform: Matrix4.identity()
        ..setEntry(3, 2, 0.001) // perspective
        ..rotateX(0.001 * _offset.dy) // changed
        ..rotateY(-0.001 * _offset.dx), // changed
      alignment: FractionalOffset.center,
      child: GestureDetector(
        onPanStart: (details) {
          _offsetController.stop();
        },
        onPanUpdate: (details) {
          setState(() {
            Offset(-limitParallaxOffset, -limitParallaxOffset) < _offset &&
                    _offset < Offset(limitParallaxOffset, limitParallaxOffset)
                ? _offset += details.delta / 2
                : _offset = Offset.zero;
          });
        },
        onPanEnd: (details) {
          _runOffsetAnimation();
        },
        child: Container(
        decoration: BoxDecoration(
          boxShadow: [
            BoxShadow(
                color: Colors.blue,
                offset: Offset(0, 20),
                blurRadius: 30.0),
            ],
          ),
         height: 100,
         width: 100,
        ),
      ),
    );
  }
}