Flutter 高仿QQ讨论组头像

作者: ea14cffb33a4 | 来源:发表于2020-07-06 16:35 被阅读0次

    View宽D,蓝色小圆半径r,红色大圆半径R,Y轴上偏移量dy,头像数量n。

    正文

    一、计算每个小圆的中心坐标

    1. 首先需要得到大圆中心坐标。这里头像数量n为3为例。
    //中心坐标x。
    double centerX = width / 2;
    //中心坐标y, 因为3和5在y轴上有偏移量,需要单独计算。
    double centerY = width / 2;
    //小圆半径。
    double r;
    //大圆中心到小圆中心的半径。
    double r1;
    switch (n) {
      case 3:
        r = width / (2 + 4 * math.sin(math.pi * (n - 2) / (2 * n)));
        r1 = r / math.cos(math.pi * (n - 2) / (2 * n));
        double R = r * (1 + math.sin(math.pi / n)) / math.sin(math.pi / n);
        double dy = 0.5 * (width - R - r * (1 + 1 / math.tan(math.pi / n)));
        centerY = dy + r + r1;
        break;    
    }
    
    1. 圆心坐标通用公式,当n为2和4的时候初始角度为-45度。
    for (int i = 0; i < n; i++) {
      double degree1 = (n == 2 || n == 4) ? (-math.pi / 4) : 0;
      double x = centerX + r1 * math.sin(degree1 + i * 2 * math.pi / n);
      double y = centerY - r1 * math.cos(degree1 + i * 2 * math.pi / n);
    }
    

    二、如何实现圆弧缺口

    使用ClipPath组件,自定义clipper。

    class QQClipper extends CustomClipper<Path> {
      QQClipper({
        this.total,
        this.index,
        this.initIndex: 1,
        this.previousX,
        this.previousY,
        this.degree,
        this.arcAngle: 60,
      }) : assert(arcAngle != null && arcAngle >= 0 && arcAngle <= 180);
      //头像数量。
      final int total;
      //头像index。
      final int index;
      //头像initIndex。
      final int initIndex;
      //上一个圆心坐标x。
      final double previousX;
      //上一个圆心坐标y。
      final double previousY;
      //圆弧中心角度。
      final double degree;
      //圆弧角度。
      final double arcAngle;
    
      @override
      Path getClip(Size size) {
        double r = size.width / 2;
        Path path = Path();
        List<Offset> points = List();
        //头像数量为2,头像index为initIndex时,保持整圆。
        if (total == 2 && index == initIndex) {
          path.addOval(Rect.fromLTRB(0, 0, size.width, size.height));
        } else {
          //圆弧路径坐标。
          double spaceA = arcAngle / 2;
          double startA = degree + spaceA;
          double endA = degree - spaceA;
          for (double i = startA; i <= 360 + endA; i = i + 1) {
            double x1 = r + r * math.sin(d2r(i));
            double y1 = r - r * math.cos(d2r(i));
            points.add(Offset(x1, y1));
          }
    
          //圆弧缺口路径坐标。
          double spaceB = math.atan(
                  r * math.sin(d2r(spaceA)) / (2 * r - r * math.cos(d2r(spaceA)))) /
              math.pi *
              180;
          double r1 = (2 * r - r * math.cos(d2r(spaceA))) / math.cos(d2r(spaceB));
          double startB = degree - 180 - spaceB;
          double endB = degree - 180 + spaceB;
          List<Offset> pointsB = List();
          for (double i = startB; i < endB; i = i + 1) {
            double x1 = previousX + r1 * math.sin(d2r(i));
            double y1 = previousY - r1 * math.cos(d2r(i));
            pointsB.add(Offset(x1, y1));
          }
          points.addAll(pointsB.reversed);
          path.addPolygon(points, true);
        }
        return path;
      }
    
      /// degree to radian.
      double d2r(double degree) {
        return degree / 180 * math.pi;
      }
    
      @override
      bool shouldReclip(CustomClipper<Path> oldClipper) {
        return this != oldClipper;
      }
    }
    

    Screenshots

    Github

    nine_grid_view

    若有需要Flutter资源(附送大厂面试真题解析),可以点击链接获取:https://docs.qq.com/doc/DYmpHVUtsSUlmcGd1

    以下是部分内容展示

    相关文章

      网友评论

        本文标题:Flutter 高仿QQ讨论组头像

        本文链接:https://www.haomeiwen.com/subject/iiqgqktx.html