Flutter之CustomPainter时钟绘制

coding

无意间在网上看到下图的绘制效果,便想着画一个时钟,正好学习一下,先上图。

环形圆

时钟

请大家忽略那个可恶的数字“0”。。。

环形圆关键代码

  1. @override

  2. void paint(Canvas canvas,Size size){

  3. int n =20;

  4. var range =List<int>.generate(n,(i)=> i +1);

  5. for(int i in range){

  6. double x =2* math.pi / n;

  7. double dx = radius * math.sin(i * x);

  8. double dy = radius * math.cos(i * x);

  9. print("dx${i.toString()}=>${dx.toString()}");

  10. print("dy${i.toString()}=>${dy.toString()}");

  11. canvas.drawCircle(Offset(dx, dy), radius, myPaint);

  12. }

  13. }

时钟完整代码

  1. classTimeClockWidgetextendsStatefulWidget{

  2. @override

  3. _TimeClockWidgetState createState()=>_TimeClockWidgetState();

  4. }


  5. class_TimeClockWidgetStateextendsState<TimeClockWidget>{

  6. Timer timer;


  7. @override

  8. void initState(){

  9. // TODO: implement initState

  10. super.initState();

  11. timer =Timer.periodic(Duration(seconds:1),(timer){

  12. setState((){});

  13. });

  14. }


  15. @override

  16. void dispose(){

  17. // TODO: implement dispose

  18. super.dispose();

  19. timer.cancel();

  20. }


  21. @override

  22. Widget build(BuildContext context){

  23. returnCenter(child:CustomPaint(painter:CustomTimeClock()));

  24. }

  25. }


  26. classCustomTimeClockextendsCustomPainter{

  27. //大外圆

  28. Paint _bigCirclePaint =Paint()

  29. ..style =PaintingStyle.stroke

  30. ..isAntiAlias =true

  31. ..color =Colors.deepOrange

  32. ..strokeWidth =4;


  33. //粗刻度线

  34. Paint _linePaint =Paint()

  35. ..style =PaintingStyle.fill

  36. ..isAntiAlias =true

  37. ..color =Colors.deepOrange

  38. ..strokeWidth =4;


  39. //圆心

  40. Offset _centerOffset =Offset(0,0);


  41. //圆半径

  42. double _bigRadius =

  43. math.min(Screen.screenHeightDp /3,Screen.screenWidthDp /3);


  44. finalint lineHeight =10;


  45. List<TextPainter> _textPaint =[

  46. _getTextPainter("12"),

  47. _getTextPainter("3"),

  48. _getTextPainter("6"),

  49. _getTextPainter("9"),

  50. ];


  51. //文字画笔

  52. TextPainter _textPainter =newTextPainter(

  53. textAlign:TextAlign.left, textDirection:TextDirection.ltr);


  54. @override

  55. void paint(Canvas canvas,Size size){

  56. // TODO: implement paint

  57. print('_bigRadius: ${_bigRadius}');

  58. //绘制大圆

  59. canvas.drawCircle(_centerOffset, _bigRadius, _bigCirclePaint);

  60. //绘制圆心

  61. _bigCirclePaint.style =PaintingStyle.fill;

  62. canvas.drawCircle(_centerOffset, _bigRadius /20, _bigCirclePaint);

  63. //绘制刻度,秒针转一圈需要跳60下,这里只画6点整的刻度线,但是由于每画一条刻度线之后,画布都会旋转60°(转为弧度2*pi/60),所以画出60条刻度线

  64. for(int i =0; i <60; i++){

  65. _linePaint.strokeWidth = i %5==0?(i %3==0?10:4):1;//设置线的粗细

  66. canvas.drawLine(Offset(0, _bigRadius - lineHeight),Offset(0, _bigRadius),

  67. _linePaint);

  68. canvas.rotate(math.pi /30);//2*math.pi/60

  69. }

  70. //方法一:绘制数字,此处暂时没想到更好的方法,TextPainter的绘制间距老有问题,不好控制

  71. /* _textPaint[0].layout();

  72. _textPaint[0].paint(canvas, new Offset(-12, -_bigRadius+20));

  73. _textPaint[1].layout();

  74. _textPaint[1].paint(canvas, new Offset(_bigRadius-30,-12));

  75. _textPaint[2].layout();

  76. _textPaint[2].paint(canvas, new Offset(-6,_bigRadius-40));

  77. _textPaint[3].layout();

  78. _textPaint[3].paint(canvas, new Offset(-_bigRadius+20,-12));*/


  79. //方法二:绘制数字,

  80. for(int i =0; i <12; i++){

  81. canvas.save();//与restore配合使用保存当前画布

  82. canvas.translate(0.0,-_bigRadius+30);//平移画布画点于时钟的12点位置,+30为了调整数字与刻度的间隔

  83. _textPainter.text =TextSpan(

  84. style:newTextStyle(color:Colors.deepOrange, fontSize:22),

  85. text: i.toString());

  86. canvas.rotate(-deg2Rad(30)* i);//保持画数字的时候竖直显示。

  87. _textPainter.layout();

  88. _textPainter.paint(

  89. canvas,Offset(-_textPainter.width /2,-_textPainter.height /2));

  90. canvas.restore();//画布重置,恢复到控件中心

  91. canvas.rotate(deg2Rad(30));//画布旋转一个小时的刻度,把数字和刻度对应起来

  92. }

  93. //绘制指针,这个也好理解

  94. int hours =DateTime.now().hour;

  95. int minutes =DateTime.now().minute;

  96. int seconds =DateTime.now().second;

  97. print("时: ${hours} 分:${minutes} 秒: ${seconds}");

  98. //时针角度//以下都是以12点为0°参照

  99. //12小时转360°所以一小时30°

  100. double hoursAngle =(minutes /60+ hours -12)* math.pi /6;//把分钟转小时之后*(2*pi/360*30)

  101. //分针走过的角度,同理,一分钟6°

  102. double minutesAngle =(minutes + seconds /60)* math.pi /30;//(2*pi/360*6)

  103. //秒针走过的角度,同理,一秒钟6°

  104. double secondsAngle = seconds * math.pi /30;

  105. //画时针

  106. _linePaint.strokeWidth =4;

  107. canvas.rotate(hoursAngle);

  108. canvas.drawLine(Offset(0,0),newOffset(0,-_bigRadius +80), _linePaint);

  109. //画分针

  110. _linePaint.strokeWidth =2;

  111. canvas.rotate(-hoursAngle);//先把之前画时针的角度还原。

  112. canvas.rotate(minutesAngle);

  113. canvas.drawLine(Offset(0,0),newOffset(0,-_bigRadius +60), _linePaint);

  114. //画秒针

  115. _linePaint.strokeWidth =1;

  116. canvas.rotate(-minutesAngle);//同理

  117. canvas.rotate(secondsAngle);

  118. canvas.drawLine(Offset(0,0),newOffset(0,-_bigRadius +30), _linePaint);

  119. }


  120. @override

  121. bool shouldRepaint(CustomPainter oldDelegate){

  122. // TODO: implement shouldRepaint

  123. returntrue;

  124. }


  125. staticTextPainter _getTextPainter(String msg){

  126. returnnewTextPainter(

  127. text:TextSpan(

  128. style:newTextStyle(color:Colors.deepOrange, fontSize:22),

  129. text: msg),

  130. textAlign:TextAlign.center,

  131. textDirection:TextDirection.ltr);

  132. }


  133. //角度转弧度

  134. num deg2Rad(num deg)=> deg *(math.pi /180.0);

  135. }

Screen代码

Screen参考screenutil改的. https://github.com/OpenFlutter/flutter_ScreenUtil Screen.init()程序启动的时候调用就行。。。

  1. import'package:flutter/material.dart';

  2. import'dart:ui'as ui;


  3. classScreen{

  4. //一些固定的配置参数

  5. staticfinaldouble width =1080;//px

  6. staticfinaldouble height =1920;//px

  7. staticfinalbool allowFontScaling =false;



  8. ///当前设备宽度 dp

  9. staticdouble _screenWidthDp;

  10. ///当前设备高度 dp

  11. staticdouble _screenHeightDp;

  12. ///设备的像素密度

  13. staticdouble _screenPixelRatio;


  14. ///状态栏高度 dp 刘海屏会更高

  15. staticdouble _topSafeHeight;

  16. ///底部安全区距离 dp

  17. staticdouble _bottomSafeHeight;


  18. ///每个逻辑像素的字体像素数,字体的缩放比例

  19. staticdouble _textScaleFactory;


  20. staticvoid init(){

  21. MediaQueryData mediaQueryData =MediaQueryData.fromWindow(ui.window);

  22. _screenWidthDp=mediaQueryData.size.width;

  23. _screenHeightDp=mediaQueryData.size.height;

  24. _screenPixelRatio=mediaQueryData.devicePixelRatio;

  25. _topSafeHeight=mediaQueryData.padding.top;

  26. _bottomSafeHeight=mediaQueryData.padding.bottom;

  27. _textScaleFactory=mediaQueryData.textScaleFactor;

  28. }


  29. ///当前设备宽度 dp

  30. staticdoubleget screenWidthDp =>_screenWidthDp;


  31. ///当前设备高度 dp

  32. staticdoubleget screenHeightDp =>_screenHeightDp;


  33. ///当前设备宽度 px

  34. staticdoubleget screenWidth => _screenWidthDp * _screenPixelRatio;


  35. ///当前设备高度 px

  36. staticdoubleget screenHeight => _screenHeightDp * _screenPixelRatio;


  37. ///设备的像素密度

  38. staticdoubleget screenPixelRatio =>_screenPixelRatio;


  39. ///状态栏高度 dp 刘海屏会更高

  40. staticdoubleget topSafeHeight=>_topSafeHeight;


  41. ///底部安全区距离 dp

  42. staticdoubleget bottomSafeHeight =>_bottomSafeHeight;


  43. ///每个逻辑像素的字体像素数,字体的缩放比例

  44. staticdoubleget textScaleFactory =>_textScaleFactory;


  45. ///ToolBarHeight +status高度

  46. staticdoubleget navigationBarHeight =>_topSafeHeight+toolBarHeight;


  47. ///TooBar高度

  48. staticdoubleget toolBarHeight =>kToolbarHeight;



  49. ///实际的dp与设计稿px 的比例


  50. staticget scaleWidth => screenWidthDp / width;


  51. staticget scaleHeight => screenHeightDp / height;


  52. ///根据设计稿的设备宽度适配

  53. ///高度也根据这个来做适配可以保证不变形

  54. static setWidth(double width)=> width * scaleWidth;


  55. /// 根据设计稿的设备高度适配

  56. /// 当发现设计稿中的一屏显示的与当前样式效果不符合时,

  57. /// 或者形状有差异时,高度适配建议使用此方法

  58. /// 高度适配主要针对想根据设计稿的一屏展示一样的效果

  59. static setHeight(double height)=> height * scaleHeight;

  60. ///字体大小适配方法

  61. ///@param fontSize 传入设计稿上字体的px ,

  62. ///@param allowFontScaling 控制字体是否要根据系统的“字体大小”辅助选项来进行缩放。默认值为false。

  63. ///@param allowFontScaling Specifies whether fonts should scale to respect Text Size accessibility settings. The default is false.

  64. static setSp(double fontSize)=> allowFontScaling

  65. ? setWidth(fontSize)

  66. : setWidth(fontSize)/ textScaleFactory;

  67. }


本文分享自微信公众号 - Flutter学习簿(gh_d739155d3b2c)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

以上是 Flutter之CustomPainter时钟绘制 的全部内容, 来源链接: utcz.com/z/510026.html

回到顶部