You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:获取Matrix4 Transform变换后的容器宽高

Flutter中获取Transform 3D旋转后容器的实际屏幕高度

当使用Transform结合Matrix4进行3D旋转时,RenderBox.size返回的是布局阶段的原始尺寸——因为Transform仅在绘制阶段对组件进行变换,不会改变布局计算的结果。要获取旋转后容器在屏幕上的实际高度,有两种可靠的方法:

方法一:通过旋转角度直接计算(正交变换场景)

如果你的变换是正交旋转(未添加透视效果,即未设置Matrix4的透视参数),可以利用三角函数直接推导变换后的高度。以绕X轴旋转为例,旋转角度为θ时,屏幕上的投影高度等于原始高度乘以cos(θ)。

代码示例

修改你的尺寸获取函数:

void getSize(GlobalKey key) {
  WidgetsBinding.instance.addPostFrameCallback((_) {
    // 获取当前旋转角度
    double rotationAngle = _controller.value * 2 * pi / 8;
    RenderBox renderBox = key.currentContext?.findRenderObject() as RenderBox;
    double originalHeight = renderBox.size.height;
    // 计算变换后的高度
    double transformedHeight = originalHeight * cos(rotationAngle);
    
    print('原始高度: ${renderBox.size.height}');
    print('变换后的高度: $transformedHeight');
  });
}

方法二:通过顶点坐标计算(通用场景)

如果你的变换包含透视效果,或者旋转中心不是组件中心,直接计算会有误差。此时可以通过获取容器四个顶点的屏幕坐标,计算Y轴方向的极值差来得到实际高度。

代码示例

实现一个通用的变换后尺寸获取函数:

void getTransformedSize(GlobalKey key) {
  WidgetsBinding.instance.addPostFrameCallback((_) {
    final renderBox = key.currentContext?.findRenderObject() as RenderBox;
    // 获取从容器到屏幕的变换矩阵
    final transform = renderBox.getTransformTo(null);

    // 容器的四个局部顶点坐标
    final topLeft = Offset(0, 0);
    final topRight = Offset(renderBox.size.width, 0);
    final bottomLeft = Offset(0, renderBox.size.height);
    final bottomRight = Offset(renderBox.size.width, renderBox.size.height);

    // 将局部坐标转换为屏幕坐标
    final screenPoints = [
      transform.transform(topLeft),
      transform.transform(topRight),
      transform.transform(bottomLeft),
      transform.transform(bottomRight),
    ];

    // 计算Y轴的最大最小值,差值即为实际高度
    final yValues = screenPoints.map((p) => p.dy).toList();
    final minY = yValues.reduce(min);
    final maxY = yValues.reduce(max);
    final transformedHeight = maxY - minY;

    print('原始高度: ${renderBox.size.height}');
    print('变换后的实际高度: $transformedHeight');
  });
}

代码优化提示

不要在AnimatedBuilder的builder方法中重复添加状态监听器,这会导致多次注册。将监听器移到initState中:

@override
void initState() {
  super.initState();
  _controller = AnimationController(
    duration: const Duration(seconds: 2),
    vsync: this,
  );
  // 只注册一次状态监听器
  _controller.addStatusListener((status) {
    if (status == AnimationStatus.completed) {
      getTransformedSize(containerKey);
    }
  });
}

内容的提问来源于stack exchange,提问作者Zakaria Louizy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 21:15:05