Introduction to the
By drawing basic lines, paths, shapes, curves, etc. At the same time combined with the canvas rotation to achieve a simulation of the clock components. Learn the basic drawing of Flutter.
Paint, and Canvas
To paint on a canvas, you need a brush:
final Paint mPaint = Paint() .. Color = color.orange // Set the brush color.. Style = paintingstyle. stroke// Set the brush type.. IsAntiAlias = true// Enable anti-aliasing.. strokeWidth = 6; // Set the brush widthCopy the code
Get the Canvas Canvas object by inheriting the CustomPainter and overriding the paint method. You can then draw on the canvas.
class Custom extends CustomPainter{
@override
void paint(Canvas canvas, Size size) {
///This is where I drew it
///You can draw directly on the canvas
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Copy the code
Paint and Canvas both provide a number of properties and methods. The Canvas methods are shown in sections later. Here are a few key properties of Paint:
Paint properties | The data type | Introduction to the |
---|---|---|
color | Color | Brush color |
strokeWidth | double | The line width of the brush |
style | PaintingStyle | Brush types (Fill and stroke lines) |
strokeCap | StrokeCap | Thread types (Butt, round, and square) |
Here are the effects of different Strokecaps:
Round, Square and Butt. You can see how they look at the ends of the thread.
### Base graphics drawing
Drawing of points, lines, and paths
Points can be drawn in batches by drawPoints(PointMode PointMode, List
points, Paint Paint). This method takes three parameters: the mode of the point, the point position, and the brush. There are three modes of point:
- Points: points;
- Lines: line;
- Polygon: polygon;
The corresponding effects are as follows:
points | lines | polygon |
---|---|---|
There is another way to draw points: DrawRawPoints (PointMode PointMode, Float32List points, Paint Paint), Canvas provides two methods drawLine(Offset P1, Offset p2, Paint Paint) and drawPath(Path Path, Paint Paint) to draw a line and a Path, respectively.
DrawLine takes three arguments: the start position, the end position, and the brush. The strokeCap effect display above is achieved through basic line drawing, and its code is as follows:
///line
void line(Canvas canvas) {
canvas.drawLine(
Offset(- 60.- 12), Offset(60.- 12), mPaint.. strokeCap = StrokeCap.round); canvas.drawLine( Offset(- 60.0), Offset(60.0), mPaint.. strokeCap = StrokeCap.square); canvas.drawLine( Offset(- 60.12), Offset(60.12), mPaint.. strokeCap = StrokeCap.butt); }Copy the code
DrawPath takes two arguments: path and brush. Here are three types of Path movement strategies:
- MoveTo and relativeMoveTo: moveTo a position that corresponds to the position of a pen
2. LineTo and relativeLineTo: Draw to a position that corresponds to the position of the brush move
Relative movement, relative movement. The passed x, y is the offset from the current coordinate. And not relative is the absolute coordinates of the canvas.
///Painting path
void pathLine(Canvas canvas) {
varpath = Path() .. moveTo(100.0.- 100.)
..lineTo(0.0.0.0)
..lineTo(100.0.- 100.);
canvas.drawPath(path, mPaint);
varpathRelative = Path() .. moveTo(100.0.100)
..relativeLineTo(100.0.100.0)
..relativeLineTo(100.0.100.0); canvas.drawPath(pathRelative, mPaint.. color = Colors.deepPurple); }Copy the code
The code above looks like this:
Path can draw not only linear paths, but also second-order Bezier curves:
///Bessel curve
void bezierQuadratic(Canvas canvas, Size size) {
var first = Offset(size.width / 4 -.0);
var second = Offset(0.- 200.);
var third = Offset(size.width / 4.0); Path path = Path() .. moveTo(first.dx, first.dy) .. quadraticBezierTo(second.dx, second.dy, third.dx, third.dy); canvas.drawPath(path, mPaint); canvas.drawPath( Path() .. moveTo(first.dx, first.dy) .. lineTo(second.dx, second.dy) .. lineTo(third.dx, third.dy), mPaint.. strokeWidth =1);
}
Copy the code
The effect is as follows:
Third-order Bezier curve:
///Bessel curve
void bezierCubic(Canvas canvas, Size size) {
var first = Offset(size.width / 4 -.0);
var second = Offset(size.width / 4 -.- 200.);
var third = Offset(size.width / 4.- 200.);
var four = Offset(size.width / 4.0); Path path = Path() .. moveTo(first.dx, first.dy) .. cubicTo(second.dx, second.dy, third.dx, third.dy, four.dx, four.dy); canvas.drawPath(path, mPaint); canvas.drawPath( Path() .. moveTo(first.dx, first.dy) .. lineTo(second.dx, second.dy) .. lineTo(third.dx, third.dy) .. lineTo(four.dx, four.dy), mPaint.. strokeWidth =1);
}
Copy the code
Second – and third-order Bezier curves, however, require three points in the front and four points in the second. But both accept 2 and 3 points, respectively, because they both take Path’s current point as the first point.
Drawing graphics
Canvas provides basic set graphics drawing, including round rectangles and sectors.
Draw round –
///Draw round
void shapeCircle(Canvas canvas) {
canvas.drawCircle(Offset(- 60.0), 60, mPaint);
canvas.drawCircle(Offset(60.0), 60, mPaint.. style = PaintingStyle.fill); }Copy the code
The method is very simple, just need to pass in the far point coordinates and radius. The effect is as follows:
Draw a rectangle
///Draw a rectangular
void shapeRect(Canvas canvas) {
///The position of the four edges, and the arc of the corners
var rrect = RRect.fromLTRBR(- 60.- 60.60.60, Radius.circular(5));
canvas.drawRRect(rrect, mPaint);
}
Copy the code
The effect is as follows:
The rectangle is a little bit more responsible and needs to Orient the rectangle. RRect provides several constructors to determine the orientation of a rectangle. In the figure above, the area to be drawn is determined by the position of the four sides of the rectangle.
Drawing fan
///Draw arc
void pathArc(Canvas canvas) {
var rect = Rect.fromPoints(Offset(- 120..- 120.), Offset(120.120));
canvas.drawArc(rect, 0, pi, false, mPaint);
canvas.drawArc(
rect, pi / - 3, pi / 2 -.true, mPaint.. color = Colors.deepPurple); }Copy the code
The effect is as follows:
Sector drawing also needs to determine the orientation first, and then need to determine the starting Angle and the end Angle.
Draw the shadow
///Draw the shadow
void shadow(Canvas canvas) {
varpathRelative = Path() .. moveTo(100.0.100)
..relativeLineTo(100.0.100.0)
..relativeLineTo(100.0.100.0);
canvas.drawShadow(pathRelative, Colors.orange, 3.false);
}
Copy the code
The effect is as follows:
Canvas transformation
Canvas transformation includes translation, rotation and scaling. I’m going to talk about the rotation of translation. Rotate: Rotate the canvas at an Angle. Rotate: Rotate the canvas at an Angle. After the transformation, the coordinates and angles of the canvas will change. If you want to restore, you need to call the save method before the transformation and then use restore to restore. We use the clock drawing directly to show the effect of rotation:
///Draw a clock
void canvasRotateTranslate(Canvas canvas) {
///Draw table circle
canvas.drawCircle(Offset(0.0), 122, mPaint.. style = PaintingStyle.stroke);///Draw the calibration
///Divide the dial sixty equal
for (var i = 1; i <= 60; i++) {
///Rotate the canvas
canvas.rotate(pi / 30);
///Draw a large scale for every five small scales
if (i % 5= =0) {
canvas.drawLine(Offset(0.- 120.), Offset(0, i % 3= =0 ? - 105. : - 108.), mPaint.. strokeWidth = i %3= =0 ? 6 : 4);
} else {
canvas.drawLine(
Offset(0.- 120.), Offset(0.- 115.), mPaint.. strokeWidth =3); }}///Draw the moment
TextPainter textPainter = new TextPainter(
textAlign: TextAlign.left, textDirection: TextDirection.ltr);
for (var i = 0; i < 12; i++) {
canvas.save();
///Move the canvas so that it is right next to the scale
canvas.translate(0.- 95.);
///The canvas is rotated so that the time array is displayed vertically
canvas.rotate(-pi / 6 * i);
///Rendering text
textPainter.text = TextSpan(
style: new TextStyle(color: Colors.deepOrange, fontSize: 22),
text: "${i == 0 ? 12 : i}");
textPainter.layout();
textPainter.paint(
canvas, Offset(-textPainter.width / 2, -textPainter.height / 2));
canvas.restore();
///Rotate the Angle so that the moment is in the correct position
canvas.rotate(pi / 6);
}
var hours = DateTime.now().hour % 12;
var minutes = DateTime.now().minute;
var seconds = DateTime.now().second;
///Draw time second hand
canvas.save();
///Calculate the Angle of rotation required for the clockwise position
canvas.rotate(
(hours * pi / 6) + (pi / 6) * minutes / 60 + (pi / 6) * seconds / 3600);
canvas.drawLine(Offset.zero, Offset(0.- 40), mPaint.. strokeWidth =3);
canvas.restore();
canvas.save();
///Calculate the Angle of rotation required for the minute hand position
canvas.rotate(((minutes * pi / 30) + seconds / 60 * pi / 30));
canvas.drawLine(Offset.zero, Offset(0.- 60), mPaint.. strokeWidth =3);
canvas.restore();
canvas.save();
///Calculate the Angle of rotation required for the second hand position
canvas.rotate((seconds * pi / 30));
canvas.drawLine(
Offset.zero,
Offset(0.- 80.), mPaint .. strokeWidth =2
..color = Colors.red);
canvas.restore();
canvas.drawCircle(Offset(0.0), 5, mPaint.. style = PaintingStyle.fill); }Copy the code
The effect is as follows:
The last
Bezier curve to achieve wavy line effect:
void wave(Canvas canvas, Size size) {
canvas.save();
var waveWidth = size.width / 8;
canvas.translate(-size.width / 2 * 3.0); Path path = Path().. moveTo(0.0);
///Determine curve path
for (var i = 1, j = - 1; i < 16; i += 2) {
path.quadraticBezierTo(size.width * value + waveWidth * i, j * waveHeight,
size.width * value + waveWidth * (i + 1), 0); j = -j; } path.. lineTo(waveWidth *16.60).. lineTo(0.60).. lineTo(0.0); canvas.drawPath( path, mPaint .. style = PaintingStyle.fill .. color = Colors.orange); canvas.restore(); }Copy the code
class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Draw Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key}) : super(key: key); @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { Timer timer; int value = 0; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { timer = Timer.periodic(Duration(milliseconds: 100), (timer) { setState(() { value++; if(value >=10){ value = 0; }}); }); }); } @override void dispose() { timer.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: new AppBar( title: new Text('$hours:$minutes:$seconds:${DateTime.now().millisecond}'), ), body: CustomPaint( painter: KarlPainter(value*0.1), size: mediaQuery.of (context).size,); }}Copy the code
The effect is as follows:
The source address