一、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 Listdata; 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 ValueChangedonChanged; 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 { Listbullets = []; 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, ), ); } }