如何用canvas绘制我们任何想要任意图案的组件,这篇文章用自定义一个五角星组件来说明
自定义组件,建议用官方的CustomPaint
来实现,先新建一个pantagram的类
class Pentagram extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.black;
final rect = Offset.zero & size;
canvas.drawRect(rect, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
它是继承CustomPainter
这个类,有两个方法,shouldRepaint
先写死返回false,paint
方法就是真实绘制的地方,这里先绘制一个黑色的矩形
然后在页面引用这个类
return Scaffold(
appBar: AppBar(
title: Text('Demo Page'),
),
body: Center(
child: CustomPaint(
painter: Pentagram(),
),
),
);
运行后,发现页面是空白的,查看了下它的源码,发现是size的问题
const CustomPaint({
Key? key,
this.painter,
this.foregroundPainter,
this.size = Size.zero, //尺寸默认是0
size默认的尺寸是0,重新设置size后,显示正常了(个人觉得,默认的size用size.infinite更合理,充满父布局)
body: Container(
child: CustomPaint(
size: Size(150, 150),
painter: Pentagram(),
),
),
使用自定义组件,绘制出了一块黑色的矩形了
由于五角星属于特殊的图案,需要手动设置path,为了验证path的可行性,先绘制一个菱形来做验证
void paint(Canvas canvas, Size size) {
//把画笔设置成stroke模式
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 2;
var rect = Offset.zero & size;
var center = rect.center;
// 连接好菱形的四个顶点
final path = Path();
path.moveTo(center.dx, 0);
path.lineTo(rect.right, center.dy);
path.lineTo(center.dx, rect.bottom);
path.lineTo(0, center.dy);
path.close(); //最后用close的方式把path封闭起来
// 调用canvas绘制path
canvas.drawPath(path, paint);
}
先把画笔设置成stroke模式,然后path连接好菱形的四个顶点,最后直接绘制即可,效果如下
菱形绘制好了,说明path的方案可行
既然path方案可行,接来下就是确定五角星的五个顶点,用path连接起来,代码如下
Path getPentagramPath(double radius) {
var initDegreen = 180;
// 连接五角星的五个顶点
final path = Path();
var posOne = getOffsetPosition(initDegreen, radius);
path.moveTo(posOne.dx, posOne.dy);
// 360/5,每个是72度
var posTwo = getOffsetPosition(72 + initDegreen, radius);
path.lineTo(posTwo.dx, posTwo.dy);
var posThree = getOffsetPosition(144 + initDegreen, radius);
path.lineTo(posThree.dx, posThree.dy);
var posfour = getOffsetPosition(216 + initDegreen, radius);
path.lineTo(posfour.dx, posfour.dy);
var posFive = getOffsetPosition(288 + initDegreen, radius);
path.lineTo(posFive.dx, posFive.dy);
//最后用close的方式把path封闭起来
path.close();
return path;
}
Offset getOffsetPosition(int degreen, double radius) {
//角度转成弧度
var radian = degreen * pi / 180;
var dx = sin(radian) * radius;
var dy = cos(radian) * radius;
return Offset(dx + radius, dy + radius);
}
看下效果图
结果变成五边形了,不是我们要的五角星,需要把path的线条连接方式修改下
var initDegreen = 180;
// 连接五角星的五个顶点,360/5,每个是72度
final path = Path();
var posOne = getOffsetPosition(initDegreen, radius);
var posTwo = getOffsetPosition(72 + initDegreen, radius);
var posThree = getOffsetPosition(144 + initDegreen, radius);
var posfour = getOffsetPosition(216 + initDegreen, radius);
var posFive = getOffsetPosition(288 + initDegreen, radius);
path.moveTo(posOne.dx, posOne.dy);
path.lineTo(posfour.dx, posfour.dy);
path.lineTo(posTwo.dx, posTwo.dy);
path.lineTo(posFive.dx, posFive.dy);
path.lineTo(posThree.dx, posThree.dy);
//最后用close的方式把path封闭起来
path.close();
这次终于看到我们需要的效果了
这个五角星,如果要用填充的方式展示,只需要把画笔style设置成fill即可
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.fill;
效果如下
对于自定义组件,想要一些参数,是可以外部控制的,比如五角星的颜色,可以用外部传进来
class Pentagram extends CustomPainter {
Pentagram(this.color);
//画笔的颜色
final Color color;
先定义好需要外部设置的参数,外部在使用的地方,把颜色设置进来
child: CustomPaint(
size: Size(150, 150),
painter: Pentagram(Colors.black),
),
包括动态调整颜色和其他熟悉,也都可以实现了