Flutter物理動畫的資料實在太少了,等不及大佬們的博客,我就本身研究源碼了。spring
在咱們平時寫的ListView和PageView中,所給定的ClampingScrollPhysics與BouncingScrollPhysics等都有這一系列的物理動畫。bash
一個木板在不光滑的水平面上,給它一個初速度,它會受滑動摩擦力逐漸中止,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,
),
)
],
),
),
);
}
}
複製代碼
效果以下
不使用任何的physics,關於physics中的Simulation能夠參考貓大的掘金
Flutter完整開發實戰詳解(十8、 神奇的ScrollPhysics與Simulation)
預覽
physics: NeverScrollableScrollPhysics(),
複製代碼
簡單計算出手指從按下的點到滑動的點產生的位移並及時控制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,
),
),
複製代碼
預覽
能夠看到它如今沒有任何的慣性
ClampingScrollSimulation對象須要三個參數:
這兒也有一個須要注意的點
須要用
AnimationController(
vsync: this,
value: 0,
lowerBound: double.negativeInfinity,
upperBound: double.infinity,
);
複製代碼
來實例化這個控制器,否則它的默認上下界爲0~1
animationController.addListener(() {
scrollController.jumpTo(animationController.value);
});
複製代碼
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的彈性效果搬來是怎樣呢?🧐
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的源碼。
下一篇是利用本章知識解決各類複雜的滑動聯動問題(應該吧🤪 )。