【问题标题】:Flutter: How to set a max/min scale using matrix_gesture_detectorFlutter:如何使用 matrix_gesture_detector 设置最大/最小比例
【发布时间】:2020-07-06 13:25:11
【问题描述】:

我正在尝试制作列比例/可移动。但是如何设置最大缩放和最小缩放?这样你就不会放大到无穷大。

现在使用这个方法:

 Matrix4 matrix = Matrix4.identity();

     MatrixGestureDetector(
      shouldRotate: false
      onMatrixUpdate: (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) {
        setState(() {
          matrix = m;
        });
      },
      child: Transform(
        transform: matrix,
        child: Column(
      ),
    ),

【问题讨论】:

    标签: flutter dart zooming scale gesture


    【解决方案1】:

    更新:正如@rdnobrega 善意解释的那样,这无论如何都不是一个理想的解决方案。它仅在唯一转换的矩阵是比例时才有效,并且会与其他转换中断。我不是数学专家或 Matrix4 专家,所以下面的解决方案需要您自担风险。

    我遇到了同样的问题,花了一段时间才找到解决方案。

    在 Flutter 的 Transform.scale source 中挖掘了一下之后,我们发现了这行代码:

    transform = Matrix4.diagonal3Values(scale, scale, 1.0)
    

    它使用您在onMatrixUpdate 中收到的Matrix4 的对角线值。所以它需要x 来自第一个 Vector4,y 来自第二个,z 来自第三个。 (据我所知,第四个是固定的)。所以这些是你需要限制的值。在这个例子中,我制作了一个小的 _minMax 方法,它在相关时将比例限制为相关的最小值/最大值(可以将它们传递给 null 以忽略限制的任一侧)。

    我用这个来限制规模:

    typedef MathF<T extends num> = T Function(T, T);
    typedef VFn = Vector4 Function(double x, double y, double z, double w);
    
    double _minMax(num _min, num _max, num actual) {
      if (_min == null && _max == null) {
        return actual.toDouble();
      }
    
      if (_min == null) {
        return min(_max.toDouble(), actual.toDouble());
      }
    
      if (_max == null) {
        return max(_min.toDouble(), actual.toDouble());
      }
    
      return min(_max.toDouble(), max(_min.toDouble(), actual.toDouble()));
    }
    
    // ... ... ...
    
    onMatrixUpdate: (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) {
        var finalM = Matrix4.copy(m);
        Map<int, VFn> colmap = {
          0: (x, y, z, w) {
            x = _minMax(widget.minScale, widget.maxScale, x);
            return Vector4(x, y, z, w);
          },
          1: (x, y, z, w) {
            y = _minMax(widget.minScale, widget.maxScale, y);
            return Vector4(x, y, z, w);
          },
          2: (x, y, z, w) {
            z = _minMax(widget.minScale, widget.maxScale, z);
            return Vector4(x, y, z, w);
          },
        };
        for (var col in colmap.keys) {
          var oldCol = m.getColumn(col);
          var colD = colmap[col];
          if (colD != null) {
            finalM.setColumn(col, colD(oldCol.x, oldCol.y, oldCol.z, oldCol.w));
          }
        }
        setState(() {
          matrix = finalM;
        });
      },
    

    【讨论】:

    • 嗨,您的解决方案只是部分正确,当不涉及平移或旋转时,您的情况只是一种边缘情况。该坐标系称为“齐次坐标”,用于透视几何,因此 OpenGL 很好。简而言之,HC代表(N+1)维空间中的N维空间,额外的维数增加了透视因子。这是一个非常有趣和令人费解的主题:)
    • 我在数学和向量方面很糟糕,如果您对此答案有改进,请随时提出修改建议!
    【解决方案2】:

    只是为了补充@casraf 的回答。

    Flutter(以及其他图形语言如OpenGL)使用的坐标系是齐次坐标。这只是存在于这些矩阵中用于透视计算的辅助维度的一个花哨的名称,例如当一个事物靠近时它会变得更大,如果它远离它会变得更小。

    因此,在这些坐标中,与笛卡尔坐标不同,我们需要一个额外的维度。如果我们试图用透视表示 3 维空间,我们需要一个 4 维矩阵。

    出于好奇,等轴测游戏和类似 2d 的 3d 游戏实际上与这个额外的 W 维度(矩阵的第 4 行)混为一谈,因为根本不应用透视。

    也就是说,Flutter/openGL 会计算屏幕中的最终顶点,将 1 行矩阵 (x, y, z, 1) 形式的原始坐标乘以变换矩阵 Matrix4。如果您进行数学计算,结果仍然是 1 行矩阵,但从原始顶点平移/旋转/缩放/倾斜,由矩阵。

    这意味着幕后需要庞大的矩阵乘法,手动更改特定系数可能会导致意外行为。在@casraf 的情况下,它适用于不涉及平移或旋转,因此更改后的系数等于比例系数。如果您应用任何其他转换,结果可能会有很大差异。

    TL;DR

    始终使用 Matrix4 的正确内置方法进行转换,除非您真的知道自己在做什么。

    另外,请记住转换的顺序很重要。进行平移然后旋转与进行旋转 -> 平移不同。

    【讨论】:

      猜你喜欢
      • 2020-04-10
      • 2017-09-26
      • 1970-01-01
      • 1970-01-01
      • 2023-04-11
      • 2019-02-25
      • 1970-01-01
      • 2019-07-03
      • 1970-01-01
      相关资源
      最近更新 更多