Flutter物理动画的浅析

Flutter物理动画的资料实在太少了,等不及大佬们的博客,我就本身研究源码了。spring

在咱们平时写的ListView和PageView中,所给定的ClampingScrollPhysics与BouncingScrollPhysics等都有这一系列的物理动画。bash

ListView惯性滑动的运动模型

一个木板在不光滑的水平面上,给它一个初速度,它会受滑动摩擦力逐渐中止,Flutter中ClampingScrollPhysics这个physics更近似的模拟了这个动画,而BouncingScrollPhysics其实并不近似于这个动画,抛开ListView达到顶部的动画差别,咱们用力滑动BouncingScrollPhysics的ListView,就会发现,无论给它多大的初速度,它的最大滑动速度远不及ClampingScrollPhysics,在较长的列表中能够表现出来。ide

从物理的角度自定义简单的动画

这部分应该彻底来自高中物理,典型的汽车刹车运动模型。post

  • 这块木板的名字是ListView动画

  • 假设屏幕无限长,且不光滑,存在必定系数的动摩擦因数,ListView有它的宽度,咱们把它画厚一点。ui

  • 设手指离开屏幕时,ListView向右滑动this

受力分析--->spa

因此它在接下来的运动中只受到来自屏幕给它的摩擦力,方向水平向左。 由动力学公式code

f = μN
N = mg
F = m*a
复制代码

f=μN=μmg=macdn

便可得出这个ListView的加速度。因此在水平运动时加速度与物体的质量是无关的。

滑动摩擦力存在的大前提是物体相对有压力

物体在滑动中滑动摩擦力为恒力,此时物体水平协力F即为f,在设备中,μ是动摩擦系数,天然是模拟的一个固定数值,因此整个滑动摩擦力f与加速度都是模拟的。

有如下两个公式

公式中的初末为下标

1.v末=v初+at
2.x=v初*t+0.5*a*t*t
复制代码

因为ListView末速度为0,因此第一个公式可得时间t,代入第二个公式可得它运动的位移x。

通常这种状况,咱们每每用一个快捷的公式

3.v末*v末-v初*v初=2ax
复制代码

因此经过一个公式算出时间,另外两个之一可得出位移。

有了物体还须要运动的时间,跟运动的位移,动画就好写了。再经过公式2算出动画执行时的每一个瞬间物体应该位移到的距离。

代码的简单实现

class SimulationWidget extends StatefulWidget {
  @override
  _SimulationWidgetState createState() => _SimulationWidgetState();
}

class _SimulationWidgetState extends State<SimulationWidget>
    with SingleTickerProviderStateMixin {
  AnimationController animationController;
  double friction = 9; //动摩擦因素
  double distance = 0.0; //运动位移
  double startVelocity = 200; //初速度
  double time; //运动所需时间
  double acceleration; //加速度
  double g=9.8;
  @override
  void initState() {
    super.initState();
    acceleration = friction * g;
    time = -startVelocity / -acceleration;
    animationController = AnimationController(
        vsync: this, duration: Duration(milliseconds: time.toInt() * 1000));
    animationController.addListener(() {
      double tmpTime = time * animationController.value;
      distance = startVelocity * tmpTime - 0.5 * acceleration * tmpTime * tmpTime;
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          animationController.reset();
          animationController.forward();
        },
      ),
      body: Center(
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Padding(
              padding: EdgeInsets.only(left: distance),
              child: Container(
                width: 20,
                height: 20,
                color: Colors.green,
              ),
            )
          ],
        ),
      ),
    );
  }
}
复制代码

效果以下

建立Flutter自定义的物理动画

不使用任何的physics,关于physics中的Simulation能够参考猫大的掘金

Flutter完整开发实战详解(十8、 神奇的ScrollPhysics与Simulation)

咱们先写一个简单的ListView

预览

将physics设置为禁止滑动

physics: NeverScrollableScrollPhysics(),
复制代码

使用GestureDetector接收滑动事件

简单计算出手指从按下的点到滑动的点产生的位移并及时控制ListView的位置

GestureDetector(
    onPanDown: (details){
        preOffset=scrollController.offset;
        onPanDownOffset=details.globalPosition.dx;
    },
    onHorizontalDragUpdate: (details){
        scrollController.jumpTo(preOffset - (details.globalPosition.dx - onPanDownOffset));
    },
        child: Container(
            color: Colors.red,
            height: 200.0,
            width: MediaQuery.of(context).size.width,
        ),
),
复制代码

预览

能够看到它如今没有任何的惯性

建立ClampingScrollPhysics效果的惯性动画

构建Simulation对象

ClampingScrollSimulation对象须要三个参数:

  • position:运动的起始位移
  • velocity:手指离开屏幕每秒的偏移量
  • tolerance:一个公差,属性有运动的距离,时间,每一时刻的速度,这个玩意个人理解不深入,因为我想要与原ListView的惯性效果同样(不考虑到达边界),因此我直接copy的官方源码。

建立动画控制器

这儿也有一个须要注意的点

须要用

AnimationController(
  vsync: this,
  value: 0,
  lowerBound: double.negativeInfinity,
  upperBound: double.infinity,
);
复制代码

来实例化这个控制器,否则它的默认上下界为0~1

监听动画控制器

animationController.addListener(() {
  scrollController.jumpTo(animationController.value);
});
复制代码

经过Simulation来执行动画

animationController.animateWith(simulation);
复制代码

这一节的完整代码,须要放在手指离开屏幕的回调中.

final Tolerance tolerance = Tolerance(
  velocity: 1.0 /
      (0.050 *
          WidgetsBinding.instance.window
              .devicePixelRatio), // logical pixels per second
  distance: 1.0 /
      WidgetsBinding
          .instance.window.devicePixelRatio, // logical pixels
);
double start = scrollController.offset;
ClampingScrollSimulation clampingScrollSimulation = ClampingScrollSimulation(
  position: start,
  velocity: -details.velocity.pixelsPerSecond.dx,
  tolerance: tolerance,
);
animationController = AnimationController(
  vsync: this,
  value: 0,
  lowerBound: double.negativeInfinity,
  upperBound: double.infinity,
);
animationController.reset();
animationController.addListener(() {
  scrollController.jumpTo(animationController.value);
});
animationController.animateWith(clampingScrollSimulation);
复制代码

预览

能够看到,咱们如今不借助physics就让ListView有了惯性滚动的效果

把PageView的弹性效果搬来是怎样呢?🧐

PageView的弹簧效果

double velocity = -details.velocity.pixelsPerSecond.dx;
double maxSize = 10000;
double itemDimension = MediaQuery.of(context).size.width;
double _getPixels(double page, double leading) {
  return (page * itemDimension) - leading;
}

double _getPage(double pixels, double leading) {
  return (pixels + leading) / itemDimension;
}

double _getTargetPixels(
  double pixels,
  Tolerance tolerance,
  double velocity,
  double leading,
) {
  double page = _getPage(pixels, leading);

  if (pixels < 0) {
    return 0;
  }

  if (pixels >= maxSize) {
    return maxSize;
  }
  if (pixels > 0) {
    if (velocity < -tolerance.velocity) {
      page -= 0.5;
    } else if (velocity > tolerance.velocity) {
      page += 0.5;
    }
    return _getPixels(page.roundToDouble(), leading);
  }
}
double start = scrollController.offset;
double target =
    _getTargetPixels(start, tolerance, velocity, 0);
var spring = SpringDescription.withDampingRatio(
  mass: 0.5,
  stiffness: 100.0,
  ratio: 1.1,
);
ScrollSpringSimulation scrollSpringSimulation = ScrollSpringSimulation(
    spring, start, target, velocity,
    tolerance: tolerance);
animationController = AnimationController(
  vsync: this,
  value: 0,
  lowerBound: double.negativeInfinity,
  upperBound: double.infinity,
);

animationController.reset();
animationController.addListener(() {
  scrollController.jumpTo(animationController.value);
});
animationController.animateWith(scrollSpringSimulation);
复制代码

预览

这就实现了PageView中的弹簧动画

关于滑动到边界时候的动画处理仍是须要参考physics的源码。

结语

  • 有的时候仍是须要本身揣摩一下原理,本身多动手,不能一直组别人的轮子
  • 数学跟物理在动画中始终有着很大的做用
  • 高考物理差班上第一名两分始终记着🤣

下一篇是利用本章知识解决各类复杂的滑动联动问题(应该吧🤪 )。

相关文章
相关标签/搜索