当前位置:网站首页>Draw a wave ball with the curve of flutter and Bessel

Draw a wave ball with the curve of flutter and Bessel

2022-07-23 07:33:00 biyezuopinvip

When flutter The existing components of cannot meet the requirements of the product UI In effect , We need to implement it through self drawing components . This article will introduce how to use flutter Custom implementation of a wave ball with text , The effect is as follows :

 Insert picture description here

Let's summarize WaveLoadingWidget Characteristics , In this way, the steps required to achieve this effect can be summarized :

  1. widget The main body of is an irregular semicircle , The top curve moves up and down from left to right in a wave like form
  2. The wave ball can customize the color , Here to waveColor name
  3. The undulating line of the wave ball divides the embedded text into upper and lower colors , The color of the upper part is in backgroundColor name , The color of the lower part is in foregroundColor name , The overall color of the text has been changing dynamically according to the operation of the waves

Although the overall color of the text is constantly changing , But as long as you can draw one of the frames , The dynamic effect can be realized by changing the position parameters of the wave curve , So here's the widget As static , First realize its static effect

Disassemble the drawing steps into the following steps :

  1. The drawing color is backgroundColor The text of , Draw it on canvas The bottom of the
  2. according to widget The width and height information constructs a maximum circular path within the range circlePath
  3. With circlePath The horizontal middle line of the wave is used as the reference fluctuation line of the wave , Draw a continuous wave with Bessel curve on the upper and lower sides of the fluctuation line path, take path The ends of the are connected together in a rectangular way , constitute wavePath,wavePath The bottom of the will circlePath Intersect at the bottom of
  4. take circlePath and wavePath Intersection combinePath, use waveColor fill , At this point, you get a semicircular spherical wave
  5. utilize canvas.clipPath(combinePath) Method to cut the canvas , Then draw the color as foregroundColor The text of , Drawn at this time foregroundColor The text will only show combinePath Part of the scope , That is, only the lower part will be displayed , Make the text drawn at two different times overlap , Thus, text with different color ranges is obtained
  6. utilize AnimationController Constantly changing wavePath The starting point of X coordinate , At the same time, refresh UI, The undulating motion from left to right

Now let's implement the above drawing steps step by step

One 、 draw backgroundColor Text

flutter adopt CustomPainter It provides self drawing for developers UI Entrance , Inside void paint(Canvas canvas, Size size) Method provides a canvas canvas Object and contains widget Wide and high information size object

Here comes the inheritance CustomPainter class , stay paint Method, first draw the color as backgroundColor The text of .flutter Of canvas Object does not provide a direct drawText Of API, Therefore, the steps of drawing text are relatively native customization View A little more trouble

class _WaveLoadingPainter extends CustomPainter {
  final String text;

  final double fontSize;

  final double animatedValue;

  final Color backgroundColor;

  final Color foregroundColor;

  final Color waveColor;

  _WaveLoadingPainter({
    required this.text,
    required this.fontSize,
    required this.animatedValue,
    required this.backgroundColor,
    required this.foregroundColor,
    required this.waveColor,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final side = min(size.width, size.height);
    _drawText(canvas: canvas, side: side, color: backgroundColor);
  }

  void _drawText(
      {required Canvas canvas, required double side, required Color color}) {
    ParagraphBuilder paragraphBuilder = ParagraphBuilder(ParagraphStyle(
      textAlign: TextAlign.center,
      fontStyle: FontStyle.normal,
      fontSize: fontSize,
    ));
    paragraphBuilder.pushStyle(ui.TextStyle(color: color));
    paragraphBuilder.addText(text);
    ParagraphConstraints pc = ParagraphConstraints(width: fontSize);
    Paragraph paragraph = paragraphBuilder.build()..layout(pc);
    canvas.drawParagraph(
      paragraph,
      Offset((side - paragraph.width) / 2.0, (side - paragraph.height) / 2.0),
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return animatedValue != (oldDelegate as _WaveLoadingPainter).animatedValue;
  }
}

 Insert picture description here

Two 、 structure circlePath

take widget The minimum value of width and height is taken as the diameter of the circle , In order to build a system that does not exceed widget The maximum circular path of the range circlePath

  @override
  void paint(Canvas canvas, Size size) {
    final side = min(size.width, size.height);
    _drawText(canvas: canvas, side: side, color: backgroundColor);

    final circlePath = Path();
    circlePath.addArc(Rect.fromLTWH(0, 0, side, side), 0, 2 * pi);
  }

3、 ... and 、 Draw wave lines

The width and height of the wave are evaluated according to a fixed proportional value , With circlePath The middle separation line of is used as the horizontal line , Draw a continuous wave line up and down the horizontal line according to the Bessel curve

  @override
  void paint(Canvas canvas, Size size) {
    final side = min(size.width, size.height);
    _drawText(canvas: canvas, side: side, color: backgroundColor);

    final circlePath = Path();
    circlePath.addArc(Rect.fromLTWH(0, 0, side, side), 0, 2 * pi);

    final waveWidth = side * 0.8;
    final waveHeight = side / 6;
    final wavePath = Path();
    final radius = side / 2.0;
    wavePath.moveTo(-waveWidth, radius);
    for (double i = -waveWidth; i < side; i += waveWidth) {
      wavePath.relativeQuadraticBezierTo(
          waveWidth / 4, -waveHeight, waveWidth / 2, 0);
      wavePath.relativeQuadraticBezierTo(
          waveWidth / 4, waveHeight, waveWidth / 2, 0);
    }
    // For the convenience of the reader , Here is the  wavePath  Draw it out , There is no need for 
    final paint = Paint()
      ..isAntiAlias = true
      ..style = PaintingStyle.fill
      ..strokeWidth = 3
      ..color = waveColor;
    canvas.drawPath(wavePath, paint);
  }

 Insert picture description here

At this time, the drawn curve is still in an open state , Need to put wavePath Connect the ends of the , In this way, we can communicate with circlePath intersect

wavePath.relativeLineTo(0, radius);
wavePath.lineTo(-waveWidth, side);
wavePath.close();
// For the convenience of the reader , Here is the  wavePath  Draw it out , There is no need for 
final paint = Paint()
  ..isAntiAlias = true
  ..style = PaintingStyle.fill
  ..strokeWidth = 3
  ..color = waveColor;
canvas.drawPath(wavePath, paint);

wavePath After closing , At this time, the color of the semicircle will be covered

 Insert picture description here

Four 、 intersect

take circlePath and wavePath Intersection , You get a semicircular wave ball

final paint = Paint()
  ..isAntiAlias = true
  ..style = PaintingStyle.fill
  ..strokeWidth = 3
  ..color = waveColor;
final combinePath = Path.combine(PathOperation.intersect, circlePath, wavePath);
canvas.drawPath(combinePath, paint);

 Insert picture description here

5、 ... and 、 draw foregroundColor Text

The color of the text is divided into two parts , The color of the upper part is backgroundColor, The second half is foregroundColor. In the first step, the color has been drawn as backgroundColor The text of ,foregroundColor The text does not need to show the top half , So I'm drawing foregroundColor Before text, you need to limit the drawing area to combinePath Inside , Make the text drawn at two different times overlap , To get text with different color ranges

canvas.clipPath(combinePath);
_drawText(canvas: canvas, side: side, color: foregroundColor);

 Insert picture description here

6、 ... and 、 Add animation

Now you have drawn the effect of static time , Consider how to make widget It's moving

It's also easy to achieve dynamic effects , Just keep changing the coordinates of the starting point of the Bezier curve , Keep it moving from left to right , You can create the effect of waves moving forward from left to right ._WaveLoadingPainter According to the animation value passed in from the outside animatedValue To set up wavePath The starting coordinate point of , Generate animatedValue The logic and other drawing parameters are determined by _WaveLoadingState To provide

class _WaveLoadingState extends State<WaveLoading>
    with SingleTickerProviderStateMixin {
  String get _text => widget.text;

  double get _fontSize => widget.fontSize;

  Color get _backgroundColor => widget.backgroundColor;

  Color get _foregroundColor => widget.foregroundColor;

  Color get _waveColor => widget.waveColor;

  late AnimationController _controller;

  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        duration: const Duration(milliseconds: 700), vsync: this);
    _animation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(_controller)
      ..addListener(() {
        setState(() => {});
      });
    _controller.repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: CustomPaint(
        painter: _WaveLoadingPainter(
          text: _text,
          fontSize: _fontSize,
          animatedValue: _animation.value,
          backgroundColor: _backgroundColor,
          foregroundColor: _foregroundColor,
          waveColor: _waveColor,
        ),
      ),
    );
  }
}

_WaveLoadingPainter according to animatedValue To set up wavePath The starting coordinate point of

wavePath.moveTo((animatedValue - 1) * waveWidth, radius);

7、 ... and 、 Use

The final will be _WaveLoadingState Package to StatefulWidget in , stay StatefulWidget Just open the parameters that can be customized in

class WaveLoading extends StatefulWidget {
  final String text;

  final double fontSize;

  final Color backgroundColor;

  final Color foregroundColor;

  final Color waveColor;

  WaveLoading({
    Key? key,
    required this.text,
    required this.fontSize,
    required this.backgroundColor,
    required this.foregroundColor,
    required this.waveColor,
  }) : super(key: key) {
    assert(text.isNotEmpty && fontSize > 0);
  }

  @override
  State<StatefulWidget> createState() {
    return _WaveLoadingState();
  }
}

Usage mode :

SizedBox(
	width: 300,
	height: 300,
	child: WaveLoading(
  		text: " Open ",
  		fontSize: 210,
  		backgroundColor: Colors.lightBlue,
  		foregroundColor: Colors.white,
  		waveColor: Colors.lightBlue,
)
原网站

版权声明
本文为[biyezuopinvip]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/204/202207222035103065.html