Flutter 源码梳理系列(二十一.一):Rect

417 阅读7分钟

Rect

 Rect:一个相对于给定原点的不可变的、二维的、轴对齐的、浮点数矩形。

 可以使用 Rect 的其中一个构造函数或者使用 Offset 和 Size 以及 & 运算符来创建一个 Rect:

Rect myRect = const Offset(1.0, 2.0) & const Size(3.0, 4.0);

Constructors

 下面是一组 Rect 的构造函数:

fromLTRB

 根据矩形的左边界、顶部边界、右边界和底部边界构造一个矩形。

image.png

  const Rect.fromLTRB(this.left, this.top, this.right, this.bottom);

fromLTWH

 根据矩形的左边界、顶部边界、宽度和高度构建一个矩形。

 要根据一个 Offset 和一个 Size 构建一个 Rect,可以使用矩形构造运算符 &。如下:Offset.&。

image.png

  const Rect.fromLTWH(double left, double top, double width, double height) : this.fromLTRB(left, top, left + width, top + height);
  
  Rect operator &(Size other) => Rect.fromLTWH(dx, dy, other.width, other.height);
  
  // Rect myRect = Offset.zero & const Size(100.0, 100.0);
  // same as: Rect.fromLTWH(0.0, 0.0, 100.0, 100.0)

fromCenter

 根据其中心点、宽度和高度构造一个矩形。

 假定 center 参数是相对于原点的偏移量。

image.png

  Rect.fromCenter({ required Offset center, required double width, required double height }) : this.fromLTRB(
    center.dx - width / 2,
    center.dy - height / 2,
    center.dx + width / 2,
    center.dy + height / 2,
  );

fromCircle

 构建一个包围给定圆的矩形。

 center 参数被假定为相对于原点的偏移量。

image.png

  Rect.fromCircle({ required Offset center, required double radius }) : this.fromCenter(
    center: center,
    width: radius * 2,
    height: radius * 2,
  );

fromPoints

 构建一个最小的矩形,包围给定的偏移量,将它们视为从原点出发的向量。

image.png

  Rect.fromPoints(Offset a, Offset b) : this.fromLTRB(
    math.min(a.dx, b.dx),
    math.min(a.dy, b.dy),
    math.max(a.dx, b.dx),
    math.max(a.dy, b.dy),
  );

left & top & right & bottom

 仅有的四个最重要的 double 类型的属性。左边界、顶部边界、右边界、底部边界。

  // 这个矩形左边界相对于 x 轴的偏移量。
  final double left;

  // 这个矩形顶部边界相对于 y 轴的偏移量。
  final double top;

  // 这个矩形右边界相对于 x 轴的偏移量。
  final double right;
  
  // 这个矩形底部边界相对于 y 轴的偏移量。
  final double bottom;

width & height & size

 一些几何属性。

  // 这个矩形的左边界和右边界之间的距离。
  double get width => right - left;

  // 这个矩形的顶部边界和底部边界之间的距离。
  double get height => bottom - top;

  // 这个矩形的左上角和右下角之间的距离。
  Size get size => Size(width, height);

hasNaN

 是否有任何维度是 NaN

  bool get hasNaN => left.isNaN || top.isNaN || right.isNaN || bottom.isNaN;

zero

 静态常量 zero。四边分别与左边、顶边、右边和底边都重合的矩形,边长均为零。

  static const Rect zero = Rect.fromLTRB(0.0, 0.0, 0.0, 0.0);

largest

 一个覆盖整个坐标空间的矩形。

 这个矩形覆盖从 -1e9,-1e9 到 1e9,1e9 的空间。这是图形操作有效的空间。

  static const double _giantScalar = 1.0E+9; // matches kGiantRect from layer.h
  static const Rect largest = Rect.fromLTRB(-_giantScalar, -_giantScalar, _giantScalar, _giantScalar);

isInfinite & isFinite & isEmpty

 isInfinite:但凡有一个值是无限的就是无限的。isFinite:必须所有值都是有限的才是有限的。

 isEmpty:即只要一个方向的大值小于等于小值,即数学中的:一个宽度或者高度为 0 的矩形,那么它的面积肯定也是零。

  // 这个矩形的任何坐标是否等于正无穷大。
  // 为了与 Offset 和 Size 保持一致而包含在内。
  bool get isInfinite {
    return left >= double.infinity
        || top >= double.infinity
        || right >= double.infinity
        || bottom >= double.infinity;
  }

  // 这个矩形的所有坐标是否都是有限的。
  bool get isFinite => left.isFinite && top.isFinite && right.isFinite && bottom.isFinite;
  
  // 这个矩形是否包含非零面积。负面积被视为空。
  bool get isEmpty => left >= right || top >= bottom;

shift

 返回一个根据给定偏移量 offset 平移的新矩形。

 如果要通过单独的 x 和 y 分量而不是通过一个 Offset 来平移一个矩形,请考虑使用 translate。

  Rect shift(Offset offset) {
    return Rect.fromLTRB(left + offset.dx, top + offset.dy, right + offset.dx, bottom + offset.dy);
  }

translate

 返回一个新的矩形,其中 translateX 添加到 x 分量中,translateY 添加到 y 分量中。

 要通过 Offset 而不是单独的 x 和 y 分量平移矩形,请考虑使用 shift。

  Rect translate(double translateX, double translateY) {
    return Rect.fromLTRB(left + translateX, top + translateY, right + translateX, bottom + translateY);
  }

inflate

 返回一个新的矩形,其边缘向外移动给定的增量。

 即可以理解为绕着原来的旧矩形扩大了一圈(delta)。(left 和 top 减去 delta,right 和 bottom 加上 delta。)

  Rect inflate(double delta) {
    return Rect.fromLTRB(left - delta, top - delta, right + delta, bottom + delta);
  }

deflate

 返回一个边缘向内移动给定偏移量的新矩形。

 刚好与 inflate 相反,即可以理解为绕着原来的旧矩形缩小了一圈(-delta)。(left 和 top 加上 delta,right 和 bottom 减去 delta。)

  Rect deflate(double delta) => inflate(-delta);

intersect

 返回一个新的矩形,它是给定矩形和该矩形的交集。这两个矩形必须重叠才有意义。如果两个矩形不重叠,则生成的矩形将具有负宽度或高度。

  Rect intersect(Rect other) {
    return Rect.fromLTRB(
      math.max(left, other.left),
      math.max(top, other.top),
      math.min(right, other.right),
      math.min(bottom, other.bottom)
    );
  }

expandToInclude

 返回一个新的矩形,该矩形是包含此矩形和给定矩形的边界框。

  Rect expandToInclude(Rect other) {
    return Rect.fromLTRB(
        math.min(left, other.left),
        math.min(top, other.top),
        math.max(right, other.right),
        math.max(bottom, other.bottom),
    );
  }

overlaps

 该矩形是否与其他矩形有非零重叠区域。

 如果一个矩形的右边界小于等于一个矩形左边界,那肯定这个矩形整体都是在另一个矩形的左边了,它们两个肯定不会重叠。其它判断如此一样。

  bool overlaps(Rect other) {
    if (right <= other.left || other.right <= left) {
      return false;
    }
    
    if (bottom <= other.top || other.bottom <= top) {
      return false;
    }
    
    return true;
  }

shortestSide & longestSide

 宽度或者高度中较大/较小的那个值。

  // 这个矩形的宽度和高度两者中较小的那个数值。
  double get shortestSide => math.min(width.abs(), height.abs());

  // 这个矩形的宽度和高度两者中较大的那个数值。
  double get longestSide => math.max(width.abs(), height.abs());

topLeft & topCenter & topRight

 顶部左、中、右的偏移量。

  // 这个矩形顶部边界和左侧边界交叉点的偏移量。
  Offset get topLeft => Offset(left, top);

  // 这个矩形顶部边界的中心的偏移。
  Offset get topCenter => Offset(left + width / 2.0, top);

  // 这个矩形顶部边界和右侧边界交叉点的偏移量。
  Offset get topRight => Offset(right, top);

centerLeft & center & centerRight

&esmp;中部左、中、右的偏移量。

  // 这个矩形左边界中心位置的偏移量。
  Offset get centerLeft => Offset(left, top + height / 2.0);

  // 这个矩形的左右和上下边界的中点的偏移量。
  Offset get center => Offset(left + width / 2.0, top + height / 2.0);

  // 这个矩形右边界中心位置的偏移量。
  Offset get centerRight => Offset(right, top + height / 2.0);

bottomLeft & bottomCenter & bottomRight

  // 这个矩形底部边界和左侧边界交叉点的偏移量。
  Offset get bottomLeft => Offset(left, bottom);

  
  // 这个矩形底部边界的中心的偏移。
  Offset get bottomCenter => Offset(left + width / 2.0, bottom);

  // 这个矩形底部边界和右侧边界交叉点的偏移量。
  Offset get bottomRight => Offset(right, bottom);

contains

 判断由给定偏移量指定的点(假定相对于原点)是否位于此矩形的左侧、右侧、上侧和下侧边界之间。

 矩形包括其上边界和左边界,但不包括其下边界和右边界。

  bool contains(Offset offset) {
    return offset.dx >= left && offset.dx < right && offset.dy >= top && offset.dy < bottom;
  }

lerp

 在两个矩形之间进行线性插值。

 如果其中一个矩形为 null,则会使用 Rect.zero 作为替代。

 参数 t 表示在时间轴上的位置,0.0 表示插值尚未开始,返回a(或等价于a),1.0 表示插值已完成,返回b(或等价于b),介于两者之间的值表示插值在时间轴上的相应位置。插值可以在 0.0 和 1.0 之外进行外推,因此负值和大于 1.0 的值都是有效的(并且可以通过诸如 Curves.elasticInOut 的曲线轻松生成)。

 通常,t 的值是从 Animation<double>(如 AnimationController)中获取的。

 后面我们学习 Flutter 动画时会深入此部分。

  static Rect? lerp(Rect? a, Rect? b, double t) {
    if (b == null) {
      if (a == null) {
        return null;
      } else {
        final double k = 1.0 - t;
        return Rect.fromLTRB(a.left * k, a.top * k, a.right * k, a.bottom * k);
      }
    } else {
      if (a == null) {
        return Rect.fromLTRB(b.left * t, b.top * t, b.right * t, b.bottom * t);
      } else {
        return Rect.fromLTRB(
          _lerpDouble(a.left, b.left, t),
          _lerpDouble(a.top, b.top, t),
          _lerpDouble(a.right, b.right, t),
          _lerpDouble(a.bottom, b.bottom, t),
        );
      }
    }
  }

Rect 总结

 OK,内容都比较简单,主要是围绕四个 double 类型的属性:left、top、right、bottom 展开,它们分别表示在 x 轴和 y 轴距离原点的一个数值,然后我们可以在大脑里面以这四个值构建出一个矩形来,再配合我们前面学习的 Size 和 Offset,为我们后续学习 Flutter 绘制、布局相关的内容时打下基础。

参考链接

参考链接:🔗