如何在Flutter中绘制钟形曲线以实现目标均衡器图形?
在Flutter中绘制钟形曲线风格的均衡器图形
核心思路:用高斯函数生成钟形曲线点
你参考的示例图里的均衡器柱体,本质是**钟形曲线(高斯曲线)**的变形。比起手动调贝塞尔曲线控制点,直接用高斯函数计算曲线上的点再连线填充,能更精准还原自然的对称衰减效果。
高斯函数的基础公式是:y = a * e^(-((x - b)^2)/(2*c^2))
参数说明:
a:控制曲线的最大高度(对应均衡器的音量强度)b:控制曲线的中心X坐标(对应均衡器的频段位置)c:控制曲线的宽度(对应频段的覆盖范围)
具体实现步骤
1. 编写高斯曲线点生成工具函数
先实现一个函数,根据参数生成曲线上的采样点集:
import 'dart:math' as math; List<Offset> generateGaussianCurvePoints({ required double centerX, required double maxHeight, required double width, required double canvasHeight, int pointCount = 50, }) { final points = <Offset>[]; // 取3倍宽度作为曲线左右边界,超出后y值趋近于0,无需计算 final startX = centerX - width * 3; final endX = centerX + width * 3; final step = (endX - startX) / pointCount; for (var i = 0; i <= pointCount; i++) { final x = startX + step * i; // 计算高斯曲线y值,转换为Flutter画布坐标系(y轴向下) final exponent = -((x - centerX) * (x - centerX)) / (2 * width * width); final y = canvasHeight - maxHeight * math.exp(exponent); points.add(Offset(x, y)); } return points; }
2. 在CustomPainter中绘制均衡器
拿到点集后,构建闭合路径并填充,批量绘制多频段的均衡器柱:
class EqualizerPainter extends CustomPainter { final List<double> frequencyValues; // 各频段高度比例,范围0-1 EqualizerPainter(this.frequencyValues); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.blueAccent ..style = PaintingStyle.fill; final columnGap = size.width * 0.1; // 柱体间的间隙比例 final columnWidth = (size.width - columnGap * (frequencyValues.length - 1)) / frequencyValues.length; final widthCoeff = columnWidth * 0.3; // 控制单条曲线的宽窄 for (var i = 0; i < frequencyValues.length; i++) { final centerX = columnGap * i + columnWidth * i + columnWidth / 2; final maxHeight = size.height * frequencyValues[i]; final curvePoints = generateGaussianCurvePoints( centerX: centerX, maxHeight: maxHeight, width: widthCoeff, canvasHeight: size.height, ); // 构建闭合路径:曲线点 + 底部左右点 final path = Path() ..addPolygon(curvePoints, false) ..lineTo(curvePoints.last.dx, size.height) ..lineTo(curvePoints.first.dx, size.height) ..close(); canvas.drawPath(path, paint); } } @override bool shouldRepaint(covariant EqualizerPainter oldDelegate) { return oldDelegate.frequencyValues != frequencyValues; } }
3. 在Widget中使用CustomPaint展示
直接传入频段高度数据即可渲染:
CustomPaint( size: const Size(350, 180), painter: EqualizerPainter([0.7, 0.4, 0.9, 0.5, 0.6, 0.3]), )
备选:用贝塞尔曲线模拟钟形效果
如果坚持用贝塞尔曲线,可通过两条三次贝塞尔曲线模拟单条柱体,但需要手动调整控制点位置,效果精度不如高斯函数:
Path buildBezierEqualizerColumn(Offset center, double height, double width) { final bottomLeft = Offset(center.dx - width, center.dy); final topLeftControl = Offset(center.dx - width * 0.5, center.dy - height * 0.8); final topCenter = Offset(center.dx, center.dy - height); final bottomRight = Offset(center.dx + width, center.dy); final topRightControl = Offset(center.dx + width * 0.5, center.dy - height * 0.8); return Path() ..moveTo(bottomLeft.dx, bottomLeft.dy) ..cubicTo(topLeftControl.dx, topLeftControl.dy, topCenter.dx, topCenter.dy, topCenter.dx, topCenter.dy) ..cubicTo(topCenter.dx, topCenter.dy, topRightControl.dx, topRightControl.dy, bottomRight.dx, bottomRight.dy) ..close(); }
内容的提问来源于stack exchange,提问作者starry
相关产品推荐
相关产品推荐

