Introduction to the

In the previous article we introduced the use of CustomPaint to draw custom shapes with the Canvas of Flutter. We can see that with the plane drawing mathematics of the shapes we can draw the desired shapes. In this paper, we will introduce the drawing of line graphics, and realize the common wave dynamic effect with Animation. Through this article, you can learn:

  • The drawing of a sine curve
  • I’m going to take two sinusoids plusAnimationAchieve wave dynamic effect
  • The general method of drawing a curve
  • Line drawing

Below is the final implementation of the renderings, and then we introduce one by one.

Sinusoidal plotting

For sinusoids, the formula is defined as follows:For screen drawing, because the points on the screen are discrete, it is actually sampling the sine curve. As long as the sampling interval is dense enough, it is difficult to distinguish the drawn effect from the line drawing between discrete points. So, to draw a sinusoid is simply to connect the points of the sinusoid. Here is the implementation code for drawing,waveHeightIs the amplitude of the sinusoidal curve, and here we’re plotting one period per screen width, so we’re using theta2 * pi * i / size.width.

Path path = Path();
path.moveTo(0, center.height);
for (double i = 1; i < size.width; i += 1) {
  path.lineTo(
    i,
    center.height +
        waveHeight * sin(2 * pi * i / size.width + startAngle * pi * 4)); } canvas.drawPath(path, paint);Copy the code

The waves move effect

Observing the wave dynamic effect, it is actually two sinusoidal curves, because the speed of movement is not the same, giving the impression that the forward surge is the same. Controlling the movement of the curve can actually be done during animation by controlling the starting Angle of the sine curve, the θ variable in the formula. Our control variable Animation

varies from 0 to 1. In order to ensure the continuity of the Animation repetition Angle, the starting Angle should be the same after the end of the Animation cycle, that is, the end of the Animation cycle should be an integer multiple of 2π. Here we set a sinusoidal cancellation period of 4π. The other one is 6 PI. The greater the initial Angle of the period, the faster the movement will appear. The following is the code for drawing two sinusoids, where startAngle is the value of the Animation

object during the Animation. Note here that since startAngle is refreshed every time, in the CustomPainter subclass we need to return shouldRepaint true to support redrawing, not if it returns false.

  void paint(Canvas canvas, Size size) {
  var center = Size(size.width / 2, waveHeight * 2);
  varpaint1 = Paint().. color = Color(0xFF20B0FE);
  paint1.strokeWidth = 1.0;
  paint1.style = PaintingStyle.stroke;

  varpaint2 = Paint().. color = Color(0x8020C0E5);
  paint2.strokeWidth = 1.0;
  paint2.style = PaintingStyle.stroke;

  Path path1 = Path();
  path1.moveTo(0, center.height);
  Path path2 = Path();
  path2.moveTo(0, center.height + waveHeight);
  for (double i = 1; i < size.width; i += 1) {
    path1.lineTo(
      i,
      center.height +
          waveHeight * sin(2 * pi * i / size.width + startAngle * pi * 4)); path2.lineTo( i, center.height + waveHeight * sin(2 * pi * i / size.width + startAngle * 6 * pi),
    );
  }
  canvas.drawPath(path1, paint1);
  canvas.drawPath(path2, paint2);
}
Copy the code

The complete code has been uploaded to: custom drawing code in curves_paint.dart under basic_paint.

Curve drawing

With the knowledge of sinusoidal curve drawing, other curves are also in fact the same truth, we through mathematical expressions, through the abscissa to calculate the ordinate value, and then form a series of sampling points, and then use the Path object to connect these points in turn can achieve all kinds of curve drawing. The following is a sample code for drawing a logarithmic curve.

var center = Size(size.width / 2, size.height / 2);
varpaint = Paint().. color = Color(0xFF2080E5); //2080E5
paint.strokeWidth = 1.0;
paint.style = PaintingStyle.stroke;

Path path = Path();
path.moveTo(0, center.height);
for (double i = 1; i <= size.width; i += 1) {
  path.lineTo(
    i - 1,
    center.height - 20.0 * log(i),
  );
}
canvas.drawPath(path, paint);
Copy the code

The drawing effect is shown below.

The line chart

Line charts are more common in actual development, usually with axes, and then the points are connected by line segments, and the position of the points needs to be marked. The principle of drawing is the same as a curve, but it’s just that the spacing of the line chart is larger. Labeling points can be achieved by drawing circles or squares on polylines. We have encapsulated two classes, one to draw polylines and the other to draw coordinate axes. The drawing of the coordinate axis is relatively simple, that is, the starting and ending points of the horizontal axis and the vertical axis are introduced from the outside, and the horizontal axis and the vertical axis are drawn out and arrows are added.

// Line drawing
class LineChartPainter extends CustomPainter {
  final List<Point<double>> points;
  LineChartPainter({Key? key, required this.points}) : super(a);@override
  void paint(Canvas canvas, Size size) {
    varpaint = Paint().. color = Color(0xFF2080E5); //2080E5
    paint.strokeWidth = 2.0;
    paint.style = PaintingStyle.stroke;

    varpointPaint = Paint().. color = Color(0xFF20FF65); //2080E5
    pointPaint.strokeWidth = 1.0;
    pointPaint.style = PaintingStyle.stroke;

    Path path = Path();
    path.moveTo(points[0].x, points[0].y);
    for (var point in points) {
      path.lineTo(point.x, point.y);
      canvas.drawCircle(Offset(point.x, point.y), 4.0, pointPaint);
    }
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false; }}// Draw the axes
class AxisPainter extends CustomPainter {
  final Point<double> horizontalStartPoint, horizontalEndPoint;
  final Point<double> verticalStartPoint, verticalEndPoint;
  AxisPainter({
    Key? key,
    required this.horizontalStartPoint,
    required this.horizontalEndPoint,
    required this.verticalStartPoint,
    required this.verticalEndPoint,
  }) : super(a);@override
  void paint(Canvas canvas, Size size) {
    varpaint = Paint().. color = Color(0xFF909090);
    paint.strokeWidth = 2.0;
    paint.style = PaintingStyle.stroke;

    Path horizontalPath = Path();
    horizontalPath.moveTo(horizontalStartPoint.x, horizontalStartPoint.y);
    horizontalPath.lineTo(horizontalEndPoint.x - 1, horizontalEndPoint.y);
    canvas.drawPath(horizontalPath, paint);

    Path verticalPath = Path();
    verticalPath.moveTo(verticalStartPoint.x, verticalStartPoint.y);
    verticalPath.lineTo(verticalEndPoint.x, verticalEndPoint.y + 1);
    canvas.drawPath(verticalPath, paint);

    paint.style = PaintingStyle.fill;
    paint.strokeWidth = 2.0;
    final double arrowLength = 12.0;
    / / draw the arrow
    Path horizontalArrow = Path();
    horizontalArrow.moveTo(horizontalEndPoint.x, horizontalEndPoint.y);
    horizontalArrow.lineTo(horizontalEndPoint.x - arrowLength,
        horizontalEndPoint.y - arrowLength / 2);
    horizontalArrow.lineTo(horizontalEndPoint.x - arrowLength,
        horizontalEndPoint.y + arrowLength / 2);
    horizontalArrow.close();
    canvas.drawPath(horizontalArrow, paint);

    / / draw the arrow
    Path verticalArrow = Path();
    verticalArrow.moveTo(verticalEndPoint.x, verticalEndPoint.y);
    verticalArrow.lineTo(
        verticalEndPoint.x - arrowLength / 2, verticalEndPoint.y + arrowLength);
    verticalArrow.lineTo(
        verticalEndPoint.x + arrowLength / 2, verticalEndPoint.y + arrowLength);
    verticalArrow.close();
    canvas.drawPath(verticalArrow, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false; }}Copy the code

The final line drawing effect is as follows.

Other instructions

For the drawing area of CustomPaint, there are special instructions. If CustomPaint is the root node of the component tree, the draw area is the entire screen. But if CustomPaint has child elements (that is, the child argument is not empty), then the draw area size is limited to the size of the child element. This example uses a list to place three drawing methods on a page. To limit the size of each drawing, a Container is specified as a child element of CustomPaint. This way you can specify the size of the drawing area. And setting the background color (for example, a wavy background with a gradient effect using Container).

conclusion

This article introduces the drawing methods of Flutter lines. Once we have mastered the drawing methods, we can draw all kinds of curves or broken lines. If it takes a little time, we can also make beautiful diagrams. Of course, pub also has many excellent charts, such as fl_chart, which we introduced earlier. For details on how to use Flutter, see the previous article: Introduction to Flutter and Practical (68) : MobX gets web data to render cool curves.

I am dao Code Farmer with the same name as my wechat official account. This is a column about the introduction and practice of Flutter, providing systematic learning articles about Flutter. See the corresponding source code here: The source code of Flutter Introduction and Practical column. If you have any questions, please add me to the wechat account: island-coder.

👍🏻 : feel the harvest please point a praise to encourage!

🌟 : Collect articles, easy to look back!

💬 : Comment exchange, mutual progress!