文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Flutter怎么用Canvas绘制精美表盘效果

2023-06-29 14:22

关注

这篇“Flutter怎么用Canvas绘制精美表盘效果”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Flutter怎么用Canvas绘制精美表盘效果”文章吧。

实现效果如下:

Flutter怎么用Canvas绘制精美表盘效果

前面说到使用 Canvas 实现该表盘效果,而在 Flutter 中使用 Canvas 更多的则是继承 CustomPainter 类实现 paint 方法,然后在 CustomPaint 中使用自定义实现的 CustomPainter。 比如这里创建的 DialPainter 使用如下:

  @override  Widget build(BuildContext context) {    double width = MediaQuery.of(context).size.width;    return Container(      color: const Color.fromARGB(255, 35, 36, 38), /// 设置背景      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;  }}

之后所有绘制的核心代码都在 DialPainter 中的 paint 中实现的,其中 shouldRepaint 是指父控件重新渲染时是否重新绘制,这里设置为 true 表示每次都重新绘制。

接下来就看具体实现代码,我们将整个表盘效果的实现分为三部分:面板刻度指针。涉及到的主要知识点包括:PaintCanvasPathTextPainter 等。

初始化

在开始进行绘制之前,先进行画笔和长度单位的初始化。

在整个效果的实现上会多次使用到画笔 Paint ,为了避免创建多个画笔实例,所以创建一个 Paint 成员变量,后续通过修改其属性值来满足不同效果的绘制。

  late final Paint _paint = _initPaint();  Paint _initPaint() {    return Paint()      ..isAntiAlias = true      ..color = Colors.white;  }

通过初始化代码设置了画笔的抗锯齿和默认颜色。

为了方便后续使用长、宽、半径等长度,创建对应的成员变量,同时为了适配不同表盘宽高,保证展示效果一致,在绘制时不直接使用数值,而使用比例长度:

/// 画布宽度late double width;/// 画布高度late double height;/// 表盘半径late double radius;/// 比例单位长度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;}

半径取宽度和高度的最小值,然后除以 2 ,单位长度 unit 取值为半径除以 15。

面板

首先绘制一个线性渐变的圆:

/// 绘制一个线性渐变的圆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);

通过 Gradient.linear 创建一个线性渐变颜色并设置给 Paint.shader,绘制出来效果如下:

Flutter怎么用Canvas绘制精美表盘效果

然后在其上添加一层径向渐变,增加表盘的立体感:

/// 绘制一层径向渐变的圆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(216,205, 212, 217),  const Color.fromARGB(216,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);

使用 Gradient.radial 创建一个径向渐变的颜色,效果如下:

Flutter怎么用Canvas绘制精美表盘效果

最后再在表盘内添加一个边框和阴影增加对比效果:

/// 绘制 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);///绘制阴影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);

最后表盘效果如下:

Flutter怎么用Canvas绘制精美表盘效果

刻度

面板绘制完成,接下来就是绘制刻度线以及刻度值。

刻度线

代码如下:

double dialCanvasRadius = radius -  0.8 * unit;canvas.save();canvas.translate(width/2, height/2);var y = 0.0;var x1 = 0.0;var x2 = 0.0;_paint.shader = null;_paint.color = const Color(0xFF929394);for( int i = 0; i < 60; i++){  x1 =  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();

表盘上有 60 个刻度,其中 12 个为小时刻度其余为分钟刻度,循环 60 次,通过 i % 5 == 0 判断是否为小时刻度,从而使用不同的 x 和 y 坐标,实现不同的长度和宽度。

这里为了避免去计算圆上的点坐标,采用的是旋转画布来实现。画布默认旋转点位左上角,所以需要通过 canvas.translate(width/2, height/2) 将旋转点移动到表盘的中心点,然后每绘制完一个刻度画布旋转 2*pi/60 的角度,即 6 度。因为画布进行了平移所以绘制的坐标都是基于圆中心,即相当于圆点移动到了圆中心。

Flutter怎么用Canvas绘制精美表盘效果

最终实现刻度效果如图:

Flutter怎么用Canvas绘制精美表盘效果

刻度值

绘制完刻度后需要给刻度标值,这里只显示 3、6、9、12 四个刻度值,代码如下:

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; i++){  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]);}

绘制文字使用的是 TextPainter 对象,首先创建一个 TextPainter 对象,用于测量获取文字的宽高,因为这里只显示 4 个刻度值,所以这里直接将对应需要绘制的坐标计算出来,然后循环绘制显示的刻度值在对应的位置即可。实现后效果如下:

Flutter怎么用Canvas绘制精美表盘效果

指针

接下来就是指针的绘制,指针分为三部分:时针分针秒针。在绘制指针之前还需要绘制中心点:

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]);/// 底部背景_paint  ..shader = radialGradient  ..style = PaintingStyle.fill;canvas.drawCircle(  Offset(width/2, height/2), 2 * unit, _paint);/// 顶部圆点_paint  ..shader = null  ..style = PaintingStyle.fill  ..color = const Color(0xFF121314);canvas.drawCircle(Offset(width/2, height/2), 0.8 * unit, _paint);

代码很简单,在中心绘制两个圆,一个底部的径向渐变的大圆,一个顶部深色的小圆,如图:

Flutter怎么用Canvas绘制精美表盘效果

时针

时针分为三部分,连接中心的矩形、连接矩形的半圆弧、最后的箭头,如图:

Flutter怎么用Canvas绘制精美表盘效果

代码实现如下:

double hourHalfHeight = 0.4 * unit;double hourRectRight =   7 * unit;Path hourPath = Path();/// 添加矩形 时针主体hourPath.moveTo(0 - hourHalfHeight, 0 - hourHalfHeight);hourPath.lineTo(hourRectRight, 0 - hourHalfHeight);hourPath.lineTo(hourRectRight, 0 + hourHalfHeight);hourPath.lineTo(0 - hourHalfHeight, 0 + hourHalfHeight);/// 时针箭头尾部弧形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);/// 时针箭头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);///绘制_paint.color = const Color(0xFF232425);canvas.drawPath(hourPath, _paint);canvas.restore();

这里是通过 Path 先添加一个矩形到路径,然后添加一个圆弧,圆弧向左偏移一定单位,防止对接效果不好,再添加一个三角形也就是箭头图形。这里所有的坐标计算都是基于圆点在圆盘的中心点计算的,所以需要平移画布,将圆点移动到圆盘的中心点,即 canvas.translate(width/2, height/2) 跟绘制表盘刻度的思路是一样的,最后再通过 canvas.drawPath 进行绘制。效果如下:

Flutter怎么用Canvas绘制精美表盘效果

分针

分针的绘制相对比较简单,因为分针就一个圆角矩形,使用画布的 drawRRect 方法即可:

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);/// 绘制分针var rRect = RRect.fromLTRBR(minutesLeft, minutesTop, minutesRight, minutesBottom, Radius.circular(0.42 * unit));_paint.color = const Color(0xFF343536);canvas.drawRRect(rRect, _paint);canvas.restore();

实现思路同样是将画布移动到圆点,然后计算坐标进行绘制,这里需要注意的是分针尾部是超过了中心大圆点的,所以这里 left 需要向左偏移一定单位:

Flutter怎么用Canvas绘制精美表盘效果

这里为了看到分针的效果,将时针隐藏掉了

秒针

秒针分为四部分:尾部弧形、尾部圆角矩形、细针、中心圆点:

Flutter怎么用Canvas绘制精美表盘效果

实现代码:

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);/// 尾部弧形var rect = Rect.fromLTWH(secondsLeft, secondsTop, 2.5 * unit, hourHalfHeight * 2);secondsPath.addArc(rect, pi/2, pi);/// 尾部圆角矩形var rRect = RRect.fromLTRBR(secondsLeft + 1 * unit, secondsTop, - 2 * unit, secondsBottom, Radius.circular(0.25 * unit));secondsPath.addRRect(rRect);/// 指针secondsPath.moveTo(- 2 * unit, - 0.125 * unit);secondsPath.lineTo(secondsRight, 0);secondsPath.lineTo(-2 * unit, 0.125 * unit);/// 中心圆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);/// 绘制阴影canvas.drawShadow(secondsPath, const Color(0xFFcc0000), 0.17 * unit, true);/// 绘制秒针_paint.color = const Color(0xFFcc0000);canvas.drawPath(secondsPath, _paint);canvas.restore();

思路跟时针的实现是一样的,通过 Path 将圆弧、圆角矩形、三角形、中心圆形组合起来,计算坐标同样的是以圆盘中心为圆点,所有同样需要使用 translate 移动画布圆点后绘制。实现效果:

Flutter怎么用Canvas绘制精美表盘效果

同样的为了更好的看到秒针的效果,将时针、分针隐藏了

动起来

经过上面的绘制,我们将表盘的所有元素都绘制出来了,但是最重要的没有动起来,动起来的关键就是要让时针、分针、秒针偏移一定的角度,既然是偏移角度自然就想到了旋转画布来实现,类似于绘制刻度一样。

分别在时针、分针、秒针的绘制之前对画布进行一定角度的旋转:

/// 时针canvas.save();canvas.translate(width/2, height/2);canvas.rotate(2*pi/4);_paint.color = const Color(0xFF232425);canvas.drawPath(hourPath, _paint);canvas.restore();///分针canvas.save();canvas.translate(width/2, height/2);canvas.rotate(2*pi/4*2);var rRect = RRect.fromLTRBR(minutesLeft, minutesTop, minutesRight, minutesBottom, Radius.circular(0.42 * unit));_paint.color = const Color(0xFF343536);canvas.drawRRect(rRect, _paint);canvas.restore();///秒针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();

分别在时针、分针、秒针的绘制前对画布旋转 90&deg;、180&deg;、270&deg; ,效果如下:

Flutter怎么用Canvas绘制精美表盘效果

通过画布旋转实现了我们想要的效果,接下来就是让指针根据时间旋转相应的角度。可以通过 DateTime.now() 获取当前时间对象,进而获取当前的小时、分钟和秒。然后根据对应的值计算出相应的角度:

 var date = DateTime.now();/// 时针canvas.rotate(2*pi/60*((date.hour - 3 + date.minute / 60 + date.second/60/60) * 5 ));/// 分针canvas.rotate(2*pi/60 * (date.minute - 15 + date.second / 60));/// 秒针canvas.rotate(2*pi/60 * (date.second - 15));

首先将 360 度分为 60 份,时针一小时为 5 份,因为角度的起始是在右侧中心点,所以获取的小时需要减 3,再加上分钟、秒钟占小时的比例;同理分别计算分钟、秒钟的角度,最终实现时针、分针、秒针根据当前时间展示。

角度计算对了以后,还需要刷新整个表盘,即每秒钟刷新一次,刷新时获取当前时间重新绘制时针、分针、秒针的位置,实现动态效果,这里使用 Timer 每一秒钟调用父布局的 setState 实现。

  @override  void initState() {    super.initState();    Timer.periodic(const Duration(seconds: 1), (timer) {      setState(() {});    });  }

大功告成,最终实现了开始展示的表盘动态效果。

Flutter怎么用Canvas绘制精美表盘效果

以上就是关于“Flutter怎么用Canvas绘制精美表盘效果”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯