Flutter 手指拖动实现弹簧动画交互

您所在的位置:网站首页 flutter原生view Flutter 手指拖动实现弹簧动画交互

Flutter 手指拖动实现弹簧动画交互

2023-04-24 18:26| 来源: 网络整理| 查看: 265

Flutter,确实提高了我们的开发效率,写Flutter代码,感觉行云流水,欢迎大家关注与提意见。

物理模拟可以让应用程序的交互感觉逼真和互动,例如,你可能希望为一个 Widget 设置动画,使其看起来像是附着在弹簧上或是重力下落。本文章实现了演示了如何使用弹簧模拟将小部件从拖动的点移回中心。

Flutter文章目录 公众号 biglead

实现步骤如下

设置动画控制器 使用手势移动小部件 为小部件制作动画 计算速度以模拟弹簧运动 1 创建一个动画控制器

首页创建一个测试使用的Demo页面

void main() { runApp(const MaterialApp(home: PhysicsCardDragDemo())); } class PhysicsCardDragDemo extends StatelessWidget { const PhysicsCardDragDemo({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: const DraggableCard( child: FlutterLogo( size: 128, ), ), ); } } 复制代码

DraggableCard 是自定义的一个 StatefulWidget,代码如下:

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, ), ); } } 复制代码

然后在 _DraggableCardState 中创建一个动画控制器,并在页面销毁的时候释放动画控制器,代码如下:

class _DraggableCardState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1)); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Align( child: Card( child: widget.child, ), ); } } 复制代码

SingleTickerProviderStateMixin是用来在StatefulWidget中管理单个AnimationController的Mixin;它提供了一个TickerProvider,用于将AnimationController与TickerProviderStateMixin一起使用。

TickerProviderStateMixin提供了一个Ticker,它可以在每个frame中调用AnimationController的方法,这使得AnimationController可以在每个frame中更新动画。

2 使用手势移动Widget

在 _DraggableCardState 中,结合使用 Alignment 与 GestureDetector,代码如下:

class _DraggableCardState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; Alignment _dragAlignment = const Alignment(0, 0); @override Widget build(BuildContext context) { var size = MediaQuery.of(context).size; return GestureDetector( onPanDown: (details) {}, onPanUpdate: (details) { _dragAlignment += Alignment( details.delta.dx / (size.width / 2), details.delta.dy / (size.height / 2), ); setState(() { }); }, onPanEnd: (details) {}, child: Align( alignment: _dragAlignment, child: Card( child: widget.child, ), ), ); } 复制代码

GestureDetector用来检测手势,例如轻触、滑动、拖动等,可以用来实现各种交互效果。

Alignment用于控制子widget在父widget中的位置。可以通过Alignment的构造函数来指定子widget相对于父widget的位置,如Alignment.topLeft表示子widget位于父widget的左上角。也可以通过FractionalOffset来指定子widget相对于父widget的位置,如FractionalOffset(0.5, 0.5)表示子widget位于父widget的中心。Alignment还可以与Stack一起使用,实现多个子widget的定位。 在这里插入图片描述

3 创建一个动画Widget

我们需要实现,当手指抬起时,被移动的 Widget 动画的方式弹回去。

在这里需要一个 Animation ,再定义一个 runAnimation 方法,同时为 第一步创建的动画控制器添加一个更新监听。

class _DraggableCardState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1)); _controller.addListener(() { setState(() { _dragAlignment = _animation.value; }); }); } void _runAnimation() { _animation = _controller.drive( AlignmentTween( begin: _dragAlignment, end: Alignment.center, ), ); _controller.reset(); _controller.forward(); } } 复制代码

然后在手指抬起的时候,执行动画,将被移动的 Widget (如这里的图片)以动画的方式移动回原位:

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

在这里插入图片描述

4 计算速度以模拟弹簧运动

最后一步是做一些数学运算,计算小部件完成拖动后的速度。这是为了使小部件在被拍回之前能够以这种速度逼真地继续。(_runAnimation方法已经通过设置动画的开始和结束对齐来设置方向。)

导入包如下:

import 'package:flutter/physics.dart'; 复制代码

onPanEnd回调提供了一个DragEndDetails对象。此对象提供指针停止接触屏幕时的速度。速度以像素每秒为单位,但Align小部件不使用像素。它使用介于[-1.0,-1.0]和[1.0,1.0]之间的坐标值,其中[0.0,0.0]表示中心。步骤2中计算的大小用于将像素转换为该范围内的坐标值。

然后修改 runAnimation 执行动画函数如下:

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, ); //SpringSimulation用来模拟一个弹簧的运动,可以用于创建具有弹性的动画效果。 final simulation = SpringSimulation(spring, 0, 1, -unitVelocity); _controller.animateWith(simulation); } 复制代码

然后在手指抬起的时候调用

onPanEnd: (details) { _runAnimation(details.velocity.pixelsPerSecond, size); }, 复制代码

在这里插入图片描述 如果有兴趣可以关注一下公众号 biglead ,每周都会有 java、Flutter、小程序、js 、英语相关的内容分享

本文正在参加「金石计划」



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3