美文网首页Flutter圈子FlutterAll in Flutter
Flutter贝塞尔曲线之水波纹与球形进度(二)

Flutter贝塞尔曲线之水波纹与球形进度(二)

作者: 向日花开 | 来源:发表于2019-09-30 08:38 被阅读0次

    续上篇,再用贝塞尔曲线绘制一个循环水波纹,一个水波纹进度球,先看效果,以下效果的实现用的都是二阶贝塞尔曲线。

    效果图

    bezierShow4.gif
    bezierShow3.gif

    我们先实现简单的循环水波纹绘制,我们可以经常见到当手持长绳或上下或左右挥舞绳子,都会产生波形路径,此时如果我们有黑幕遮住
    绳子两端只留下绳子中间的波形部分,就可得到一段无限循环的波形图,前提是一直在抖动绳子。。。

    循环水波纹

    实现分析

    先看一下下图,下图也表述了上述场景。


    bezier2.jpeg

    综上所述,只要屏幕内的波形够多,只要控制住offset的偏移量,无限循环,就能得到我们所想要的波形。

    不封闭的循环波

    bezierShow4_2.gif

    图3 _waveCount 不够的封闭循环波

    bezierShow4_1.gif

    关键代码

    @override
    void paint(Canvas canvas, Size size) {
      // TODO: implement paint
      _screenHeight = size.height; //屏幕高,这里不是一直成立,像当有Center 父控件的时候就不成立
      _screenWidth = size.width; //屏幕宽
      _waveCount = (_screenWidth / waveLength)
          .round()+2; //根据波长算出波的个数,这里的波浪个数多一点,这样可以在屏幕内看到更完整的波浪
      _centerY = _screenHeight / 2; //中心高度的值
      _path.moveTo(
          -waveLength + this.myOffsetX, _centerY); //把画笔的起点移到屏幕外的一个波长处,Y轴在屏幕中间
      //this.myOffsetX水平方向的偏移量
      for (int i = 0; i < _waveCount; i++) {
        canvas.save();
        canvas.restore();
        //绘制波谷,控制点在(-3M/4,_centerY),结束点在(-waveLength / 2,_centerY)
        //此处可以先把(waveLength * i) + this.myOffsetX 这段代码移除,所画的就是一个波谷,屏幕外的波谷
        _path.quadraticBezierTo(
            -waveLength / 4 * 3 + (waveLength * i) + this.myOffsetX,
            _centerY + _waveHeight,
            -waveLength / 2 + (waveLength * i) + this.myOffsetX,
            _centerY);
        //绘制波峰,控制点在(-M/4,_centerY),结束点在(0,_centerY)
        //此处可以先把(waveLength * i) + this.myOffsetX 这段代码移除,所画的就是一个波峰,屏幕外的波峰
        _path.quadraticBezierTo(
            -waveLength / 4 + (waveLength * i) + this.myOffsetX,
            _centerY - _waveHeight,
            0 + waveLength * i + this.myOffsetX,
            _centerY);
        canvas.drawPath(_path, _whitePaint); //绘制
      }
    
      ///封闭绘制区域,构成“深水面”
      _path.lineTo(_screenWidth, _screenHeight); //将画笔从当前位置画到屏幕最右下角
      _path.lineTo(0, _screenHeight); //画到屏幕左下角
      _path.close();
      canvas.drawPath(_path, _pathPaint);
    }
    
    

    水波纹进度球

    在上面的基础上,在加了一个竖直方向变化的偏移量_progressY;还有就是之前是方形的,现在变成圆形,方形变圆形只要
    把整个画布裁剪成圆的即可。

    没有裁剪画布成圆的图

    ![] bezierShow3_1.gif

    关键代码

    @override
    void paint(Canvas canvas, Size size) {
      // TODO: implement paint
      //centerOffset圆心
      _path.addArc(Rect.fromCircle(center: centerOffset, radius: r), 0, 360);
      canvas.clipPath(_path); //把画布裁剪成一个圆形,这样怎么画都是圆。
      canvas.drawCircle(centerOffset, r, _pointPaint); //画圆
      _path.reset(); //重置路径
      //this.progress的范围是0-100。
      _progressY = centerOffset.dy + (r - r / 50 * this.progress); //算出Y点的坐标
      //将画笔移动至屏幕外
      _path.moveTo(-waveLength + moveX, _progressY);
      //这里的波峰波谷稍微多点,所以waveCount*2
      for (int i = 0; i < waveCount * 2; i++) {
        canvas.save();
        canvas.restore();
        //绘制波谷,同上
        _path.quadraticBezierTo(
            -waveLength / 4 * 3 + (waveLength * i) + moveX,
            _progressY + waveHeight,
            -waveLength / 2 + (waveLength * i) + moveX,
            _progressY);
        //绘制波峰,同上
        _path.quadraticBezierTo(-waveLength / 4 + (waveLength * i) + moveX,
            _progressY - waveHeight, 0 + waveLength * i + moveX, _progressY);
      }
      print("_moveX=${moveX.toString()}");
      //封闭圆
      _path.moveTo(centerOffset.dx + r, _progressY);
      _path.lineTo(centerOffset.dx + r, centerOffset.dy + r);
      _path.lineTo(centerOffset.dx - r, centerOffset.dy + r);
      _path.lineTo(centerOffset.dx - r, _progressY);
      _path.close();
      canvas.drawPath(_path, _pathPaint);
    }
    

    完整代码

    Screen类的代码在前面时钟绘制一篇有,要在程序启动的时候调用init初始化。

    ///贝塞尔曲线示例二
    class CustomBezierWidget1 extends StatefulWidget {
      @override
      _CustomBezierWidget1State createState() => _CustomBezierWidget1State();
    }
    
    class _CustomBezierWidget1State extends State<CustomBezierWidget1>
        with SingleTickerProviderStateMixin {
      AnimationController animationController;
    
      Animation<double> animation;
    
      final double _waveLength = 300; //波浪长
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        animationController = new AnimationController(
            vsync: this, duration: Duration(milliseconds: 600));
        animation = Tween<double>(begin: 0, end: 300).animate(animationController)
          ..addListener(() {
            setState(() {});
          });
        animationController.repeat();
      }
    
      @override
      void deactivate() {
        // TODO: implement deactivate
        super.deactivate();
        animationController.stop();
      }
    
      @override
      void dispose() {
        // TODO: implement dispose
        super.dispose();
        animationController.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return CustomPaint(
          painter: BezierPainter1(animation.value, _waveLength),
        );
      }
    }
    
    class BezierPainter1 extends CustomPainter {
      final double myOffsetX; //平移量
      final int _waveHeight = 30; //波浪高
      final double waveLength; //一个波浪的长度
    
      Paint _pointPaint; //点画笔
      Paint _pathPaint; //线画笔
      Paint _whitePaint; //空白画笔
    
      double _screenHeight; //屏幕高
      double _screenWidth; //屏幕宽
      double _centerY; //屏幕中间Y坐标
    
      int _waveCount; //波浪个数
    
      Path _path = Path(); //路径
    
      BezierPainter1(this.myOffsetX, this.waveLength) {
        _pointPaint = Paint()
          ..color = Colors.teal
          ..strokeWidth = 4
          ..isAntiAlias = true
          ..style = PaintingStyle.fill;
        _pathPaint = Paint()
          ..color = Colors.deepOrange
          ..style = PaintingStyle.fill
          ..isAntiAlias = true
          ..strokeWidth = 1;
    
        _whitePaint = Paint()
          ..color = Colors.white
          ..style = PaintingStyle.fill
          ..isAntiAlias = true
          ..strokeWidth = 1;
      }
    
      @override
      void paint(Canvas canvas, Size size) {
        // TODO: implement paint
        _screenHeight = size.height; //屏幕高,这里不是一直成立,像当有Center 父控件的时候就不成立
        _screenWidth = size.width; //屏幕宽
        _waveCount = (_screenWidth / waveLength)
            .round()+2; //根据波长算出波的个数,这里的波浪个数多一点,这样可以在屏幕内看到更完整的波浪
        _centerY = _screenHeight / 2; //中心高度的值
        _path.moveTo(
            -waveLength + this.myOffsetX, _centerY); //把画笔的起点移到屏幕外的一个波长处,Y轴在屏幕中间
        //this.myOffsetX水平方向的偏移量
        for (int i = 0; i < _waveCount; i++) {
          canvas.save();
          canvas.restore();
          //绘制波谷,控制点在(-3M/4,_centerY),结束点在(-waveLength / 2,_centerY)
          //此处可以先把(waveLength * i) + this.myOffsetX 这段代码移除,所画的就是一个波谷,屏幕外的波谷
          _path.quadraticBezierTo(
              -waveLength / 4 * 3 + (waveLength * i) + this.myOffsetX,
              _centerY + _waveHeight,
              -waveLength / 2 + (waveLength * i) + this.myOffsetX,
              _centerY);
          //绘制波峰,控制点在(-M/4,_centerY),结束点在(0,_centerY)
          //此处可以先把(waveLength * i) + this.myOffsetX 这段代码移除,所画的就是一个波峰,屏幕外的波峰
          _path.quadraticBezierTo(
              -waveLength / 4 + (waveLength * i) + this.myOffsetX,
              _centerY - _waveHeight,
              0 + waveLength * i + this.myOffsetX,
              _centerY);
          canvas.drawPath(_path, _whitePaint); //绘制
        }
    
        ///封闭绘制区域,构成“深水面”
        _path.lineTo(_screenWidth, _screenHeight); //将画笔从当前位置画到屏幕最右下角
        _path.lineTo(0, _screenHeight); //画到屏幕左下角
        _path.close();
        canvas.drawPath(_path, _pathPaint);
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        // TODO: implement shouldRepaint
        return true;
      }
    }
    
    /// 贝塞尔曲线示例三
    class WidgetCircleProgressWidget extends StatefulWidget {
      @override
      _WidgetCircleProgressWidgetState createState() =>
          _WidgetCircleProgressWidgetState();
    }
    
    class _WidgetCircleProgressWidgetState
        extends State<WidgetCircleProgressWidget> {
      Timer timer;
    
      double progress = 0;
    
      bool flag = false;
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        timer = Timer.periodic(Duration(milliseconds: 500), (timer) {
          setState(() {
            if (flag) {
              progress = progress - 1;
            } else {
              progress = progress + 1;
            }
    
            if (progress == 0) {
              flag = false;
            }
    
            if (progress == 100) flag = true;
          });
        });
      }
      @override
      void dispose() {
        // TODO: implement dispose
        super.dispose();
        timer.cancel();
      }
    
      @override
      Widget build(BuildContext context) {
        return CustomBezierWidget2(progress);
      }
    }
    
    class CustomBezierWidget2 extends StatefulWidget {
      final double progress;
    
      CustomBezierWidget2(this.progress);
    
      @override
      _CustomBezierWidget2State createState() => _CustomBezierWidget2State();
    }
    
    class _CustomBezierWidget2State extends State<CustomBezierWidget2>
        with SingleTickerProviderStateMixin {
      AnimationController _animationController;
    
      Animation<double> _animationTranslate;
    
      double _moveX; //移动的X,此处变化一个波长
      double _r; //半径
      double waveLength; //波长
    
      double _waveCount = 2;
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        _r = Screen.screenWidthDp / 3;
        waveLength = 2 * _r / _waveCount;
        _animationController =
            new AnimationController(vsync: this, duration: Duration(seconds: 1));
        _animationTranslate =
            Tween<double>(begin: 0, end: waveLength).animate(_animationController)
              ..addListener(() {
                setState(() {});
              });
        _animationController.repeat();
      }
    
      @override
      void deactivate() {
        // TODO: implement deactivate
        super.deactivate();
        _animationController.stop();
      }
    
      @override
      void dispose() {
        // TODO: implement dispose
        super.dispose();
        _animationController.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return CustomPaint(
          painter: BezierPainter2(
              progress: this.widget.progress,
              waveHeight: 15,
              moveX: _animationTranslate.value,
              r: _r,
              waveLength: waveLength),
        );
      }
    }
    
    class BezierPainter2 extends CustomPainter {
      final double progress; //进度
      final double waveHeight; //波浪高
      final double moveX; //移动的X,此处变化一个波长
      final double r; //半径
      final double waveLength; //一个波浪的长度
      final waveCount = 2; //波浪个数
    
      double _progressY; //移动中Y的坐标
    
      Paint _pointPaint; //点画笔
      Paint _pathPaint; //线画笔
      Paint _whitePaint; //空白画笔
    
      Path _path = Path(); //路径
    
      Offset centerOffset; //圆心
    
      BezierPainter2(
          {this.progress, this.waveHeight, this.moveX, this.r, this.waveLength}) {
        _pointPaint = Paint()
          ..color = Colors.teal
          ..strokeWidth = 4
          ..isAntiAlias = true
          ..style = PaintingStyle.stroke;
        _pathPaint = Paint()
          ..color = Colors.deepOrange
          ..style = PaintingStyle.fill
          ..isAntiAlias = true
          ..strokeWidth = 1;
        _whitePaint = Paint()
          ..color = Colors.white
          ..style = PaintingStyle.stroke
          ..isAntiAlias = true
          ..strokeWidth = 1;
        centerOffset = Offset(Screen.screenWidthDp / 2, Screen.screenHeightDp / 2);
      }
    
      @override
      void paint(Canvas canvas, Size size) {
        // TODO: implement paint
        //centerOffset圆心
        _path.addArc(Rect.fromCircle(center: centerOffset, radius: r), 0, 360);
        canvas.clipPath(_path); //把画布裁剪成一个圆形,这样怎么画都是圆。
        canvas.drawCircle(centerOffset, r, _pointPaint); //画圆
        _path.reset(); //重置路径
        //this.progress的范围是0-100。
        _progressY = centerOffset.dy + (r - r / 50 * this.progress); //算出Y点的坐标
        //将画笔移动至屏幕外
        _path.moveTo(-waveLength + moveX, _progressY);
        //这里的波峰波谷稍微多点,所以waveCount*2
        for (int i = 0; i < waveCount * 2; i++) {
          canvas.save();
          canvas.restore();
          //绘制波谷,同上
          _path.quadraticBezierTo(
              -waveLength / 4 * 3 + (waveLength * i) + moveX,
              _progressY + waveHeight,
              -waveLength / 2 + (waveLength * i) + moveX,
              _progressY);
          //绘制波峰,同上
          _path.quadraticBezierTo(-waveLength / 4 + (waveLength * i) + moveX,
              _progressY - waveHeight, 0 + waveLength * i + moveX, _progressY);
        }
        print("_moveX=${moveX.toString()}");
        //封闭圆
        _path.moveTo(centerOffset.dx + r, _progressY);
        _path.lineTo(centerOffset.dx + r, centerOffset.dy + r);
        _path.lineTo(centerOffset.dx - r, centerOffset.dy + r);
        _path.lineTo(centerOffset.dx - r, _progressY);
        _path.close();
        canvas.drawPath(_path, _pathPaint);
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        // TODO: implement shouldRepaint
        return true;
      }
    }
    

    相关文章

      网友评论

        本文标题:Flutter贝塞尔曲线之水波纹与球形进度(二)

        本文链接:https://www.haomeiwen.com/subject/igffdctx.html