【问题标题】:Flutter: Layout multiple children with differing z coordinates from matrix transformations with correct overlapFlutter:从具有正确重叠的矩阵变换中布局具有不同 z 坐标的多个子级
【发布时间】:2021-04-14 21:56:34
【问题描述】:

我正在尝试构建 Threejs 周期表 Helix 视图的 Flutter Web 版本(请参阅此处:https://mrdoob.com/lab/javascript/threejs/css3d/periodictable/ 并点击“Helix”)

目前,我将所有元素拼贴放在一个堆栈中,并使用矩阵变换定位和旋转它们。见以下代码:

  @override
  Widget build(BuildContext context) {
    double initialRotateX = (widget.atomicNumber - 1) *
        (math.pi / Constants.numberOfElementsPerHalfCircle);
    return Transform(
      transform: Matrix4.identity()
        ..translate(
          math.sin((widget.atomicNumber - 1) *
                  (math.pi / Constants.numberOfElementsPerHalfCircle)) *
              Constants.radius,
          widget.atomicNumber * 4,
          -math.cos((widget.atomicNumber - 1) *
                  (math.pi / Constants.numberOfElementsPerHalfCircle)) *
              Constants.radius,
        )
        ..translate(Constants.elementCardHalfSize)
        ..rotateY(-initialRotateX)
        ..translate(-Constants.elementCardHalfSize),
      child: Container(
        decoration: BoxDecoration(
            color: Constants.elementCardColor,
            border: Border.all(width: 1, color: Colors.white.withOpacity(0.3))),
        child: SizedBox(
          width: Constants.elementCardWidth.toDouble(),
          height: Constants.elementCardHeight.toDouble(),
          child: ElementText()
          
        ),
      ),
    );

然后将所有这些卡片放在另一个类的堆栈小部件中,然后旋转该小部件,如下所示:

return AnimatedBuilder(
      animation: widget.animationControllerX,
      builder: (context, _) {
        double rotateX =
            widget.animationControllerX.value / Constants.turnResolution;
        double rotateY =
            widget.animationControllerY.value / Constants.turnResolution;
        return Transform(
          transform: Matrix4.identity()
            ..setEntry(3, 2, 0.001)
            ..rotateY(-rotateX)
            ..rotateX(rotateY),
          alignment: Alignment.center,
          child: Container(
            child: Stack(
              children: List<Widget>.generate(
                Constants.numberOfElements,
                (index) => ElementCard(
                  atomicNumber:
                      Constants.elements[(index + 1).toString()].atomicNumber,
                ),
              ),
            ),
          ),
        );
      },
    );

如您所见,应用了“..rotateY”变换,使卡片在堆栈旋转时看起来向前移动。

但是,在旋转时,应该在后面的卡片会变小,但会在应该在前面的卡片上涂漆。 (毫无疑问,卡片的位置是正确的,因为我可以沿 x 轴旋转它们并亲自查看,但是在绘画时,背面的卡片会覆盖在正面卡片中的文字上。我相信这是因为堆栈总是根据它们提供的顺序来定位其小部件的高度,并且顺序在列表中是固定的。有什么办法可以解决这个问题吗?

截图来解释我的意思:

我可以更改图块的不透明度以使问题更加明显:

如您所见,较大的卡片更靠近屏幕,因为它们是这样转换的,但它们是在较小的卡片后面绘制的。关于如何实现这一点的任何想法?

【问题讨论】:

    标签: flutter dart flutter-layout flutter-web flutter-animation


    【解决方案1】:

    所以我找到了解决方案。它并不完美,但到目前为止还有效。

    这个想法是使用 Flow() 小部件。在我通常构建它们的子小部件中,我将它们的转换存储在地图中。然后,在流委托函数中,访问这些转换,计算转换后每个小部件的 z 坐标(转换矩阵中的第 3 行,第 3 列(1 索引))。按 z 坐标对小部件进行排序,然后按该顺序在屏幕上绘制它们。

    这是相同的代码:

    带有根 Flow 小部件的部分:

    @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
          animation: widget.animationControllerX,
          builder: (context, _) {
            double rotateX =
                widget.animationControllerX.value / Constants.turnResolution;
            double rotateY =
                widget.animationControllerY.value / Constants.turnResolution;
            return Container(
              transform: Matrix4.identity()
                ..translate(
                  MediaQuery.of(context).size.width / 2,
                ),
              child: Flow(
                clipBehavior: Clip.none,
                delegate: CustomFlowDelegate(rotateY: -rotateX, rotateX: rotateY),
                children: List<Widget>.generate(
                  Constants.numberOfElements,
                  (index) => ElementCard(
                    atomicNumber:
                        Constants.elements[(index + 1).toString()].atomicNumber,
                  ),
                ),
              ),
            );
          },
        );
      }
    

    小部件的 FlowDelegate:

    class CustomFlowDelegate extends FlowDelegate {
      CustomFlowDelegate({this.rotateX, this.rotateY});
      final rotateY, rotateX;
      @override
      void paintChildren(FlowPaintingContext context) {
        Map<int, double> zValue = {};
        for (int i = 0; i < context.childCount; i++) {
          var map = Constants.transformationsMap[i + 1];
          Matrix4 transformedMatrix = Matrix4.identity()
            ..setEntry(3, 2, 0.001)
            ..setEntry(3, 1, 0.001)
            ..rotateY(this.rotateY)
            ..rotateX(this.rotateX)
            ..translate(
                map['translateOneX'], map['translateOneY'], map['translateOneZ'])
            ..translate(map['translateTwoX'])
            ..rotateY(map['rotateThreeY'])
            ..translate(map['translateFourX']);
          zValue[i + 1] = transformedMatrix.getRow(2)[2];
        }
        var sortedKeys = zValue.keys.toList(growable: false)
          ..sort((k1, k2) => zValue[k1].compareTo(zValue[k2]));
        LinkedHashMap sortedMap = new LinkedHashMap.fromIterable(sortedKeys,
            key: (k) => k, value: (k) => zValue[k]);
        for (int key in sortedMap.keys) {
          var map = Constants.transformationsMap[key];
          context.paintChild(
            key - 1,
            transform: Matrix4.identity()
              ..setEntry(3, 2, 0.001)
              ..setEntry(3, 1, 0.001)
              ..rotateY(this.rotateY)
              ..rotateX(this.rotateX)
              ..translate(
                  map['translateOneX'], map['translateOneY'], map['translateOneZ'])
              ..translate(map['translateTwoX'])
              ..rotateY(map['rotateThreeY'])
              ..translate(map['translateFourX']),
          );
        }
      }
    
      @override
      bool shouldRepaint(covariant FlowDelegate oldDelegate) {
        return true;
      }
    }
    

    结果如下:

    随着不透明度的提高,您可以真正看到修复的效果:

    是的,我知道将转换存储在地图中,然后从另一个页面对其进行排序并不是最佳做法。

    希望这对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 2014-11-04
      • 2020-11-20
      相关资源
      最近更新 更多