您的位置:

Flutter Canvas的多方面探究

一、Canvas简介

Canvas是Flutter中的绘图类,它提供了一系列绘制2D图形的方法和工具。例如,绘制直线,矩形,圆形等形状。在使用Canvas之前,必须先创建一个CustomPaint类,它允许在其内部绘制自定义的图形。

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: MyPainter(),
    );
  }

MyPainter是自定义Painter的子类,它继承了PaintingContext抽象类,可以在自身的Canvas上绘制图形元素。

二、Canvas的属性和方法

Canvas提供了一系列用于创建和修改图形的方法和属性。

1. 绘制形状

Canvas的常用图形方法包括drawLine、drawRect、drawCircle、drawArc等。例如,以下代码演示了如何使用Canvas绘制一个矩形:

  @override
  void paint(Canvas canvas, Size size) {
    Rect rect = Rect.fromLTWH(0, 0, size.width, size.height);
    Paint paint = Paint();
    paint.color = Colors.red;
    canvas.drawRect(rect, paint);
  }

2. 设置画笔属性

Canvas的Paint类包含一系列用于设置和修改画笔属性的方法和属性,例如color、strokeWidth、strokeCap、strokeJoin等。

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint();
    paint.color = Colors.black;
    paint.strokeWidth = 5;
    paint.strokeCap = StrokeCap.round;
    paint.strokeJoin = StrokeJoin.round;
    canvas.drawLine(Offset.zero, Offset(size.width, size.height), paint);
  }

3. 设置绘制文字的属性

在Canvas中,TextPainter类用于绘制文本。可以使用TextStyle类设置文字的样式,例如字体颜色、字体大小、字体样式等。以下代码演示了如何在Canvas中绘制文本:

  @override
  void paint(Canvas canvas, Size size) {
    TextPainter textPainter = TextPainter(
        text: TextSpan(text: "Hello World", style: TextStyle(color: Colors.black, fontSize: 20)),
        textDirection: TextDirection.ltr,
      );

    textPainter.layout();
    textPainter.paint(canvas, Offset(50, 50));
  }

4. Matrix变换

使用Matrix可以进行Canvas上的几何变换。常用的变换包括平移,旋转,缩放。

  @override
  void paint(Canvas canvas, Size size) {
    Rect rect = Rect.fromLTWH(0, 0, 100, 100);
    Paint paint = Paint();
    canvas.save();
    canvas.translate(size.width / 2, size.height / 2); // 平移
    canvas.rotate(pi / 4); // 旋转
    canvas.scale(2); // 缩放
    canvas.drawRect(rect, paint);
    canvas.restore();
  }

三、Canvas的应用

Canvas可以用于很多应用场景,例如绘制图表,制作自定义的UI组件,创建游戏等。

1. 绘制图表

Canvas可以绘制各种图表,例如柱状图、饼状图、折线图等。

以下代码演示了如何使用Canvas绘制一个柱状图:

class BarChartPainter extends CustomPainter {
  final List data;

  BarChartPainter(this.data);

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 2;

    double barWidth = size.width / data.length;

    for (int i = 0; i < data.length; i++) {
      double xPos = i * barWidth;
      double barHeight = data[i] / 100 * size.height;
      Rect rect = Rect.fromLTRB(xPos, size.height - barHeight, xPos + barWidth, size.height);
      canvas.drawRect(rect, paint);
    }
  }

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

class BarChart extends StatelessWidget {
  final List
    data;

  BarChart(this.data);

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: BarChartPainter(data),
      size: Size(double.infinity, 200),
    );
  }
}

// Example usage: 
BarChart([30, 50, 80, 20]);

   
  

2. 制作自定义的UI组件

使用Canvas,我们可以为我们的应用程序创建独特的自定义UI组件。例如,我们可以使用Canvas创建带有自定义绘图的开关按钮。

class CustomSwitchPainter extends CustomPainter {
  final bool value;

  CustomSwitchPainter(this.value);

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = value ? Colors.green : Colors.grey
      ..style = PaintingStyle.fill;

    canvas.drawRRect(
        RRect.fromRectAndRadius(Rect.fromLTWH(0, 0, size.width, size.height), Radius.circular(size.height / 2)),
        paint);

    double switchWidth = size.width / 2;
    double offset = value ? size.width - switchWidth : 0;

    Paint switchPaint = Paint()
      ..color = Colors.white
      ..style = PaintingStyle.fill;

    canvas.drawCircle(
      Offset(offset, size.height / 2),
      size.height / 2 - 2,
      switchPaint,
    );
  }

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

class CustomSwitch extends StatefulWidget {
  final bool value;
  final ValueChanged onChanged;

  CustomSwitch({@required this.value, @required this.onChanged});

  @override
  _CustomSwitchState createState() => _CustomSwitchState(value);
}

class _CustomSwitchState extends State
    with SingleTickerProviderStateMixin {
  bool value;
  AnimationController _controller;
  Animation
     _animation;

  _CustomSwitchState(this.value);

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(duration: Duration(milliseconds: 200), vsync: this);
    _animation = Tween
     (begin: 0, end: 1).animate(_controller);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        value = !value;
        widget.onChanged(value);
        if (value) {
          _controller.forward();
        } else {
          _controller.reverse();
        }
      },
      child: AnimatedBuilder(
        animation: _animation,
        builder: (context, child) {
          return Transform.translate(
            offset: Offset(20 * _animation.value - 20, 0),
            child: CustomPaint(
              painter: CustomSwitchPainter(value),
              size: Size(70, 40),
            ),
          );
        },
      ),
    );
  }
}

     
    
   
  

3. 创建游戏

Canvas可以用于创建跨平台的2D游戏。例如,我们可以使用Canvas创建一个简单的飞行射击游戏。

class Bullet {
  Rect rect;

  Bullet(this.rect);
}

class Enemy {
  Rect rect;
  double speed;

  Enemy(this.rect, this.speed);
}

class GamePainter extends CustomPainter {
  List bullets = [];
  List
    enemies = [];
  int score = 0;

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.fill;

    for (Bullet bullet in bullets) {
      canvas.drawRect(bullet.rect, paint);
      bullet.rect = bullet.rect.translate(0, -5);
    }

    paint.color = Colors.green;
    for (Enemy enemy in enemies) {
      canvas.drawRect(enemy.rect, paint);
      enemy.rect = enemy.rect.translate(0, enemy.speed);
    }

    List
     bulletsToDelete = [];
    List
      enemiesToDelete = [];

    for (Bullet bullet in bullets) {
      if (bullet.rect.top < 0) {
        bulletsToDelete.add(bullet);
        continue;
      }
      for (Enemy enemy in enemies) {
        if (bullet.rect.overlaps(enemy.rect)) {
          bulletsToDelete.add(bullet);
          enemiesToDelete.add(enemy);
          score++;
        }
      }
    }

    for (Bullet bullet in bulletsToDelete) {
      bullets.remove(bullet);
    }

    for (Enemy enemy in enemiesToDelete) {
      enemies.remove(enemy);
    }

    if (Random().nextInt(100) < 5) {
      enemies.add(
        Enemy(
          Rect.fromLTWH(Random().nextInt(size.width.toInt()), -20, 20, 20),
          Random().nextInt(5) + 2.toDouble(),
        ),
      );
    }
  }

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

class Game extends StatefulWidget {
  @override
  _GameState createState() => _GameState();
}

class _GameState extends State
       {
  GlobalKey _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanUpdate: (details) {
        RenderBox renderBox = _key.currentContext.findRenderObject();
        Offset localPosition = renderBox.globalToLocal(details.globalPosition);

        List
       
        bulletsToAdd = []; bulletsToAdd.add(Bullet(Rect.fromLTWH(localPosition.dx - 3, localPosition.dy - 20, 6, 20))); setState(() { bulletsToAdd.forEach((bullet) => bullets.add(bullet)); }); }, child: CustomPaint( painter: GamePainter(), key: _key, ), ); } }