Flutter canvas 画一条会动的波浪线 进度条

之前用 Flutter Canvas 画过一个三角三角形,html 的 Canvas 也画过一次类似的, 今天用 Flutter Canvas 试了下 感觉差不多:

html 版本

大致效果如下:

思路和 html 实现的类似:

也就是找出点的位置,使用二阶贝塞尔曲线实现:

代码如下:
复制代码
import 'package:flutter/material.dart';

class PageCanvas extends StatefulWidget {
  const PageCanvas({Key? key}) : super(key: key);

  @override
  State<PageCanvas> createState() => _PageCanvasState();
}

class _PageCanvasState extends State<PageCanvas> with TickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;
  double _waveHeight = 0.5;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controller = AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);
    animation = Tween<double>(begin: 0, end: 1).animate(controller);
    controller.repeat();
  }

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Canvas'),
        backgroundColor: Colors.blue,
      ),
      body: Column(
        children: [
          AnimatedBuilder(
              animation: controller,
              builder: (context, widget) {
                return CustomPaint(
                  size: Size(size.width, size.height / 3),
                  painter: MyPainter(animation.value, _waveHeight),
                );
              }),
          const SizedBox(
            height: 60,
          ),
          Center(
            child: SizedBox(
              // color: Colors.grey,
              width: 200,
              height: 200,
              child: ClipOval(
                child: Container(
                  color: Colors.grey.withOpacity(0.3),
                  child: AnimatedBuilder(
                      animation: controller,
                      builder: (context, widget) {
                        return CustomPaint(
                          size: Size(size.width, size.height / 3),
                          painter: MyPainter2(animation.value, _waveHeight),
                        );
                      }),
                ),
              ),
            ),
          ),
          const SizedBox(
            height: 20,
          ),
          Slider(
            min: 0,
            max: 2,
            value: _waveHeight,
            onChanged: (value) {
              setState(() {
                _waveHeight = value;
              });
              //_waveHeight = value;
            },
          )
        ],
      ),
    );
  }

  @override
  void dispose() {
    controller.dispose();
    // TODO: implement dispose
    super.dispose();
  }
}

class MyPainter extends CustomPainter {
  final double value;
  final double waveHeight;
  const MyPainter(this.value, this.waveHeight);
  @override
  void paint(Canvas canvas, Size size) {
    // print(value);

    Paint paint = Paint();
    Path path = Path();

    double positionX = -size.width * value;
    double positionY = 100;
    double positionRange = 5 * (1 + waveHeight);

    double positionX2 = -size.width * (1 - value);
    double positionY2 = 110;
    double positionRange2 = 10 * (1 + waveHeight);

    double positionX3 = -size.width * (1.3 - value);
    double positionY3 = 120;
    double positionRange3 = 20 * (1 + waveHeight);

    double step = size.width / 4;
    //
    path.moveTo(0 + positionX, positionY);
    for (int i = 1; i < 13; i++) {
      if (i % 2 == 1) {
        path.quadraticBezierTo(step * (2 * i - 1) + positionX,
            positionY - positionRange, step * (2 * i) + positionX, positionY);
      } else {
        path.quadraticBezierTo(step * (2 * i - 1) + positionX,
            positionY + positionRange, step * (2 * i) + positionX, positionY);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue.withOpacity(0.2);
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();

    path = Path();
    path.moveTo(0 + positionX2, positionY2);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 1) {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX2,
            positionY2 - positionRange2,
            step * (2 * i) + positionX2,
            positionY2);
      } else {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX2,
            positionY2 + positionRange2,
            step * (2 * i) + positionX2,
            positionY2);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue.withOpacity(0.6);
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();

    path = Path();
    path.moveTo(0 + positionX3, positionY3);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 1) {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX3,
            positionY3 - positionRange3,
            step * (2 * i) + positionX3,
            positionY3);
      } else {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX3,
            positionY3 + positionRange3,
            step * (2 * i) + positionX3,
            positionY3);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue;
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return oldDelegate != this;
    //return true;
  }
}

class MyPainter2 extends CustomPainter {
  final double value;
  final double waveHeight;
  const MyPainter2(this.value, this.waveHeight);
  @override
  void paint(Canvas canvas, Size size) {
    // print(value);

    Paint paint = Paint();
    Path path = Path();

    double positionX = -size.width * value;
    double positionY = 50;
    double positionRange = 5 * (1 + waveHeight);

    double positionX2 = -size.width * (1 - value);
    double positionY2 = 60;
    double positionRange2 = 10 * (1 + waveHeight);

    double positionX3 = -size.width * (1.3 - value);
    double positionY3 = 70;
    double positionRange3 = 20 * (1 + waveHeight);

    double step = size.width / 4;
    //
    path.moveTo(0 + positionX, positionY);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 0) {
        path.quadraticBezierTo(step * (2 * i - 1) + positionX,
            positionY - positionRange, step * (2 * i) + positionX, positionY);
      } else {
        path.quadraticBezierTo(step * (2 * i - 1) + positionX,
            positionY + positionRange, step * (2 * i) + positionX, positionY);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue.withOpacity(0.2);
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();

    path = Path();
    path.moveTo(0 + positionX2, positionY2);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 0) {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX2,
            positionY2 - positionRange2,
            step * (2 * i) + positionX2,
            positionY2);
      } else {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX2,
            positionY2 + positionRange2,
            step * (2 * i) + positionX2,
            positionY2);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue.withOpacity(0.6);
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();

    path = Path();
    path.moveTo(0 + positionX3, positionY3);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 0) {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX3,
            positionY3 - positionRange3,
            step * (2 * i) + positionX3,
            positionY3);
      } else {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX3,
            positionY3 + positionRange3,
            step * (2 * i) + positionX3,
            positionY3);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue;
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return oldDelegate != this;
    //return true;
  }
}
相关推荐
GIS之路5 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug8 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213810 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中32 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路35 分钟前
GDAL 实现矢量合并
前端
hxjhnct38 分钟前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端
LawrenceLan1 小时前
Flutter 零基础入门(九):构造函数、命名构造函数与 this 关键字
开发语言·flutter·dart