• Flutter “孔雀开屏”的动画效果


    老孟导读:今天分享一个类似“孔雀开屏”的动画效果,打开新的页面时,新的页面从屏幕右上角以圆形逐渐打开到全屏。

    先来看下具体的效果

    ping

    不知道这种效果大家叫什么名字?如果有更合适的名字可以在评论处告诉我,下面来说下如何实现此效果。

    在使用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返回新页面的动画效果即可。

    新的页面以圆形效果逐渐打开,注意并没有缩放效果,所以新的页面是被裁减的,新的页面以右上角为圆心,半径逐渐变大进行裁切,就是我们想要的效果。

    通过上面的分析,使用ClipPath对新的页面进行裁切

    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,这就是裁切的路径,

    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();
    }));
    

    如果你查看CupertinoPageRouteMaterialPageRoutePageRouteBuilder的源码,你会发现这3个都是继承自PageRoute,所以,不知不觉我们又学会了自定义路由。

    交流

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

    欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:

  • 相关阅读:
    PYTHON 爬虫笔记八:利用Requests+正则表达式爬取猫眼电影top100(实战项目一)
    PYTHON 爬虫笔记七:Selenium库基础用法
    PYTHON 爬虫笔记六:PyQuery库基础用法
    java练习题(六)
    js练习题(三)
    js练习题(一)
    表格
    MAYA网络Z1J10928
    //绑定select下拉框change事件只执行一次
    枚举数组转换字符串
  • 原文地址:https://www.cnblogs.com/mengqd/p/12952356.html
Copyright © 2020-2023  润新知