路由动画组合_Flutter在鸿蒙平台实现复杂过渡效果
·



概述
路由动画组合是将多个动画效果结合起来使用的技术。通过组合不同的动画效果,可以创造出更加丰富和生动的页面切换体验。本章将详细介绍路由动画组合的实现方法、常见组合模式和最佳实践。
核心概念
动画组合的类型
动画组合主要分为以下几种类型:
| 组合类型 | 说明 | 适用场景 |
|---|---|---|
| 同时组合 | 多个动画同时执行 | 创建复杂的进入效果 |
| 顺序组合 | 动画按顺序执行 | 创造层次感 |
| 交错组合 | 动画在不同时间点开始 | 增加动态感 |
组合动画的优势
- 视觉丰富:单一动画效果有限,组合动画可以创造出更多样化的视觉效果
- 层次分明:通过顺序或交错组合,可以创造出层次感
- 用户引导:组合动画可以引导用户注意力到关键元素上
- 品牌特色:独特的组合动画可以成为应用的品牌特色
同时组合动画
基本实现
同时组合动画是最常见的组合方式,多个动画效果同时应用到同一个子组件上。
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const DetailPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: FadeTransition(
opacity: animation,
child: child,
),
);
},
),
);
多种效果同时应用
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const DetailPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return ScaleTransition(
scale: Tween<double>(begin: 0.8, end: 1.0).animate(animation),
child: RotationTransition(
turns: Tween<double>(begin: 0.1, end: 0.0).animate(animation),
child: FadeTransition(
opacity: animation,
child: child,
),
),
);
},
),
);
可配置的同时组合
通过封装,可以创建一个可配置的同时组合动画工具类:
enum AnimationEffect {
slide,
fade,
scale,
rotate,
}
class RouteAnimationComposition {
static Route composeMultipleAnimations(
BuildContext context,
Widget page, {
List<AnimationEffect> effects = const [AnimationEffect.slide, AnimationEffect.fade],
Duration duration = const Duration(milliseconds: 300),
Curve curve = Curves.ease,
}) {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionDuration: duration,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
Widget result = child;
for (var effect in effects) {
result = _applyEffect(effect, animation, result);
}
return result;
},
);
}
static Widget _applyEffect(AnimationEffect effect, Animation<double> animation, Widget child) {
switch (effect) {
case AnimationEffect.slide:
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: child,
);
case AnimationEffect.fade:
return FadeTransition(
opacity: animation,
child: child,
);
case AnimationEffect.scale:
return ScaleTransition(
scale: Tween<double>(begin: 0.8, end: 1.0).animate(animation),
child: child,
);
case AnimationEffect.rotate:
return RotationTransition(
turns: Tween<double>(begin: 0.1, end: 0.0).animate(animation),
child: child,
);
default:
return child;
}
}
}
使用方法
Navigator.push(
context,
RouteAnimationComposition.composeMultipleAnimations(
context,
const DetailPage(),
effects: [AnimationEffect.slide, AnimationEffect.fade, AnimationEffect.scale],
duration: const Duration(milliseconds: 400),
),
);
顺序组合动画
使用 Interval 创建顺序动画
Interval 可以控制动画的执行时间段,通过设置不同的区间,可以实现顺序动画效果。
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const DetailPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
var slideAnimation = CurvedAnimation(
parent: animation,
curve: const Interval(0.0, 0.6, curve: Curves.ease),
);
var fadeAnimation = CurvedAnimation(
parent: animation,
curve: const Interval(0.4, 1.0, curve: Curves.ease),
);
return FadeTransition(
opacity: fadeAnimation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(slideAnimation),
child: child,
),
);
},
),
);
完整的顺序动画示例
static Route sequentialAnimation(BuildContext context, Widget page) {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
var delayedAnimation = CurvedAnimation(
parent: animation,
curve: const Interval(0.2, 1.0, curve: Curves.ease),
);
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(delayedAnimation),
child: FadeTransition(
opacity: animation,
child: child,
),
);
},
);
}
顺序动画的时间控制
| 时间段 | 动画效果 | 说明 |
|---|---|---|
| 0.0-0.2 | 无 | 延迟阶段 |
| 0.2-1.0 | 滑动 + 淡入 | 动画执行阶段 |
交错组合动画
使用多个 Interval 创建交错效果
交错组合动画通过设置不同的开始和结束时间,让不同的动画效果在不同时间点开始和结束。
static Route staggeredAnimation(BuildContext context, Widget page) {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.3),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: animation,
curve: const Interval(0.1, 1.0, curve: Curves.easeOut),
),
),
child: ScaleTransition(
scale: Tween<double>(begin: 0.9, end: 1.0).animate(
CurvedAnimation(
parent: animation,
curve: const Interval(0.2, 1.0, curve: Curves.easeOut),
),
),
child: child,
),
),
);
},
);
}
交错动画的时间线
时间线: 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
|----|----|----|----|----|----|----|----|----|----|
淡入: |<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<|
滑动: |<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<|
缩放: |<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<|
复杂交错动画示例
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const DetailPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return RotationTransition(
turns: Tween<double>(begin: -0.1, end: 0.0).animate(
CurvedAnimation(
parent: animation,
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
),
),
child: ScaleTransition(
scale: Tween<double>(begin: 0.7, end: 1.0).animate(
CurvedAnimation(
parent: animation,
curve: const Interval(0.2, 0.8, curve: Curves.elasticOut),
),
),
child: FadeTransition(
opacity: animation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, -0.2),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: animation,
curve: const Interval(0.3, 1.0, curve: Curves.easeOut),
),
),
child: child,
),
),
),
);
},
transitionDuration: const Duration(milliseconds: 500),
),
);
动画组合的最佳实践
选择合适的组合方式
| 场景 | 推荐组合方式 | 说明 |
|---|---|---|
| 普通页面切换 | 同时组合(slide + fade) | 经典组合,效果稳定 |
| 详情页面 | 同时组合(scale + fade) | 突出内容 |
| 设置页面 | 顺序组合(slide up) | 引导用户注意力 |
| 特殊效果页面 | 交错组合 | 创造独特体验 |
控制动画数量
- 建议数量:2-3个动画效果
- 避免过多:过多动画会让页面显得混乱
- 保持简洁:聚焦于核心效果
动画时长的设置
| 组合类型 | 推荐时长 | 说明 |
|---|---|---|
| 简单组合 | 200-300ms | 快速响应 |
| 中等组合 | 300-400ms | 平衡效果和性能 |
| 复杂组合 | 400-500ms | 充分展示效果 |
使用缓动曲线
static Route composeWithCurve(BuildContext context, Widget page) {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
var curvedAnimation = CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
reverseCurve: Curves.easeInCubic,
);
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(curvedAnimation),
child: FadeTransition(
opacity: curvedAnimation,
child: child,
),
);
},
transitionDuration: const Duration(milliseconds: 350),
);
}
性能优化
使用 RepaintBoundary
对于复杂的组合动画,使用 RepaintBoundary 可以减少不必要的重绘:
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return RepaintBoundary(
child: const DetailPage(),
);
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: FadeTransition(
opacity: animation,
child: child,
),
);
},
),
);
避免过度动画
- 不要在每个页面切换都使用复杂的组合动画
- 只在关键页面使用特殊效果
- 提供关闭动画的选项
使用 AnimatedBuilder
对于需要自定义动画逻辑的场景,使用 AnimatedBuilder:
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const DetailPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return AnimatedBuilder(
animation: animation,
builder: (context, child) {
double value = animation.value;
return Transform(
transform: Matrix4.translationValues(
value * MediaQuery.of(context).size.width,
0,
0,
),
child: Opacity(
opacity: value,
child: child,
),
);
},
child: child,
);
},
),
);
实际应用场景
社交应用首页
Navigator.push(
context,
RouteAnimationComposition.composeMultipleAnimations(
context,
const FeedDetailPage(),
effects: [AnimationEffect.slide, AnimationEffect.fade],
duration: const Duration(milliseconds: 300),
),
);
设置页面
Navigator.push(
context,
RouteAnimationComposition.sequentialAnimation(
context,
const SettingsPage(),
),
);
图片详情页
Navigator.push(
context,
RouteAnimationComposition.staggeredAnimation(
context,
const ImageDetailPage(),
),
);
总结
路由动画组合是创造丰富用户体验的重要技术。通过掌握同时组合、顺序组合和交错组合三种方式,开发者可以根据不同场景选择合适的动画组合策略。在实际应用中,需要注意控制动画数量、合理设置时长、选择合适的缓动曲线,并考虑性能优化。组合动画不仅能提升应用的视觉效果,还能引导用户注意力,创造独特的品牌体验。
常见问题
Q1: 组合动画会影响性能吗?
A: 是的,组合动画会增加渲染开销。建议:
- 使用
RepaintBoundary减少重绘 - 控制动画数量在2-3个
- 设置合理的动画时长
Q2: 如何调试组合动画?
A: 可以通过以下方式调试:
- 使用 Flutter DevTools 的动画检查器
- 添加打印语句观察动画值变化
- 分步实现,逐步添加动画效果
Q3: 组合动画在不同平台上表现一致吗?
A: 基本一致,但需要注意:
- iOS 和 Android 的默认动画风格不同
- 可以根据平台选择不同的动画组合
- 使用
Theme.of(context).platform判断平台
Q4: 如何实现退出时的组合动画?
A: 使用 secondaryAnimation 实现退出动画:
transitionsBuilder: (context, animation, secondaryAnimation, child) {
var enterTransition = SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: FadeTransition(
opacity: animation,
child: child,
),
);
var exitTransition = SlideTransition(
position: Tween<Offset>(
begin: Offset.zero,
end: const Offset(-0.2, 0.0),
).animate(secondaryAnimation),
child: FadeTransition(
opacity: ReverseAnimation(secondaryAnimation),
child: enterTransition,
),
);
return exitTransition;
},
更多推荐



所有评论(0)