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

Flutter中如何对Path执行inflate/deflate操作?

实现Canvas Path的膨胀(Inflate)与收缩(Deflate)

Path本身没有像Rect那样现成的inflate/deflate方法,但咱们可以通过路径采样+法线偏移的方式自己实现,原理很简单:沿着路径的法线方向,把路径上的每个点往外(膨胀)或往里(收缩)偏移指定距离,再用这些偏移后的点重新构建新路径。

原生代码实现

给Path写个扩展方法,直接调用就能实现膨胀/收缩,正数是膨胀,负数是收缩:

import 'dart:math';
import 'package:flutter/material.dart';

extension PathInflate on Path {
  Path inflate(double distance) {
    final newPath = Path();
    final pathMetrics = computeMetrics();

    for (final metric in pathMetrics) {
      final totalLength = metric.length;
      const sampleStep = 2.0; // 采样步长,越小越平滑但越费性能,按需调整
      var currentOffset = 0.0;
      final sampledPoints = <Offset>[];

      while (currentOffset <= totalLength) {
        final tangent = metric.getTangentForOffset(currentOffset)!;
        // 计算法线方向:切线转90度后归一化,再乘以偏移距离
        final normal = Offset(-tangent.direction.dy, tangent.direction.dx)
            .normalized * distance;
        sampledPoints.add(tangent.position + normal);
        currentOffset += sampleStep;
      }

      // 用采样点拼接新路径
      if (sampledPoints.isNotEmpty) {
        newPath.moveTo(sampledPoints.first.dx, sampledPoints.first.dy);
        for (var i = 1; i < sampledPoints.length; i++) {
          newPath.lineTo(sampledPoints[i].dx, sampledPoints[i].dy);
        }
        // 原路径闭合的话,新路径也自动闭合
        if (metric.isClosed) {
          newPath.close();
        }
      }
    }

    return newPath;
  }
}

使用示例

举个实际绘制的例子:

// 定义原始自定义路径
final originalPath = Path()
  ..moveTo(50, 50)
  ..lineTo(200, 50)
  ..quadraticBezierTo(250, 100, 200, 150)
  ..lineTo(50, 150)
  ..close();

// 膨胀10像素
final inflatedPath = originalPath.inflate(10);
// 收缩5像素
final deflatedPath = originalPath.inflate(-5);

// 在Canvas上渲染路径
@override
void paint(Canvas canvas, Size size) {
  // 原始路径(黑色实线)
  canvas.drawPath(originalPath, Paint()..color = Colors.black..strokeWidth = 2..style = PaintingStyle.stroke);
  // 膨胀后的路径(红色描边)
  canvas.drawPath(inflatedPath, Paint()..color = Colors.red..strokeWidth = 2..style = PaintingStyle.stroke..strokeJoin = StrokeJoin.round);
  // 收缩后的路径(蓝色描边)
  canvas.drawPath(deflatedPath, Paint()..color = Colors.blue..strokeWidth = 2..style = PaintingStyle.stroke..strokeJoin = StrokeJoin.round);
}

注意事项

  • 采样步长:步长越小,生成的偏移路径越平滑,但计算开销会增加,根据路径复杂度自行调整。
  • 复杂曲线:如果路径包含大量曲线段,缩小采样步长能保证偏移后的曲线更贴近原路径形状。
  • 边界情况:收缩距离不宜过大,否则可能导致路径自相交甚至完全消失,需根据实际场景做边界判断。

替代方案:第三方库

如果需要处理特别复杂的路径,或者想要更精确的偏移效果,可以使用flutter_path_operations库,它提供了专业的路径偏移、布尔运算等高级功能,比手动采样的实现更可靠。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 01:48:31