交錯式動畫
交錯動畫是一個簡單的概念:視覺變化以一系列操作發生,而不是同時發生。動畫可以是純粹循序的,一個變化接一個變化地發生,或者它可以部分或完全重疊。它也可能會有間隙,其中沒有發生任何變化。
本指南說明如何在 Flutter 中建立交錯動畫。
以下影片示範了 basic_staggered_animation 執行的動畫
在影片中,您會看到單一 Widget 的以下動畫,該動畫一開始是一個帶有稍微圓角的藍色方塊。方塊會依以下順序進行變更
- 淡入
- 變寬
- 在向上移動的同時變得更高
- 轉換成帶有邊框的圓形
- 將顏色變更為橘色
在向前執行後,動畫會反向執行。
交錯動畫的基本結構
#下圖顯示 basic_staggered_animation 範例中使用的 Interval
。您可能會注意到以下特性
- 不透明度在時間軸的前 10% 期間變更。
- 不透明度變更與寬度變更之間會出現一個很小的間隙。
- 在時間軸的最後 25% 期間沒有動畫。
- 增加邊距會讓 Widget 看起來像是向上移動。
- 將邊框圓角半徑增加到 0.5,會將帶有圓角的方形轉換成圓形。
- 邊距和高度變更發生在完全相同的間隔內,但它們不必如此。
若要設定動畫
- 建立一個管理所有
Animation
的AnimationController
。 - 為每個要進行動畫的屬性建立一個
Tween
。Tween
定義值的範圍。Tween
的animate
方法需要parent
控制器,並產生該屬性的Animation
。
- 在
Animation
的curve
屬性上指定間隔。
當控制動畫的值變更時,新動畫的值會變更,從而觸發 UI 更新。
以下程式碼會建立 width
屬性的補間。它會建立一個 CurvedAnimation
,指定一個緩和曲線。請參閱 Curves
以取得其他可用的預先定義動畫曲線。
width = Tween<double>(
begin: 50.0,
end: 150.0,
).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.125,
0.250,
curve: Curves.ease,
),
),
),
begin
和 end
值不必是 double。以下程式碼會使用 BorderRadius.circular()
,為 borderRadius
屬性(控制方形邊角的圓度)建立補間。
borderRadius = BorderRadiusTween(
begin: BorderRadius.circular(4),
end: BorderRadius.circular(75),
).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.375,
0.500,
curve: Curves.ease,
),
),
),
完整的交錯動畫
#與所有互動式 Widget 一樣,完整的動畫由一對 Widget 組成:一個無狀態和一個有狀態 Widget。
無狀態 Widget 指定 Tween
,定義 Animation
物件,並提供一個 build()
函式,該函式負責建立 Widget 樹狀結構的動畫部分。
有狀態 Widget 建立控制器、播放動畫,並建立 Widget 樹狀結構的非動畫部分。當在螢幕中的任何位置偵測到點擊時,動畫就會開始。
basic_staggered_animation 的完整程式碼位於 main.dart 中
無狀態 Widget:StaggerAnimation
#在無狀態 Widget StaggerAnimation
中,build()
函式會執行個體化一個 AnimatedBuilder
——一個用於建立動畫的通用 Widget。AnimatedBuilder
會建立一個 Widget,並使用 Tween
的目前值來設定其設定。此範例會建立一個名為 _buildAnimation()
的函式(執行實際的 UI 更新),並將其指派給其 builder
屬性。AnimatedBuilder 會監聽來自動畫控制器的通知,並在值變更時將 Widget 樹狀結構標示為「髒」。對於動畫的每個刻度,值都會更新,從而導致呼叫 _buildAnimation()
。
class StaggerAnimation extends StatelessWidget {
StaggerAnimation({super.key, required this.controller}) :
// Each animation defined here transforms its value during the subset
// of the controller's duration defined by the animation's interval.
// For example the opacity animation transforms its value during
// the first 10% of the controller's duration.
opacity = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.0,
0.100,
curve: Curves.ease,
),
),
),
// ... Other tween definitions ...
);
final AnimationController controller;
final Animation<double> opacity;
final Animation<double> width;
final Animation<double> height;
final Animation<EdgeInsets> padding;
final Animation<BorderRadius?> borderRadius;
final Animation<Color?> color;
// This function is called each time the controller "ticks" a new frame.
// When it runs, all of the animation's values will have been
// updated to reflect the controller's current value.
Widget _buildAnimation(BuildContext context, Widget? child) {
return Container(
padding: padding.value,
alignment: Alignment.bottomCenter,
child: Opacity(
opacity: opacity.value,
child: Container(
width: width.value,
height: height.value,
decoration: BoxDecoration(
color: color.value,
border: Border.all(
color: Colors.indigo[300]!,
width: 3,
),
borderRadius: borderRadius.value,
),
),
),
);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
builder: _buildAnimation,
animation: controller,
);
}
}
有狀態 Widget:StaggerDemo
#有狀態 Widget StaggerDemo
會建立 AnimationController
(統籌一切的控制器),指定 2000 毫秒的持續時間。它會播放動畫,並建立 Widget 樹狀結構的非動畫部分。當在螢幕中偵測到點擊時,動畫就會開始。動畫會向前執行,然後向後執行。
class StaggerDemo extends StatefulWidget {
@override
State<StaggerDemo> createState() => _StaggerDemoState();
}
class _StaggerDemoState extends State<StaggerDemo>
with TickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
);
}
// ...Boilerplate...
Future<void> _playAnimation() async {
try {
await _controller.forward().orCancel;
await _controller.reverse().orCancel;
} on TickerCanceled {
// The animation got canceled, probably because it was disposed of.
}
}
@override
Widget build(BuildContext context) {
timeDilation = 10.0; // 1.0 is normal animation speed.
return Scaffold(
appBar: AppBar(
title: const Text('Staggered Animation'),
),
body: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
_playAnimation();
},
child: Center(
child: Container(
width: 300,
height: 300,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.1),
border: Border.all(
color: Colors.black.withOpacity(0.5),
),
),
child: StaggerAnimation(controller:_controller.view),
),
),
),
);
}
}
除非另有說明,否則本網站上的文件反映了 Flutter 的最新穩定版本。本頁面最後更新於 2024-08-01。 檢視原始碼 或 回報問題。