老孟導讀:今天分享一個相似「孔雀開屏」的動畫效果,打開新的頁面時,新的頁面從屏幕右上角以圓形逐漸打開到全屏。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】: