鸿蒙开发-想逐段读取路径信息?PathIterator路径遍历详解 想分析路径是怎么画出来的PathIterator 让你逐段拆解你有没有遇到过这样的需求拿到一个 Path 对象想知道它是由哪些线段、曲线组成的比如你想把一个复杂路径拆成一段一段的或者想在路径的某个位置放一个小图标这时候你就需要PathIterator了。简单说PathIterator 就是一个路径操作迭代器它可以帮你逐个读取路径里的每一个操作指令——移动到哪、画直线到哪、画曲线到哪、在哪里闭合。PathIterator 是什么在 HarmonyOS 的 drawing 模块里Path 是由一系列操作指令组成的。比如移动到 (10, 20) ——moveTo画直线到 (100, 200) ——lineTo画贝塞尔曲线到某处 ——quadTo/cubicTo闭合路径 ——closePathIterator 就是让你能够逐个读取这些指令的工具。它就像一个阅读指针从路径的开头开始每调用一次next就往下读一条指令。创建迭代器下面的流程图展示了 PathIterator 的完整遍历流程否是MOVELINEQUADCUBICCLOSECONIC创建Path对象构建路径: moveTo/lineTo/quadTo/cubicTo/close创建PathIterator迭代器hasNext?遍历结束: DONE调用next读取操作操作类型verb写入1个目标点写入2个点: 起点终点写入3个点: 控制点终点写入4个点: 2控制点终点闭合路径: 回到起点写入3点权重值处理当前操作数据创建 PathIterator 非常简单只需要传一个 Path 对象进去import{drawing}fromkit.ArkGraphics2D;letpath:drawing.Pathnewdrawing.Path();letiter:drawing.PathIteratornewdrawing.PathIterator(path);注意PathIterator 从 API version 18 开始支持确保你的项目版本符合要求。判断是否有下一个操作hasNext在遍历路径之前你需要先检查还有没有剩余的操作。hasNext方法就是干这个的import{drawing}fromkit.ArkGraphics2D;letpath:drawing.Pathnewdrawing.Path();letiter:drawing.PathIteratornewdrawing.PathIterator(path);letresiter.hasNext();返回true说明还有下一个操作可以读取false说明已经遍历完了。预览下一个操作peek有时候你想偷偷看一眼下一个操作是什么但又不想让迭代器往前走。这时候用peekimport{drawing}fromkit.ArkGraphics2D;letpath:drawing.Pathnewdrawing.Path();letiter:drawing.PathIteratornewdrawing.PathIterator(path);letresiter.peek();peek和next的区别是peek只看不走迭代器还停在原地next看完就往前走一步。这在你需要根据下一个操作的类型来决定怎么处理时很有用。读取下一个操作next这是 PathIterator 的核心方法。每次调用next它会返回当前操作的类型并把操作对应的坐标点写入你传入的数组里import{common2D,drawing}fromkit.ArkGraphics2D;letpath:drawing.Pathnewdrawing.Path();path.moveTo(10,20);letiter:drawing.PathIteratornewdrawing.PathIterator(path);letverbStr:Arraystring[MOVE,LINE,QUAD,CONIC,CUBIC,CLOSE,DONE];letpointCount:Arraynumber[1,2,3,4,4,0,0];letpoints:Arraycommon2D.Point[{x:0,y:0},{x:0,y:0},{x:0,y:0},{x:0,y:0}];letoffset0;letverbiter.next(points,offset);letoutputMessage:stringpathIteratorNext: ;outputMessageverb verbStr[verb]; has pointCount[verb] pairs: ;for(letj0;jpointCount[verb]offset;j){outputMessage[points[j].x, points[j].y];}console.info(outputMessage);这段代码看起来有点长让我拆开解释points 数组这是一个输出参数。调用next之后操作对应的坐标点会被写入这个数组。数组的长度必须至少为offset 4因为不同操作类型需要的点数不同最多需要 4 个点CUBIC 曲线。offset写入位置的偏移量默认是 0。如果你不想从数组的第一个元素开始写可以设置偏移量。返回值 verb这是一个PathIteratorVerb枚举表示操作类型。具体有这些操作类型含义写入的点数MOVE移动到某点不画线1 个点LINE画直线到某点2 个点QUAD二次贝塞尔曲线3 个点CONIC圆锥曲线3 个点 1 个权重CUBIC三次贝塞尔曲线4 个点CLOSE闭合路径0 个点DONE路径结束0 个点注意MOVE 操作写入的是目标点终点但实际上它需要 1 个点来表示从当前点移动到哪里。LINE 写入的是起点和终点。QUAD、CUBIC 写入的是控制点和终点。完整遍历示例下面是一个完整的路径遍历例子把路径里的所有操作都打印出来import{common2D,drawing}fromkit.ArkGraphics2D;// 创建一个复杂路径letpath:drawing.Pathnewdrawing.Path();path.moveTo(10,20);path.lineTo(100,200);path.quadTo(150,250,200,200);path.close();// 创建迭代器letiter:drawing.PathIteratornewdrawing.PathIterator(path);letverbStr:Arraystring[MOVE,LINE,QUAD,CONIC,CUBIC,CLOSE,DONE];letpointCount:Arraynumber[1,2,3,4,4,0,0];letpoints:Arraycommon2D.Point[{x:0,y:0},{x:0,y:0},{x:0,y:0},{x:0,y:0}];while(iter.hasNext()){letverbiter.next(points,0);letoutputMessage操作: verbStr[verb], 点数: pointCount[verb];for(letj0;jpointCount[verb];j){outputMessage [points[j].x, points[j].y];}console.info(outputMessage);}输出会是类似这样的操作: MOVE, 点数: 1 [10, 20] 操作: LINE, 点数: 2 [100, 200] 操作: QUAD, 点数: 3 [150, 250] [200, 200] 操作: CLOSE, 点数: 0 操作: DONE, 点数: 0实际应用场景下面的流程图展示了 PathIterator 在不同场景下的应用思路路径调试路径动画路径序列化路径变换获取Path对象创建PathIterator应用场景遍历打印每个操作和坐标拆分各段曲线对每段做插值计算获取路径上任意位置坐标读取所有操作和坐标转换为JSON格式保存遍历读取所有坐标点对坐标做缩放/旋转/平移用新坐标重建Path场景一路径分析和调试当你用代码生成了一个复杂路径但显示效果不对时可以用 PathIterator 来逐段检查路径的每个操作看看哪一步出了问题。场景二路径动画想让一个小图标沿着路径运动你需要知道路径的每一段是什么类型的曲线然后计算出对应位置的坐标。PathIterator 可以帮你把路径拆分成一段一段的然后对每一段做插值计算。场景三路径序列化想把路径保存到文件或通过网络传输你可以用 PathIterator 把路径的每个操作和坐标点都读出来转换成 JSON 或其他格式保存。之后再用这些数据重建路径。场景四路径变换想把路径里的所有坐标都缩放一下遍历每个操作把坐标点乘以缩放系数然后用新的坐标重新构建路径。场景五计算路径长度想知道一条路径总共有多长遍历每一段分别计算直线长度、曲线长度然后加起来。各种操作类型的坐标含义这里再详细说一下每种操作类型写入 points 数组的坐标含义MOVE写入 1 个点表示移动的目标位置。就像抬笔移动到这个位置。LINE写入 2 个点。第一个是线段的起点第二个是终点。实际上起点就是上一次操作的终点所以你主要关注第二个点。QUAD二次贝塞尔曲线写入 3 个点。第一个是控制点第二个是曲线的终点。起点同样是上一次操作的终点。CONIC圆锥曲线写入 3 个点加 1 个权重值。和 QUAD 类似但多了一个权重参数来控制曲线的形状。权重值存在第 4 个点的 x 坐标里。CUBIC三次贝塞尔曲线写入 4 个点。前两个是控制点第三个是曲线的终点。CLOSE不写入任何点。表示从当前位置画一条直线回到当前子路径的起点然后闭合。DONE不写入任何点。表示路径已经遍历完了。注意事项API 版本PathIterator 从 API version 18 开始支持确保你的项目最低版本符合要求。数组长度传入的 points 数组长度必须至少为offset 4否则可能会出错。坐标覆盖每次调用next都会覆盖 points 数组里的内容。如果你需要保存之前的坐标要在下次调用之前把它们复制出来。线程安全PathIterator 和其他 drawing 对象一样是单线程模型需要自己管理线程安全。物理像素坐标单位是物理像素 px。小结PathIterator 是一个非常实用的路径分析工具。它让你能够逐段读取路径的每个操作指令包括移动、直线、各种曲线、闭合等。虽然它本身不直接产生视觉效果但在做路径动画、路径调试、路径序列化等场景下它是不可或缺的。下次你拿到一个 Path 对象想知道它里面到底画了什么的时候就用 PathIterator 来拆解它吧。