
用到的知識點Matrix4矩陣
貝茲曲線
第一步:畫出目標執行大致軌跡路線
首先我們先畫一條二階貝茲曲線,這樣我們能更直觀的觀察到目標移動的大致軌跡。我們先確定二階貝茲曲線的三個點:p0(開始點)、p1(控制點)、p2(結束點),像上圖的紅線軌跡,螢幕的中心點開始,到達螢幕的寬2/3的位置,那麼p0和p2的點就可以確定了:// 二階貝茲曲線 p0:開始點、p1:控制點、p2:結束點
Offset p0, p1, p2;
@override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
if (p0 == null) {
p0 = Offset(_size.width/2, _size.height/2);
p1 = Offset(_size.width+_size.width/4, _size.height/4);
p2 = Offset(_size.width*2/3, 0);
}
}
複製程式碼
得到二階貝茲曲線的三個點之後就可以用 CustomPaint 把它畫出來了,因為每次 setState 之後,build 會重新走一遍,所以我加了判斷讓它們只初始化一次就夠了。
第二步:放置紙飛機在螢幕的中心點
Flutter裡面使用矩陣是透過 Transform 這個 Widget 來設定的,Transform 有多個擴展建構方法,例如 Transform.rotate、Transform.translate、Transform.scale,有興趣的可以自己去了解一下,在這裡我們使用 Transform 預設的建構方法才能透過 Matrix4 來實現飛機的移動、縮放、旋轉。// 矩陣
Matrix4 _matrix4 = Matrix4.identity();
// 飛機尺寸
Size planeSize;
@override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
if (planeSize == null) {
planeSize = Size(_size.width/4, _size.width/4);
}
return Stack(
children: [
Container(
alignment: Alignment.center,
child: Transform(
transform: _matrix4,
child: SvgPicture.asset(Res.svg_paper_plane, width: planeSize.width, height: planeSize.height,),
),
),
],
);
}
複製程式碼
第三步:設定影片,透過影片計算飛機移動軌跡等
這一步最重要,涉及到移動軌跡、旋轉角度和縮放倍數的計算,這裡面涉及到一些計算,我直接把程式碼貼出來(PS:畢竟我數學太過垃圾,這一步花了我好多時間也沒能找到移動距離、縮放倍數、旋轉角度的最佳計算公式,只能自己一步一步慢慢調到大致的視覺效果)。// 移動軌跡點,即移動物的中心點
Offset bezierCenter;
// 當前移動距離
Offset transSize = Offset(0.0, 0.0);
/// 初始化影片
_initAnim() {
_animationController = AnimationController(duration: Duration(seconds: 3), vsync: this);
_animation = Tween(begin: 0.0, end: 1.0).animate(_animationController);
_animation.addListener(() {
// t 動態變化的值
var t = _animation.value;
if (mounted) {
setState(() {
_matrix4 = Matrix4.identity();
// 根據二階貝茲曲線計算移動軌跡點
double _left = pow(1 - t, 2) * p0.dx + 2 * t * (1 - t) * p1.dx + pow(t, 2) * p2.dx;
double _top = pow(1 - t, 2) * p0.dy + 2 * t * (1 - t) * p1.dy + pow(t, 2) * p2.dy;
// 設定移動
if (bezierCenter == null) {
transSize = Offset(0.0, 0.0);
} else {
transSize = Offset(transSize.dx - (bezierCenter.dx - _left),
transSize.dy - (bezierCenter.dy - _top));
}
_matrix4..translate(transSize.dx, transSize.dy, 0.0);
bezierCenter = Offset(_left, _top);
// 設定縮小倍數
_matrix4..scale((1-t) < 0.4 ? 0.4 : (1-t));
// 設定旋轉角度
double rotate = pi/2*t;
_matrix4..rotateX(rotate > rotate*0.8 ? rotate*0.8 : rotate);
_matrix4..rotateY(rotate > rotate*0.8 ? -rotate*0.8 : -rotate);
_matrix4..rotateZ(rotate > rotate*0.8 ? -rotate*0.8 : -rotate);
});
}
});
}
複製程式碼
最後附錄上完整程式碼(Github):PlaneFly