Flutter CustomPaint 绘制声音波动曲线(一)

6,731 阅读2分钟

voicewave.gif

下一节 Flutter CustomPaint 绘制声音波动曲线(二)

前言

最近用flutter custompaint做了个曲线随声音波动的动效,和大家来分享下实现的过程和思路

曲线绘制的实现

无论原生,Flutter还是前端,曲线的绘制在不同平台实现都大同小异通过确定不同点的位置坐标(x1, y1)(x2,y2)... ,然后用过贝塞尔曲线或者系统方法拟合两点之间的曲线,最后把曲线path给到画布绘制,这样曲线就出来了

小插曲

  • 最终效果是 开始和末尾振幅为0 向中间位置递增的曲线

  • 我们从基础开始,我们先画条振幅相同的曲线,并波动。

  • 写代码要循循渐进,一步步的来。。。 截屏2021-12-13 下午12.15.58.png 这简单啊

  • 画条两倍容器view宽的波纹曲线

  • 重复从左到右滑动,视觉效果就是曲线波动的效果

    搞定后,设计说上面这个是当时偷懒给错了图,当时内心的想法

平复下情绪,那我们就把 振幅相同振幅不同 的曲线都分享下

绘制振幅相同的曲线

class VoiceWavePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;
    Path path = Path();
    // 
    path.quadraticBezierTo(50, -80, 100, 0);
    path.quadraticBezierTo(150, 80, 200, 0);

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(VoiceWavePainter oldDelegate) => false;
}

path.quadraticBezierTo 定义P1 和 P2 位置 一个类似正弦的曲线绘制完成,P0 是路径起始点 path.quadraticBezierTo(50, -80, 100, 0); P0(0,0) P1 (50,-80) p2 (100,0) path.quadraticBezierTo(150, 80, 200, 0); P0(100,0) P1 (150,80) p2 (200,0) 以此类推,就出现需要的类正弦曲线

振幅相同的曲线的波动

原理已经说过不再重复,图示更直观, 重复的左滑动,矩形框左右两侧波形 左滑的开始和结束时完全一样的,视觉效果就产生曲线一直波动的效果

最终实现

  @override
  void paint(Canvas canvas, Size size) {
    int waveNum = 4;
    double waveWidth = this.painterWidth / waveNum;
    
    canvas.clipRect((Rect.fromCenter(
        center: Offset(0, size.height / 2),
        width: painterWidth * 2,
        height: 120)));
    canvas.translate(0, size.height / 2);
    Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;

    Path path = Path();
   
    for (var i = 0; i < waveNum * 2; i++) {
      path.quadraticBezierTo(waveWidth / 2 + waveWidth * i,
          waveHeight * (i.isOdd ? 1 : -1), waveWidth * (i + 1), 0);
    }
    canvas.translate(-painterWidth * repaint.value, 0);
    canvas.drawPath(path, paint);
  }
}
  class _VoiceWaveState extends State<VoiceWave>
    with SingleTickerProviderStateMixin {
  AnimationController _waveController;
  @override
  void initState() {
    _waveController =
        AnimationController(vsync: this, duration: Duration(seconds: 1))..repeat();
  }

  @override
  void dispose() {
    _waveController.dispose();
    super.dispose();
  }
  • 定义 AnimationController 并传入 自定义的 CustomPainter
  • painterWidth 曲线的长度 waveNum 可见波动的个数
  • waveWidth = this.painterWidth / waveNumwaveWidth半个波长的长度
    for (var i = 0; i < waveNum * 2; i++) {
       path.quadraticBezierTo(waveWidth / 2 + waveWidth * i,
          waveHeight * (i.isOdd ? 1 : -1), waveWidth * (i + 1), 0);
    }
  • 循环定义点位的位置,假如要看到4个上下波峰长的曲线,我们需要绘制8的上下波峰的曲线,共16 个坐标点,简化后通过循环确定点的位置
  • 通过 canvas.translate(-painterWidth * repaint.value, 0) 实现canvas的重复移动

总结

  • 优点
    • 能够快速实现曲线波动的效果
  • 缺点
    • 波动个数必须是半个的正弦周期的偶数倍,奇数的话 重复动画的开始和奇数的点y值正好相反,无所出现连贯的动效
    • 无法使用渐变色效果

下一节 Flutter CustomPaint 绘制声音波动曲线(二)

未经作者授权,禁止转载