zoukankan      html  css  js  c++  java
  • Flutter仿掘金点赞效果

    老孟导读:今天分享一下如何实现掘金点赞效果,这不仅仅是一篇技术文章,还是一篇解决问题思路的文章,遇到一个需求时,如何拆分需求,然后一步一步实现,这个过程比单纯的技术(此文)更有含金量。

    先来看一下掘金点赞的效果:

    jue_1

    说点题外话,感谢一下二哥沉默王二 ),给了我很多建议和帮助,公众号搜索沉默王二即可关注。

    遇到组合动画效果时,首先拆分一下这个动画,以掘金点赞效果为例,共分为3个动画效果:

    1. 小手图标改变颜色并且缩放一下。
    2. 圆环由粗变细,透明度逐渐变为0。
    3. 最外圈小点点透明度逐渐变为0。

    拆分好了之后,就一步一步实现其效果。

    小手缩放效果

    小手缩放效果需要2个图标,选中和未选中两种状态,我从阿里的图标库中选了2个类似的图标(未找到一摸一样的)。两种状态的图标定义如下:

    ///
    /// 未点赞icon
    ///
    const Icon _unLikeIcon = Icon(
      IconData(0xe60a, fontFamily: 'appIconFonts'),
    );
    
    ///
    /// 点赞icon
    ///
    const Icon _likeIcon = Icon(
      IconData(0xe60c, fontFamily: 'appIconFonts'),
      color: Color(0xFF1afa29),
    );
    

    关于如何使用阿里的图标库中的图标可以查看此文章

    由于小手图标的动画效果是放大->还原,使用组合动画实现其效果,代码如下:

    @override
    initState() {
      _animationController =
          AnimationController(duration: Duration(milliseconds: 300), vsync: this);
    
    
      _iconAnimation = Tween(begin: 1.0, end: 1.3).animate(_animationController);
    
      _iconAnimation = TweenSequence([
        TweenSequenceItem(
            tween: Tween(begin: 1.0, end: 1.3)
                .chain(CurveTween(curve: Curves.easeIn)),
            weight: 50),
        TweenSequenceItem(tween: Tween(begin: 1.3, end: 1.0), weight: 50),
      ]).animate(_animationController);
    }
    
    @override
      Widget build(BuildContext context) {
        return _buildLikeIcon();
      }
    
    _buildLikeIcon() {
        return ScaleTransition(
          scale: _iconAnimation,
          child: widget.like
              ? IconButton(
                  padding: EdgeInsets.all(0),
                  icon: _likeIcon,
                  onPressed: () {
                    _clickIcon();
                  },
                )
              : IconButton(
                  padding: EdgeInsets.all(0),
                  icon: _unLikeIcon,
                  onPressed: () {
                    _clickIcon();
                  },
                ),
        );
      }
    

    添加按钮点击效果:

    _clickIcon() {
      if (_iconAnimation.status == AnimationStatus.forward ||
          _iconAnimation.status == AnimationStatus.reverse) {
        return;
      }
      setState(() {
        widget.like = !widget.like;
      });
      if (_iconAnimation.status == AnimationStatus.dismissed) {
        _animationController.forward();
      } else if (_iconAnimation.status == AnimationStatus.completed) {
        _animationController.reverse();
      }
    }
    

    jue_2

    圆环动画

    圆环的动画效果是线条宽度逐渐变为0,透明度逐渐变为0,相对简单,使用AnimatedBuilder实现:

    _buildCircle() {
      return !widget.like
          ? Container()
          : AnimatedBuilder(
              animation: _circleAnimation,
              builder: (BuildContext context, Widget child) {
                return Container(
                  decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      border: Border.all(
                          color: Color(0xFF5FA0EC)
                              .withOpacity(_circleAnimation.value),
                           _circleAnimation.value * 8)),
                );
              },
            );
    }
    

    定义_circleAnimation

    _circleAnimation =
        Tween(begin: 1.0, end: 0.0).animate(_animationController);
    

    jue_3

    最外圈小点点

    最外圈的小点点动画效果是最简单的,透明度逐渐变为0,但布局相对复杂,围绕小手形成一个圆形,使用Flow实现此布局,Flow是一个非常酷炫的布局组件,更多用法查看此文

    构建单个小圆点

    _buildCirclePoint(double radius, Color color) {
      return !widget.like
          ? Container()
          : AnimatedBuilder(
              animation: _circleAnimation,
              builder: (BuildContext context, Widget child) {
                return Container(
                   radius,
                  height: radius,
                  decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      color: color.withOpacity(_circleAnimation.value)),
                );
              },
            );
    }
    

    构建围绕小手的多个点:

    _buildCirclePoints() {
      return Flow(
        delegate: CirclePointFlowDelegate(),
        children: <Widget>[
          _buildCirclePoint(2, Color(0xFF97B1CE)),
          _buildCirclePoint(5, Color(0xFF4AC6B7)),
          _buildCirclePoint(2, Color(0xFF97B1CE)),
          _buildCirclePoint(5, Color(0xFF4AC6B7)),
          _buildCirclePoint(2, Color(0xFF97B1CE)),
          _buildCirclePoint(5, Color(0xFF4AC6B7)),
          _buildCirclePoint(2, Color(0xFF97B1CE)),
          _buildCirclePoint(5, Color(0xFF4AC6B7)),
          _buildCirclePoint(2, Color(0xFF97B1CE)),
          _buildCirclePoint(5, Color(0xFF4AC6B7)),
          _buildCirclePoint(2, Color(0xFF97B1CE)),
          _buildCirclePoint(5, Color(0xFF4AC6B7)),
          _buildCirclePoint(2, Color(0xFF97B1CE)),
          _buildCirclePoint(5, Color(0xFF4AC6B7)),
          _buildCirclePoint(2, Color(0xFF97B1CE)),
          _buildCirclePoint(5, Color(0xFF4AC6B7)),
        ],
      );
    }
    

    CirclePointFlowDelegate 定义如下:

    class CirclePointFlowDelegate extends FlowDelegate {
      CirclePointFlowDelegate();
    
      @override
      void paintChildren(FlowPaintingContext context) {
        var radius = min(context.size.width, context.size.height) / 2.0;
        //中心点
        double rx = radius;
        double ry = radius;
        for (int i = 0; i < context.childCount; i++) {
          if (i % 2 == 0) {
            double x =
                rx + (radius - 5) * cos(i * 2 * pi / (context.childCount - 1));
            double y =
                ry + (radius - 5) * sin(i * 2 * pi / (context.childCount - 1));
            context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
          } else {
            double x = rx +
                (radius - 5) *
                    cos((i - 1) * 2 * pi / (context.childCount - 1) +
                        2 * pi / ((context.childCount - 1) * 3));
            double y = ry +
                (radius - 5) *
                    sin((i - 1) * 2 * pi / (context.childCount - 1) +
                        2 * pi / ((context.childCount - 1) * 3));
            context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
          }
        }
      }
    
      @override
      bool shouldRepaint(FlowDelegate oldDelegate) => true;
    }
    

    jue_4

    交流

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

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

  • 相关阅读:
    AX 利用windows粘贴板功能实现批量数据快速导出EXCEL
    在ax中怎么对enum类型循环取其中每一个值
    vba 快速定位当前EXCEL最后一栏
    在AX4.0中使用C#脚本的实现
    悟透javascript
    SIGAI机器学习第四集 基本概念
    SIGAI机器学习第十七集 线性模型1
    胸小肌的重要性
    SIGAI机器学习第十六集 支持向量机3
    英语听力
  • 原文地址:https://www.cnblogs.com/mengqd/p/12926338.html
Copyright © 2011-2022 走看看