Flutter Svg转Path对象,path.getBounds()获取测量信息不准问题记录

问题描述

今天有群友询问一个问题,就是想把svg转换的path对象缩放绘制在一个边框容器内,但是无论如何计算偏移坐标和缩放都对不准位置和大小,一起看了看,感觉代码逻辑没问题,但是还是差点。

于是乎,因为想到自己可能有类似需求,就让群友把代码发我看了看。果然怎么改都不行。然后发现端倪,使用getBounds获取到的测量信息跟实际绘制出的路径,视觉上明显对不上。

然后把svg的path中的各点都绘制出来。果然发现问题所在,使用getBounds测量出来的连同曲线控制点也给算上了,这是用到的path:

arduino 复制代码
"M 288.0517, 4.0782 C420.9688, 1.9839 499.5736, 71.3123 537.7337, 164.3545 557.7525, 213.1645 561.2316, 294.7823 540.9246, 346.958 535.6071, 358.652 530.2881, 370.3494 524.9705, 382.0433 520.1162, 388.897 506.8675, 390.4646 499.4438, 394.8017 486.4159, 403.838 473.3842, 412.8769 460.3562, 421.9131 447.2893, 432.3373 436.3263, 450.3531 428.4479, 465.7699 426.0551, 471.6168 423.6616, 477.4656 421.2686, 483.3125 410.2451, 497.7952 354.2471, 507.8667 330.3301, 512.0188 287.0907, 519.5253 240.4923, 508.3152 211.4718, 496.8683 104.4496, 454.6541 1.4113, 322.4794 62.3006, 167.5441 90.1993, 96.5551 142.6207, 46.1894 213.865, 18.4313 230.0834, 14.4447 246.3066, 10.4569 262.5249, 6.4703 271.033, 5.673 279.5436, 4.8755 288.0517, 4.0782 Z"

从图上我们就能看出来,空白区域的那部分宽或高,跟他上面截图的问题差不多,于是乎我直接推断就是这个问题导致到。

解决方案

知道是因为曲线弯曲控制点也被计算在内导致的,那么我们是不是可以考虑直接排除这些曲线控制点,然后取剩余点的的最小x、最小y、最大x、最大y,最终返回一个真实绘制路径测量的Rect。怎么实现呢,直接交给AI吧,哈哈哈哈。工具就是给人服务的,AI实现出来的就是你实现出来的,不要有什么自己过于依赖AI的愧疚感。通过对路径进行采样,来自己确定哪些点参与路径绘制了,这方法虽然当采样步长小的时候性能上不行,但是好在能精度更高的解决问题,先实现再等待更好的方案出现

dart 复制代码
/// 对路径进行采样,获取路径上的点集合
List<Offset> samplePathPoints(String pathData, {double step = 1.0}) {
  Path path = parseSvgPathData(pathData);
  List<Offset> points = [];
  // 获取路径的所有测量信息
  List<PathMetric> metrics = path.computeMetrics().toList();
  for (PathMetric metric in metrics) {
    // 沿着路径以指定步长采样点
    for (double distance = 0.0; distance <= metric.length; distance += step) {
      Tangent? tangent = metric.getTangentForOffset(distance);
      if (tangent != null) {
        points.add(tangent.position);
      }
    }
    // 确保包含路径的终点
    Tangent? endPoint = metric.getTangentForOffset(metric.length);
    if (endPoint != null && !points.contains(endPoint.position)) {
      points.add(endPoint.position);
    }
  }
  return points;
}

然后再看一下,是不是测量的非常准确了,OK解决了。

最后附上完整核心代码

dart 复制代码
import 'dart:math';
import 'dart:ui' show Path, Rect, Offset, PathMetric, Tangent;

import 'package:path_drawing/path_drawing.dart';

/// Path类的扩展,提供了额外的实用方法
class PathUtils{
  static Rect getBoundsReal(String svgPath){
    List<Offset> sampledPoints = samplePathPoints(svgPath);
    return Rect.fromPoints(
      _getLeftTopPointFromKeyPoints(sampledPoints),
      _getRightBottomPointFromKeyPoints(sampledPoints),
    );
  }

  /// 过滤掉SVG路径中的弯曲控制点,只保留关键点(起始点和终点)
  /// 然后获取最左侧点的x坐标和最上方点的y坐标组成新点
  static Offset _getLeftTopPointFromKeyPoints(List<Offset> sampledPoints) {
    if (sampledPoints.isEmpty) {
      return Offset.zero;
    }

    // 找到最左侧点的x坐标(最小x值)
    double leftMostX = sampledPoints.map((point) => point.dx).reduce(min);

    // 找到最上方点的y坐标(最小y值)
    double topMostY = sampledPoints.map((point) => point.dy).reduce(min);

    return Offset(leftMostX, topMostY);
  }

  /// 获取最右侧点的x坐标和最下方点的y坐标组成新点
  static Offset _getRightBottomPointFromKeyPoints(List<Offset> sampledPoints) {
    if (sampledPoints.isEmpty) {
      return Offset.zero;
    }

    // 找到最右侧点的x坐标(最大x值)
    double rightMostX = sampledPoints.map((point) => point.dx).reduce(max);

    // 找到最下方点的y坐标(最大y值)
    double bottomMostY = sampledPoints.map((point) => point.dy).reduce(max);

    return Offset(rightMostX, bottomMostY);
  }

  /// 对路径进行采样,获取路径上的点集合
  static List<Offset> samplePathPoints(String svgPath, {double step = 1.0}) {
    Path path = parseSvgPathData(svgPath);
    List<Offset> points = [];
    // 获取路径的所有测量信息
    List<PathMetric> metrics = path.computeMetrics().toList();
    for (PathMetric metric in metrics) {
      // 沿着路径以指定步长采样点
      for (double distance = 0.0; distance <= metric.length; distance += step) {
        Tangent? tangent = metric.getTangentForOffset(distance);
        if (tangent != null) {
          points.add(tangent.position);
        }
      }
      // 确保包含路径的终点
      Tangent? endPoint = metric.getTangentForOffset(metric.length);
      if (endPoint != null && !points.contains(endPoint.position)) {
        points.add(endPoint.position);
      }
    }
    return points;
  }
}

用上面getBoundsReal来代替path.getBounds。最后Svg路径转Path我用的是path_drawing

📚 结尾

开拓思路,大胆用AI尝试,反正不是烧的自己的脑细胞,努力成为AI人柱力

相关推荐
晚霞的不甘11 小时前
深度解析:Flutter 与 OpenHarmony 融合架构下的跨平台渲染机制与系统级集成
flutter·架构
kirk_wang11 小时前
Flutter图片库CachedNetworkImage鸿蒙适配:从原理到实践
flutter·移动开发·跨平台·arkts·鸿蒙
松☆11 小时前
Flutter 与 OpenHarmony 数据持久化协同方案:从 Shared Preferences 到分布式数据管理
分布式·flutter
松☆11 小时前
OpenHarmony + Flutter 离线能力构建指南:打造无网可用的高可靠政务/工业应用
flutter·政务
松☆11 小时前
OpenHarmony + Flutter 多语言与国际化(i18n)深度适配指南:一套代码支持中英俄等 10+ 语种
android·javascript·flutter
晚霞的不甘11 小时前
Flutter 与开源鸿蒙(OpenHarmony)性能调优与生产部署实战:从启动加速到线上监控的全链路优化
flutter·开源·harmonyos
AskHarries12 小时前
Flutter + Supabase 接入 Google 登录
flutter
晚霞的不甘12 小时前
架构演进与生态共建:构建面向 OpenHarmony 的 Flutter 原生开发范式
flutter·架构
Ya-Jun12 小时前
架构设计模式:依赖注入最佳实践
flutter
松☆12 小时前
Flutter + OpenHarmony 构建工业巡检 App:离线采集、多端协同与安全上报
安全·flutter