Flutter“孔雀開屏”的動畫效果

Flutter“孔雀開屏”的動畫效果

老孟導讀:今天分享一個類似“孔雀開屏”的動畫效果,打開新的頁面時,新的頁面從屏幕右上角以圓形逐漸打開到全屏。

先來看下具體的效果

Flutter“孔雀開屏”的動畫效果

不知道這種效果大家叫什麼名字?如果有更合適的名字可以在評論處告訴我,下面來說下如何實現此效果。

在使用Navigator進入一個新的頁面時,通常用法如下:

<code>Navigator.of(context).push(MaterialPageRoute(
builder: (context){
return PageB();
}
));/<code>

MaterialPageRoute就包含了切換頁面時的動畫效果,在iOS上效果是左右滑動切換,在Android上效果是上下滑動,如果想要自定義切換效果如何實現呢?答案是使用PageRouteBuilder,用法如下:

<code>Navigator.of(context).push(PageRouteBuilder(pageBuilder:
(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
...
}));/<code>

在pageBuilder函數中使用animation返回新頁面的動畫效果即可。

新的頁面以圓形效果逐漸打開,注意並沒有縮放效果,所以新的頁面是被裁減的,新的頁面以右上角為圓心,半徑逐漸變大進行裁切,就是我們想要的效果。

通過上面的分析,使用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(),
);
}));/<code>

重點是CirclePath,這就是裁切的路徑,

<code>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;
}
}/<code>

由於Path沒有直接添加圓形的API函數,因此使用橢圓方法,只需將橢圓

的矩形區域設置為正方形,那麼裁切出來的就是圓形。

半徑的最大值並不是屏幕的寬或者高,而是屏幕的對角線長度。

由於是從右上角開始,而且裁切的矩形區域必須是正方形,所以裁切的矩形區域是超出頁面區域的。

如果很多頁面都用到了這個效果,可以進行封裝,類似於MaterialPageRoute,封裝如下:

<code>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),
);
}
}/<code>

使用

<code>Navigator.of(context).push(CirclePageRoute(builder: (context) {
return PageB();
}));/<code>

如果你查看CupertinoPageRoute、MaterialPageRoute、PageRouteBuilder的源碼,你會發現這3個都是繼承自PageRoute,所以,不知不覺我們又學會了自定義路由。

交流

老孟Flutter博客地址(近200個控件用法):http://laomengit.com

歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】


分享到:


相關文章: