问题描述
今天有群友询问一个问题,就是想把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人柱力
。