You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter自定义Paint实现带圆角曲线的左右移动锯齿线动画

带圆角曲线的移动锯齿线动画实现方案

我需要实现一个从左到右移动的带圆角曲线的锯齿线动画,但当前用CustomPainter写的代码生成的是直边锯齿线,怎么修改才能让线条带有圆角曲线?

目标效果:锯齿的转角处为平滑圆角,整体线条流畅
当前效果:锯齿边角尖锐,线条生硬


解决方案

核心思路是把原代码中直接用lineTo连接点的方式,替换为**二次贝塞尔曲线(quadraticBezierTo)**实现圆角过渡,同时统一处理所有线段的平滑连接,而非仅在边界处做处理。以下是修改后的完整代码:

import 'dart:math' as math;
import 'package:flutter/material.dart';

class DigitalWavePainter extends CustomPainter {
  final double animationValue;
  final double widthOfCanvas;
  final double heightOfCanvas;

  DigitalWavePainter(this.animationValue, this.widthOfCanvas, this.heightOfCanvas);

  @override
  void paint(Canvas canvas, Size size) {
    final width = widthOfCanvas;
    final height = heightOfCanvas;
    final path = Path();
    final path2 = Path();
    final numSegments = 200;
    final segmentWidth = width / numSegments;
    final amplitude = height + 10;
    final upperBoundary = height * 0.01;
    final lowerBoundary = height;
    final waveProgress = animationValue * 2;
    double dotY = 0;

    // 构建右侧路径
    path.moveTo(width, (math.sin(0) * amplitude) + height / 2);
    for (int i = 1; i <= numSegments / 2; i++) {
      final x = width - i * segmentWidth;
      final prevX = width - (i - 1) * segmentWidth;
      final animationTime = (x / width * math.pi) + waveProgress * math.pi;
      final y = (math.sin(animationTime) * amplitude) + height / 12;
      final clampedY = y.clamp(upperBoundary, lowerBoundary);
      dotY = clampedY;

      // 二次贝塞尔曲线实现圆角过渡
      final controlX = (prevX + x) / 2;
      path.quadraticBezierTo(prevX, clampedY, controlX, clampedY);
      path.quadraticBezierTo(controlX, clampedY, x, clampedY);
    }

    // 构建左侧路径
    path2.moveTo(-50, (math.sin(0) * amplitude) + height / 2);
    for (int i = 1; i <= numSegments; i++) {
      final x = i * segmentWidth;
      final prevX = (i - 1) * segmentWidth;
      final animationTime = (x / width * math.pi) + waveProgress * math.pi;
      final y = (math.sin(animationTime) * amplitude) + height / 12;
      final clampedY = y.clamp(upperBoundary, lowerBoundary);
      dotY = clampedY;

      // 二次贝塞尔曲线实现圆角过渡
      final controlX = (prevX + x) / 2;
      path2.quadraticBezierTo(prevX, clampedY, controlX, clampedY);
      path2.quadraticBezierTo(controlX, clampedY, x, clampedY);
    }

    const dotRadius = 20.0;
    final paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 16.0
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    final circlePaint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    final boundaryPaint = Paint()
      ..color = Colors.white
      ..style = PaintingStyle.stroke
      ..strokeWidth = 5.0;

    canvas.drawPath(path, paint);
    canvas.drawPath(path2, paint);

    canvas.drawCircle(
      Offset(width / 2, dotY + animationValue),
      dotRadius,
      circlePaint,
    );

    canvas.drawCircle(Offset(width / 2, dotY), dotRadius, boundaryPaint);
  }

  @override
  bool shouldRepaint(DigitalWavePainter oldDelegate) {
    return oldDelegate.animationValue != animationValue;
  }
}

关键修改点说明

  • 移除原代码中仅在边界处使用贝塞尔曲线的逻辑,改为对所有线段使用二次贝塞尔曲线连接,确保每个锯齿转角都有圆角。
  • 使用clamp()方法统一处理Y值的上下边界,简化重复的条件判断。
  • 每个线段的控制点取前后两点的X坐标中点,保证圆角过渡自然流畅。
  • 保留原有动画逻辑,确保锯齿线的移动效果不受影响。

内容的提问来源于stack exchange,提问作者Waleed Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 16:07:03