In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article "Flutter how to use Canvas to draw exquisite dial effect" most people do not understand, so the editor summarized the following content, detailed, clear steps, with a certain reference value, I hope you can get something after reading this article, let's take a look at this "Flutter how to use Canvas to draw exquisite dial effect" article.
The results are as follows:
I mentioned earlier that Canvas is used to achieve the dial effect, while in Flutter, Canvas is more likely to inherit the CustomPainter class to implement the paint method, and then use a custom implemented CustomPainter in CustomPaint. For example, the DialPainter created here is used as follows:
@ override Widget build (BuildContext context) {double width = MediaQuery.of (context) .size.width; return Container (color: const Color.fromARGB (255,35,36,38), / / set background child: Center (child: CustomPaint (size: Size (width, width), painter: DialPainter (),) } class DialPainter extends CustomPainter {@ override void paint (Canvas canvas, Size size) {} @ override bool shouldRepaint (covariant CustomPainter oldDelegate) {return true;}}
After that, all the core code for drawing is implemented in paint in DialPainter, where shouldRepaint refers to whether the parent control will be redrawn when it is re-rendered. Here, set to true means to redraw every time.
Next, let's look at the specific implementation code, we divide the implementation of the entire dial effect into three parts: panel, scale, pointer. The main knowledge points involved include: Paint, Canvas, Path, TextPainter and so on.
Initialization
Initialize the brush and length units before you start painting.
Brush Paint will be used many times in the implementation of the whole effect, in order to avoid creating multiple brush instances, create a Paint member variable, and then modify its attribute values to meet different effects of painting.
Late final Paint _ paint = _ initPaint (); Paint _ initPaint () {return Paint ().. isAntiAlias = true.. color = Colors.white;}
The anti-aliasing and default color of the brush is set through the initialization code.
In order to facilitate the subsequent use of length, width, radius and other lengths, create corresponding member variables, and in order to adapt to the width and height of different dials and ensure the consistent display effect, use proportional length instead of directly using numerical values when drawing:
/ / canvas width late double width;/// canvas height late double height;/// dial radius late double radius;/// scale unit length late double unit; @ overridevoid paint (Canvas canvas, Size size) {initSize (size);} void initSize (Size size) {width = size.width; height = size.height; radius = min (width, height) / 2; unit = radius / 15;}
The radius takes the minimum value of width and height, then divided by 2, and the value of unit per unit length is taken as the radius divided by 15.
Panel
First draw a circle with a linear gradient:
/ / draw a linear gradient circle var gradient = ui.Gradient.linear (Offset (width/2, height/2-radius,), Offset (width/2, height/2 + radius), [const Color (0xFFF9F9F9), const Color (0xFF666666)]; _ paint.shader = gradient;_paint.color = Colors.white;canvas.drawCircle (Offset (width/2, height/2), radius, _ paint)
Create a linear gradient color through Gradient.linear and set it to Paint.shader. The drawing effect is as follows:
Then add a layer of radial gradient to it to increase the three-dimensional feel of the dial:
/ / draw a circle with radial gradient var radialGradient = ui.Gradient.radial (Offset (width/2, height/2), radius, [const Color.fromARGB (216,246,248,249), const Color.fromARGB (216,229,235,238), const Color.fromARGB (216Min 205,212,217), const Color.fromARGB (216PM 245,247,249),], [0,0.92,0.93,1.0]; _ paint.shader = radialGradient Canvas.drawCircle (Offset (width/2, height/2), radius-0.3 * unit, _ paint)
Use Gradient.radial to create a radial gradient color with the following effect:
Finally, add a border and shadow to the dial to increase the contrast effect:
/ / draw bordervar shadowRadius = radius-0.8 * unit;_paint.. color = const Color.fromARGB (33,0,0,0).. shader = null.. style = PaintingStyle.stroke.. strokeWidth = 0.1 * unit;canvas.drawCircle (Offset (width/2, height/2), shadowRadius-0.2 * unit, _ paint); / / draw shadows Path path = Path (); path.moveTo (width/2, height/2) Var rect = Rect.fromLTRB (width/2-shadowRadius, height/2-shadowRadius, width/2+shadowRadius, height/2 + shadowRadius); path.addOval (rect); canvas.drawShadow (path, const Color.fromARGB (51,0,0,0), 1 * unit, true)
The final dial effect is as follows:
Scale
When the panel is drawn, the next step is to draw the tick mark and the scale value.
Scale line
The code is as follows:
Double dialCanvasRadius = radius-0. 8 * unit;canvas.save (); canvas.translate (width/2, height/2); var y = 0. 0 dialCanvasRadius-(I% 5 = 0? 0.85 * unit: 1 * unit); x2 = dialCanvasRadius-(I% 5 = 0? 2 * unit: 1.67 * unit) _ paint.strokeWidth = I% 5 = 0? 0.38 * unit: 0.2 * unit; canvas.drawLine (Offset (x1, y), Offset (x2, y), _ paint); canvas.rotate (2*pi/60);} canvas.restore ()
There are 60 scales on the dial, of which 12 are hour scales and the rest are minute scales with 60 cycles. Whether it is an hour scale is judged by I% 5 = = 0, thus using different x and y coordinates to achieve different lengths and widths.
In order to avoid calculating the point coordinates on the circle, it is realized by rotating the canvas. The canvas defaults to the upper left corner of the rotation point, so you need to move the rotation point to the center of the dial through canvas.translate (width/2, height/2), and then rotate the 2*pi/60 angle of 6 degrees for each scale canvas drawn. Because the canvas is translated, the coordinates drawn are based on the center of the circle, which is equivalent to the movement of the dot to the center of the circle.
Finally, the calibration effect is as shown in the figure:
Scale value
You need to give the scale value after drawing the scale. Only four scale values of 3, 6, 9 and 12 are displayed here. The code is as follows:
Double dialCanvasRadius = radius-0.8 * unit;var textPainter = TextPainter (text: const TextSpan (text: "3", style: TextStyle (color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold, height: 1.0), textDirection: TextDirection.rtl, textWidthBasis: TextWidthBasis.longestLine, maxLines: 1,).. layout (); var offset = 2.25 * unit Var points = [Offset (width / 2 + dialCanvasRadius-offset-textPainter.width, height / 2-textPainter.height / 2), Offset (width / 2-textPainter.width / 2, height / 2 + dialCanvasRadius-offset-textPainter.height), Offset (width / 2-dialCanvasRadius + offset, height / 2-textPainter.height / 2), Offset (width / 2-textPainter.width, height / 2-dialCanvasRadius + offset); for (int I = 0; I < 4) ) {textPainter = TextPainter (text: TextSpan (text: "${(I + 1) * 3}", style: const TextStyle (color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold, height: 1.0)), textDirection: TextDirection.rtl, textWidthBasis: TextWidthBasis.longestLine, maxLines: 1,). Layout (); textPainter.paint (canvas, points [I]);}
The TextPainter object is used to draw the text. First, a TextPainter object is created to measure the width and height of the text. Because only four scale values are displayed here, the corresponding coordinates to be drawn are calculated directly here, and then the scale values displayed in the loop are drawn in the corresponding position. The results are as follows:
Pointer
Then there is the drawing of the pointer, which is divided into three parts: the hour hand, the minute hand and the second hand. You also need to draw the center point before you draw the pointer:
Var radialGradient = ui.Gradient.radial (Offset (width / 2, height / 2), radius, [const Color.fromARGB (255,200,200,200), const Color.fromARGB (255,190,190,190), const Color.fromARGB (255,130,130,130),], [0,0.9,1.0]); / / bottom background _ paint.. shader = radialGradient.. style = PaintingStyle.fill Canvas.drawCircle (Offset (width/2, height/2), 2 * unit, _ paint); / / Top dot _ paint.. shader = null.. style = PaintingStyle.fill.. color = const Color (0xFF121314); canvas.drawCircle (Offset (width/2, height/2), 0.8 * unit, _ paint)
The code is simple: draw two circles in the center, a large circle with a radial gradient at the bottom and a small dark circle at the top, as shown in the figure:
Hour hand
The hour hand is divided into three parts, the rectangle connecting the center, the semicircular arc connecting the rectangle, and the final arrow, as shown in the figure:
The code is implemented as follows:
Double hourHalfHeight = 0.4 * unit;double hourRectRight = 7 * unit;Path hourPath = Path (); / / add rectangular clockwise body hourPath.moveTo (0-hourHalfHeight, 0-hourHalfHeight); hourPath.lineTo (hourRectRight, 0-hourHalfHeight); hourPath.lineTo (hourRectRight, 0 + hourHalfHeight); hourPath.lineTo (0-hourHalfHeight, 0 + hourHalfHeight); / / clockwise arrow tail arc double offsetTop = 0.5 * unit;double arcWidth = 1.5 * unit;double arrowWidth = 2.17 * unit Double offset = 0.42 * unit;var rect = Rect.fromLTWH (hourRectRight-offset, 0-hourHalfHeight-offsetTop, arcWidth, hourHalfHeight * 2 + offsetTop * 2); hourPath.addArc (rect, pi/2, pi); / / clockwise arrow hourPath.moveTo (hourRectRight-offset + arcWidth/2, 0-hourHalfHeight-offsetTop); hourPath.lineTo (hourRectRight-offset + arcWidth/2 + arrowWidth, 0); hourPath.lineTo (hourRectRight-offset + arcWidth/2, 0 + hourHalfHeight + offsetTop); hourPath.close (); canvas.save () Canvas.translate (width/2, height/2); / / draw _ paint.color = const Color (0xFF232425); canvas.drawPath (hourPath, _ paint); canvas.restore ()
Here is through Path to add a rectangle to the path, and then add an arc, arc offset to the left of a certain unit, to prevent poor docking effect, and then add a triangle that is arrow graphics. All the coordinate calculations here are based on the point at the center of the disc, so it is necessary to translate the canvas and move the dot to the center point of the disc, that is, canvas.translate (width/2, height/2) is the same as drawing the dial scale, and then draw it through canvas.drawPath. The effect is as follows:
Minute hand
The minute hand is relatively easy to draw, because the minute hand is a rounded rectangle, and you can use the drawRRect method of the canvas:
Double hourHalfHeight = 0.4 * unit;double minutesLeft =-1.33 * unit;double minutesTop =-hourHalfHeight;double minutesRight = 11 * unit;double minutesBottom = hourHalfHeight;canvas.save (); canvas.translate (width/2, height/2); / / drawing minute hand var rRect = RRect.fromLTRBR (minutesLeft, minutesTop, minutesRight, minutesBottom, Radius.circular); _ paint.color = const Color (0xFF343536); canvas.drawRRect (rRect, _ paint); canvas.restore ()
The implementation idea is also to move the canvas to the dot, and then calculate the coordinates to draw. It should be noted here that the tail of the minute hand exceeds the central big dot, so here left needs to offset a certain unit to the left:
In order to see the effect of the minute hand, the hour hand is hidden here.
Second hand
The second hand is divided into four parts: tail arc, tail rounded rectangle, fine needle, center dot:
Implementation code:
Double hourHalfHeight = 0.4 * unit;double secondsLeft =-4.5 * unit;double secondsTop =-hourHalfHeight;double secondsRight = 12.5 * unit;double secondsBottom = hourHalfHeight;Path secondsPath = Path (); secondsPath.moveTo (secondsLeft, secondsTop); / / tail arc var rect = Rect.fromLTWH (secondsLeft, secondsTop, 2.5 * unit, hourHalfHeight * 2); secondsPath.addArc (rect, pi/2, pi) / / tail rounded rectangle var rRect = RRect.fromLTRBR (secondsLeft + 1 * unit, secondsTop,-2 * unit, secondsBottom, Radius.circular (0.25 * unit)); secondsPath.addRRect (rRect); / / pointer secondsPath.moveTo (- 2 * unit,-0.125 * unit); secondsPath.lineTo (secondsRight, 0); secondsPath.lineTo (- 2 * unit, 0.125 * unit) / / Center circle var ovalRect = Rect.fromLTWH (- 0.67 * unit,-0.67 * unit, 1.33 * unit, 1.33 * unit); secondsPath.addOval (ovalRect); canvas.save (); canvas.translate (width/2, height/2); / / draw shadow canvas.drawShadow (secondsPath, const Color (0xFFcc0000), 0.17 * unit, true); / / draw second hand _ paint.color = const Color (0xFFcc0000); canvas.drawPath (secondsPath, _ paint) Canvas.restore ()
The idea is the same as the realization of the clockwise, through the Path will arc, rounded rectangle, triangle, center circle together, the calculation of coordinates is the same as the center of the disk as the dot, all the same need to use translate to move the canvas dot after drawing. Achieve results:
Similarly, in order to better see the effect of the second hand, hide the hour hand and minute hand.
Move.
After the above drawing, we have drawn all the elements of the dial, but the most important thing is not to move. The key to moving is to offset the hour hand, minute hand and second hand by a certain angle. since it is an offset angle, we naturally want to rotate the canvas, similar to the drawing scale.
Rotate the canvas at a certain angle before drawing the hour hand, minute hand, and second hand respectively:
/ / hour hand canvas.save (); canvas.translate (width/2, height/2); canvas.rotate (2*pi/4); _ paint.color = const Color (0xFF232425); canvas.drawPath (hourPath, _ paint); canvas.restore (); / / minute hand canvas.save (); canvas.translate (width/2, height/2); canvas.rotate (2*pi/4*2); var rRect = RRect.fromLTRBR (minutesLeft, minutesTop, minutesRight, minutesBottom, Radius.circular (.42 * unit)) _ paint.color = const Color (0xFF343536); canvas.drawRRect (rRect, _ paint); canvas.restore (); / / second hand canvas.save (); canvas.translate (width/2, height/2); canvas.rotate (2*pi/4*3); canvas.drawShadow (secondsPath, const Color (0xFFcc0000), 0.17 * unit, true); _ paint.color = const Color (0xFFcc0000); canvas.drawPath (secondsPath, _ paint); canvas.restore ()
Rotate the canvas 90 °, 180 °and 270 °before drawing the hour hand, minute hand and second hand, respectively. The effects are as follows:
Through the canvas rotation to achieve the desired effect, the next step is to let the pointer rotate the corresponding angle according to time. You can get the current time object through DateTime.now (), and then get the current hours, minutes, and seconds. Then the corresponding angle is calculated according to the corresponding value:
Var date = DateTime.now (); / / hour hand canvas.rotate (2 seconds piplet 60 * ((date.hour-3 + date.minute / 60 + date.second/60/60) * 5)); / / minute hand canvas.rotate (2 seconds piplet 60 * (date.minute-15 + date.second/60)); / / second hand canvas.rotate (2 seconds piplet 60 * (date.second-15))
First of all, 360 degrees is divided into 60 parts, and the hour hand is 5 parts an hour. Because the angle starts at the center point on the right, the hour obtained needs to be subtracted by 3, plus the proportion of minutes and seconds to the hour. In the same way, the angles of minutes and seconds are calculated respectively. Finally, the hour hand, minute hand and second hand are displayed according to the current time.
After the angle calculation is correct, you also need to refresh the entire dial, that is, once every second, to obtain the position of the hour hand, minute hand and second hand at the current time to redraw the dynamic effect. Here, use Timer to call the setState implementation of the parent layout every second.
@ override void initState () {super.initState (); Timer.periodic (const Duration (seconds: 1), (timer) {setState (() {});});}
It is done, and finally realize the dynamic effect of the dial that began to be displayed.
The above is about the content of this article on "how to draw beautiful dial effects with Canvas in Flutter". I believe we all have a certain understanding. I hope the content shared by the editor will be helpful to you. If you want to know more about the relevant knowledge, please pay attention to the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.