老孟导读:今天分享一个相似“孔雀开屏”的动画效果,打开新的页面时,新的页面从屏幕右上角以圆形逐渐打开到全屏。git
先来看下具体的效果微信
不知道这种效果你们叫什么名字?若是有更合适的名字能够在评论处告诉我,下面来讲下如何实现此效果。ide
在使用Navigator进入一个新的页面时,一般用法以下:函数
Navigator.of(context).push(MaterialPageRoute( builder: (context){ return PageB(); } ));
MaterialPageRoute
就包含了切换页面时的动画效果,在iOS上效果是左右滑动切换,在Android上效果是上下滑动,若是想要自定义切换效果如何实现呢?答案是使用PageRouteBuilder
,用法以下:动画
Navigator.of(context).push(PageRouteBuilder(pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { ... }));
在pageBuilder
函数中使用animation
返回新页面的动画效果便可。ui
新的页面以圆形效果逐渐打开,注意并无缩放效果,因此新的页面是被裁减的,新的页面以右上角为圆心,半径逐渐变大进行裁切,就是咱们想要的效果。this
经过上面的分析,使用ClipPath
对新的页面进行裁切code
Navigator.of(context).push(PageRouteBuilder(pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { return AnimatedBuilder( animation: animation, builder: (context, child) { return ClipPath( clipper: CirclePath(animation.value), child: child, ); }, child: PageB(), ); }));
重点是CirclePath
,这就是裁切的路径,blog
class CirclePath extends CustomClipper<Path> { CirclePath(this.value); final double value; @override Path getClip(Size size) { var path = Path(); double radius = value * sqrt(size.height * size.height + size.width * size.width); path.addOval(Rect.fromLTRB( size.width - radius, -radius, size.width + radius, radius)); return path; } @override bool shouldReclip(CustomClipper<Path> oldClipper) { return true; } }
因为Path
没有直接添加圆形的API函数,所以使用椭圆方法,只需将椭圆的矩形区域设置为正方形,那么裁切出来的就是圆形。继承
半径的最大值并非屏幕的宽或者高,而是屏幕的对角线长度。
因为是从右上角开始,并且裁切的矩形区域必须是正方形,因此裁切的矩形区域是超出页面区域的。
若是不少页面都用到了这个效果,能够进行封装,相似于MaterialPageRoute
,封装以下:
class CirclePageRoute extends PageRoute { CirclePageRoute({ @required this.builder, this.transitionDuration = const Duration(milliseconds: 500), this.opaque = true, this.barrierDismissible = false, this.barrierColor, this.barrierLabel, this.maintainState = true, }); final WidgetBuilder builder; @override final Duration transitionDuration; @override final bool opaque; @override final bool barrierDismissible; @override final Color barrierColor; @override final String barrierLabel; @override final bool maintainState; @override Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { return AnimatedBuilder( animation: animation, builder: (context, child) { return ClipPath( clipper: CirclePath(animation.value), child: child, ); }, child: builder(context), ); } }
使用
Navigator.of(context).push(CirclePageRoute(builder: (context) { return PageB(); }));
若是你查看CupertinoPageRoute
、MaterialPageRoute
、PageRouteBuilder
的源码,你会发现这3个都是继承自PageRoute
,因此,不知不觉咱们又学会了自定义路由。
老孟Flutter博客地址(近200个控件用法):http://laomengit.com
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
![]() |
![]() |