目标:绘制小猪佩奇
Flutter在自定义绘制上同Android类似,提供给你canvas和paint,剩下的就海阔凭鱼跃,天高任鸟飞了,给了你工具,能造出什么来就凭个人本事了。
这里为了学习与巩固flutter中的绘制相关API,于是使用其参照上图绘制一张小猪佩奇。
这里为了更有扩展性,让画布变大时绘制的内容不会变形,全图使用宽高比例绘制。
这张图相对比较简单,曲线部分全部使用贝塞尔曲线绘制,由于canvas 的API中最高支持3阶贝塞尔曲线,本图绘制正好也不需要使用更高阶的贝塞尔曲线。
先上一张最终效果器:
首先我们要自定义一个绘制佩奇的控件PeppaPaint,继承自CustomPainter,需要我们实现paint(Canvas canvas, Size size)
和bool shouldRepaint(CustomPainter oldDelegate)
的方法:
class PeppaPaint extends CustomPainter{
}
Paint方法中即为我们真正绘制内容的区域,shouldRepaint则控制是否需要重新绘制界面,由于flutter中所有视图内容都是和数据状态绑定的,所有我们可以判断数据状态是否有改变来控制是否重绘,以减少性能损耗。
首先我们做一下准备工作,从size拿到画布的宽高,由于绘制过程中难免会有画笔粗细,圆的半径大小等相对绝对的数值,所有我们这边记一个标准,相当于我们在这个标准的尺寸下用多粗的画笔,多长的半径,在画板尺寸放大或者缩小时,对应的画笔粗细,半径尺寸也会有对应比例的变化。另外需要注意一下,下面绘制的所有基于宽高绘制的定位点都是本子自己大致估算而来大家不必纠结。这里我们以宽度为360作为标准尺寸,则有以下代码:
@override
void paint(Canvas canvas, Size size) {
var width = size.width;
var height = size.height;
var k = width / 360; //对应标准尺寸下的比例尺
……
}
接下来开始真正会内容绘制,首先我们需要绘制一个背景:
var paint = Paint()
..isAntiAlias = true
..style = PaintingStyle.fill //填充
..color = Color(0x77cdb175); //背景为纸黄色
canvas.drawRect(Offset.zero & size, paint);
接下来我们开始画鼻子,鼻子为一个圆孔和一个椭圆:
paint
..style = PaintingStyle.stroke //线
..color = Colors.pink[200]
..strokeWidth = 3.0 * k;
canvas.rotate(-0.25);
//画鼻子
canvas.drawOval(
Rect.fromLTRB(width * 0.7, height * 0.2, width * 0.8, height * 0.30),
paint);
paint.style = PaintingStyle.fill; //填充
canvas.drawCircle(Offset(width * 0.725, height * 0.25), 5 * k, paint);
canvas.drawCircle(Offset(width * 0.775, height * 0.25), 5 * k, paint);

大家可能注意到上面有一句canvas.rotate(-0.25)
,这是因为鼻子的椭圆是有一定角度的,我们需要将画布旋转一定角度后作画。由于脸部轮廓和鼻子是连着的,所以我们不着急把画布旋转回来,否则旋转回来之后就找不到鼻子与脸部轮廓链接的点了。我们接着画脸部,脸部是由两个贝塞尔曲线构成,一条三阶