使用Flutter Custom Painter实现绘制路径(Path)的边角圆润处理
Flutter Custom Painter 路径圆角处理方案
下面提供针对自定义路径的圆角实现方法,适用于不规则折线组成的路径:
核心思路
遍历路径的每个线段拐角,计算合适的圆弧替换尖角,在保留路径原有形状的同时实现圆润边角。
代码实现
1. 路径圆角处理工具函数
Path addCornersToPath(Path originalPath, double radius) { final pathMetrics = originalPath.computeMetrics(); final roundedPath = Path(); for (final metric in pathMetrics) { final path = metric.extractPath(0, metric.length); final iterator = path.iterator; var previousPoint = Offset.zero; var currentPoint = Offset.zero; var nextPoint = Offset.zero; var isFirstMove = true; while (iterator.moveNext()) { currentPoint = iterator.current; if (iterator.currentSegment == PathSegment.moveTo) { roundedPath.moveTo(currentPoint.dx, currentPoint.dy); previousPoint = currentPoint; isFirstMove = false; } else if (iterator.currentSegment == PathSegment.lineTo) { if (iterator.moveNext()) { nextPoint = iterator.current; // 计算拐角圆弧参数并添加到新路径 final cornerData = _calculateCornerArc(previousPoint, currentPoint, nextPoint, radius); roundedPath.addArc(cornerData.rect, cornerData.startAngle, cornerData.sweepAngle); // 回退迭代,保留nextPoint作为下一轮的currentPoint iterator.movePrevious(); previousPoint = currentPoint; } else { // 处理最后一段线段,直接连接到终点 roundedPath.lineTo(currentPoint.dx, currentPoint.dy); } } } } roundedPath.close(); return roundedPath; } // 计算拐角处的圆弧参数 ({Rect rect, double startAngle, double sweepAngle}) _calculateCornerArc(Offset prev, Offset curr, Offset next, double radius) { // 计算两条线段的方向向量 final v1 = prev - curr; final v2 = next - curr; // 判断拐角方向(顺时针/逆时针) final isClockwise = (v1.dx * v2.dy - v1.dy * v2.dx) > 0; // 计算两条线段的单位向量 final unitV1 = v1.normalized; final unitV2 = v2.normalized; // 计算圆弧圆心 final bisector = (unitV1 + unitV2).normalized; final distance = radius / sin((pi - v1.angleTo(v2)) / 2); final center = curr + bisector * distance * (isClockwise ? -1 : 1); // 计算圆弧的起始和结束角度 final startAngle = (prev - center).direction; final endAngle = (next - center).direction; // 计算扫过的角度,确保方向正确 var sweepAngle = endAngle - startAngle; if (isClockwise && sweepAngle > 0) sweepAngle -= 2 * pi; if (!isClockwise && sweepAngle < 0) sweepAngle += 2 * pi; return ( rect: Rect.fromCircle(center: center, radius: radius), startAngle: startAngle, sweepAngle: sweepAngle ); }
2. 自定义Painter使用示例
class RoundedPathPainter extends CustomPainter { final double cornerRadius; RoundedPathPainter({this.cornerRadius = 16.0}); @override void paint(Canvas canvas, Size size) { // 模拟你的原始不规则路径 final originalPath = Path() ..moveTo(size.width * 0.2, size.height * 0.2) ..lineTo(size.width * 0.8, size.height * 0.2) ..lineTo(size.width * 0.9, size.height * 0.5) ..lineTo(size.width * 0.7, size.height * 0.8) ..lineTo(size.width * 0.3, size.height * 0.8) ..lineTo(size.width * 0.1, size.height * 0.5); // 生成带圆角的路径 final roundedPath = addCornersToPath(originalPath, cornerRadius); // 绘制填充路径 final fillPaint = Paint() ..color = Colors.blue ..style = PaintingStyle.fill; canvas.drawPath(roundedPath, fillPaint); // 可选:绘制原始路径对比(半透明红色描边) final strokePaint = Paint() ..color = Colors.red.withOpacity(0.5) ..style = PaintingStyle.stroke ..strokeWidth = 2; canvas.drawPath(originalPath, strokePaint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return oldDelegate is RoundedPathPainter && oldDelegate.cornerRadius != cornerRadius; } }
注意事项
- 该方法仅适用于直线段组成的路径,如果路径包含贝塞尔曲线,需要额外扩展处理逻辑。
- 圆角半径不宜超过线段最短长度的一半,否则会导致路径变形或圆弧重叠。
- 若路径是简单矩形,直接使用
Path.addRRect(RRect.fromRectAndRadius(rect, Radius.circular(radius)))会更高效。
内容的提问来源于stack exchange,提问作者Hossein Asadi
相关产品推荐
相关产品推荐

