19

Flutter 绘制集录 | 画个表 (上)

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzI0NjU3MDA4NQ%3D%3D&%3Bmid=2247484657&%3Bidx=1&%3Bsn=f6baee00f5d2c323ea4b889e044bfe7a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、本作介绍和准备

1. 效果图

如下图,通过 Flutter 的 Canvas 绘制如下的 静态 表面。

ummu6r6.png!mobile

本文知识点

1】. Flutter 中绘制旋转型的元素
2】. Flutter 中弧线的方式
3】. Flutter 中绘制文字的方式
4】. Canvas.save 和 Canvas.restore 的使同。

2.资源准备

绘制文字时,可以指定文字的字体,如下在 assets/fonts 中放入对应字体。

VvMfQfZ.png!mobile

pubspec.yaml 中配置后即可使用。

BzYnIz.png!mobile

3.程序入口

程序入口如下,通过 ClockWidget 组件来绘制表盘。本篇   ClockWidget 只是进行静态效果展现,只需继承 StatelessWidget 即可。在 300*300 的区域内通过 ClockPainter 进行绘制。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: Scaffold(
          body: Center(child: ClockWidget()),
        ));
  }
}

class ClockWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(300300),
      painter: ClockPainter(),
    );
  }
}

一、表盘外围绘制

1.画板的定义

这个绘制中的旋转操作很多,为了方便处理,可以将画布的中心移到画板区域的中心。这样绘制时原点就会在中心。

umEnUzq.png!mobile

class ClockPainter extends CustomPainter {
  Paint _paint = Paint();

  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(size.width / 2, size.height / 2);
    drawHelp(canvas,size);
  }

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

  void drawHelp(Canvas canvas,Size size){
  canvas.drawPoints(PointMode.lines, [
    Offset( -size.width/2,0),Offset( size.width/2,0),
    Offset( 0,-size.height/2),Offset(0,size.height/2),
  ], Paint());
}
}

2.绘制外圈

首先分析一下外圈的绘制,这里只要运用 绘制圆弧画布旋转 。如下图,左侧是一个弧,然后通过三次旋转画布,再绘制即可的得到右图。代码如下,在每次绘制完后,将画布旋转 90° ,由于涉及局部的旋转操作,需要保存之前的画布状态,局部变换完后,恢复到之前的状态。(其实这里恰好转了360°,已复原,保不保存无所谓,但如果非 360°,如果不保存和恢复,变换将会影响之后的绘制)

四分之一 旋转绘制 EZ36NfE.png!mobile7JVJBf.png!mobile

void drawOuterCircle(Canvas canvas, Size size) {
  final offsetAngle = 5// 圆弧顶点和轴的夹角
  _paint ..strokeWidth = 4
    ..color = Color(0xffD5D5D5)
    ..style = PaintingStyle.stroke;
  canvas.save();
  for (int i = 0; i < 4; i++) {
    canvas.drawArc(
        Rect.fromPoints(Offset(-size.width / 2, -size.height / 2),
            Offset(size.width / 2, size.height / 2)),
        offsetAngle * pi / 180,
        pi / 2 - 2 * offsetAngle * pi / 180,
        false, _paint);
    canvas.rotate(pi / 2);
  }
  canvas.restore(); 
}

3.外圈格点的绘制

如下 drawDot 方法,count 变量表示格点的个数,每次遍历之后,通过 canvas.rotate(perAngle) 进行画布旋转。判断 i % 5 == 0 时绘制较粗的格线,其他的是普通格线。这就是在遍历时根据情况进行绘制的方式,在一些刻度类型的绘制中很常用。

uAZfIrU.png!mobile

void drawDot(Canvas canvas) {
  canvas.save();
  _paint
    ..strokeCap = StrokeCap.round
    ..style = PaintingStyle.fill;
  double count = 60;
  double perAngle = 2 * pi / count;
  for (int i = 0; i < count; i++) {
    if (i % 5 == 0) {
      _paint
        ..strokeWidth = 3
        ..color = Colors.blue;
      canvas.drawLine(Offset(1200), Offset(1350), _paint);
      canvas.drawCircle(Offset(1150), 2, _paint..color = Colors.orange);
    } else {
      _paint
        ..strokeWidth = 1.5
        ..color = Colors.black;
      canvas.drawLine(Offset(1250), Offset(1350), _paint);
    }
    canvas.rotate(perAngle);
  }
  canvas.restore();
}

4.绘制文字

文字通过 TextPainter 对象进行绘制,如下 _drawCircleText 方法绘制圆框中的四个文字。 _drawLogoText 绘制 CHOPS 字体的文字。这样,主要的外框就绘制完毕。

mMNZRz6.png!mobile

final TextPainter _textPainter = TextPainter(
    textAlign: TextAlign.center, textDirection: TextDirection.ltr);

void drawText(Canvas canvas) {
  _drawCircleText(canvas, 'Ⅸ', offsetX: -150);
  _drawCircleText(canvas, 'Ⅲ', offsetX: 150);
  _drawCircleText(canvas, 'Ⅵ', offsetY: 150);
  _drawCircleText(canvas, 'Ⅻ', offsetY: -150);
  _drawLogoText(canvas, offsetY: -80);
}

_drawCircleText(Canvas canvas, String text,
    {double offsetX = 0double offsetY = 0}) {
  _textPainter.text = TextSpan(
      text: text, style: TextStyle(fontSize: 20, color: Colors.blue));
  _textPainter.layout();
  _textPainter.paint(
      canvas,
      Offset.zero.translate(-_textPainter.size.width / 2 + offsetX,
          -_textPainter.height / 2 + offsetY));
}

_drawLogoText(Canvas canvas, {double offsetX = 0double offsetY = 0}) {
  _textPainter.text = TextSpan(
      text: 'Toly',
      style:
          TextStyle(fontSize: 30, color: Colors.blue, fontFamily: 'CHOPS'));
  _textPainter.layout();
  _textPainter.paint(
      canvas,
      Offset.zero.translate(-_textPainter.size.width / 2 + offsetX,
          -_textPainter.height / 2 + offsetY));
}

三、表盘指针绘制

1.时针绘制

如下,通过 drawH(canvas, 120); 绘制120° 偏转的时针。注意,此角度是与横轴正向的夹角。

7by2i2A.png!mobile

void drawH(Canvas canvas, double deg) {
  canvas.save();
  canvas.rotate(deg / 180 * pi);
  _paint
    ..strokeWidth = 3
    ..color = Color(0xff8FC552)
    ..strokeCap = StrokeCap.round;
  canvas.drawLine(Offset.zero, Offset(600), _paint);
  canvas.restore();
}

2.分针绘制

同理,如下,通过 drawM(canvas, 0); 绘制 0° 偏转的分针。

3qaeEz.png!mobile

void drawM(Canvas canvas, double deg) {
  canvas.save();
  canvas.rotate(deg / 180 * pi);
  _paint
    ..strokeWidth = 2
    ..color = Color(0xff87B953)
    ..strokeCap = StrokeCap.round;
  canvas.drawLine(Offset.zero, Offset(800, ), _paint);
  canvas.restore();
}

3.秒针绘制

如下,通过 drawS(canvas, 90); 绘制 90° 偏转的秒针。

ZNJFBn.png!mobile

void drawS(Canvas canvas, double deg) {
  _paint..strokeWidth = 2.5
    ..color = Color(0xff6B6B6B)
    ..strokeCap = StrokeCap.square
    ..style = PaintingStyle.stroke;

  Path path = Path();
  canvas.save();
  canvas.rotate(deg / 180 * pi);
  canvas.save();
  canvas.rotate((360 - 270) / 2 / 180 * pi);
  path.addArc(Rect.fromPoints(Offset(-9-9), Offset(99)), 0270 / 180 * pi);
  canvas.drawPath(path, _paint);
  canvas.restore();

  _paint..strokeCap = StrokeCap.round;
  canvas.drawLine(Offset(-90), Offset(-200), _paint);
  _paint..strokeWidth = 1..color = Colors.black;
  canvas.drawLine(Offset(00), Offset(1000), _paint);

  _paint..strokeWidth = 3..color = Color(0xff6B6B6B);
  canvas.drawCircle(Offset.zero, 5, _paint);

  _paint..color = Color(0xff8FC552)..style = PaintingStyle.fill;
  canvas.drawCircle(Offset.zero, 4, _paint);
  canvas.restore();
}

这里指针指示进行了简单的绘制,你可以在对应的方法中进行自己的处理。这样通过角度的设置就可以将指针进行变动,加上定时器,或者动画就可以形成动态的表面。这些处理我们在下篇进行讲述,谢谢观看 ~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK