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人柱力

相关推荐
一只大侠的侠9 小时前
Flutter开源鸿蒙跨平台训练营 Day 10特惠推荐数据的获取与渲染
flutter·开源·harmonyos
renke336413 小时前
Flutter for OpenHarmony:色彩捕手——基于HSL色轮与感知色差的交互式色觉训练系统
flutter
子春一15 小时前
Flutter for OpenHarmony:构建一个 Flutter 四色猜谜游戏,深入解析密码逻辑、反馈算法与经典益智游戏重构
算法·flutter·游戏
铅笔侠_小龙虾16 小时前
Flutter 实战: 计算器
开发语言·javascript·flutter
微祎_17 小时前
Flutter for OpenHarmony:构建一个 Flutter 重力弹球游戏,2D 物理引擎、手势交互与关卡设计的工程实现
flutter·游戏·交互
一起养小猫18 小时前
Flutter for OpenHarmony 实战_魔方应用UI设计与交互优化
flutter·ui·交互·harmonyos
hudawei99618 小时前
flutter和Android动画的对比
android·flutter·动画
一只大侠的侠18 小时前
Flutter开源鸿蒙跨平台训练营 Day7Flutter+ArkTS双方案实现轮播图+搜索框+导航组件
flutter·开源·harmonyos
一只大侠的侠19 小时前
Flutter开源鸿蒙跨平台训练营 Day9分类数据的获取与渲染实现
flutter·开源·harmonyos
一只大侠的侠19 小时前
Flutter开源鸿蒙跨平台训练营 Day 5Flutter开发鸿蒙电商应用
flutter·开源·harmonyos