一、前期准备
先来看一段代码。
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: PathExample(),
),
);
class PathExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: PathPainter(),
);
}
}
class PathPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
// TODO: do operations here
path.close();
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
复制代码
在上面代码里,我们主页面是 StatelessWidget 类型的组件,在 build 方法里面返回的是 CustomPaint 这个 Widget,而 CustomPaint 需要使用的 painter 是我们自定义的 PathPainter 。CustomPaint 这个组件为我们提供了一个画布 (canvas),我们可以使用我们自定义的 CustomPainter 在这个画布上把 paint() 这个方法里面指定的内容绘制上去。
对于绘制有关的选项,我们可以通过 Paint 组件来进行设置,包括颜色、样式、画笔粗细等。
Paint paint = Paint()
..color = Colors.blueAccent //画笔颜色
..strokeCap = StrokeCap.round //画笔笔触类型
..isAntiAlias = true //是否启动抗锯齿
..blendMode = BlendMode.exclusion //颜色混合模式
..style = PaintingStyle.fill //绘画风格,默认为填充
..colorFilter = ColorFilter.mode(Colors.blueAccent,
BlendMode.exclusion) //颜色渲染模式
..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果
..filterQuality = FilterQuality.high //颜色渲染模式的质量
..strokeWidth = 5.0; //画笔的宽度
复制代码
接下来我们就可以定义一个 Path 对象来决定画什么了。 Path 这个对象其实是一些列需要绘制的元素的集合,这些元素在绘制的时候都是根据一个起始点来绘制的(默认的 Path 的起始点是 (0,0) )。
最后需要使用 canvas 的 drawPath 方法来绘制 path ,这个方法需要两个参数一个是 path 另一个是 paint 。
使用 Path 进行绘图的基本流程差不多就是这些。对于手机上的坐标系统,如下所示,左上角为坐标原点。
二、moveTo
moveTo 方法就把绘制的起点移动到指定的位置。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
// 将起点移动到屏幕中心
path.moveTo(size.width / 2, size.height / 2);
canvas.drawPath(path, paint);
}
复制代码
三、lineTo
lineTo 方法就是从起点绘制一条直线到 lineTo 里面指定的一个点。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
// 从左上角起点到右下角终点
path.lineTo(size.width, size.height);
canvas.drawPath(path, paint);
}
复制代码
四、quadraticBezierTo
quadraticBezierTo 是绘制二阶贝塞尔曲线的。
从上图可以看到,绘制贝塞尔曲线需要三个点,一个起点,一个控制点,一个终点。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
path.moveTo(0, size.height / 2);
path.quadraticBezierTo(size.width / 2, size.height, size.width, size.height / 2);
canvas.drawPath(path, paint);
}
复制代码
五、cubicTo
cubicTo 是绘制三阶贝塞尔曲线的。
三阶贝塞尔需要两个控制点。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
path.cubicTo(size.width / 4, 3 * size.height / 4, 3 * size.width / 4, size.height / 4, size.width, size.height);
canvas.drawPath(path, paint);
}
复制代码
六、conicTo
conicTo 方法也是绘制二次曲线的,和 quadraticBeizerTo 方法类似,但是这个方法主要是受到 weight 参数的控制。 当 weight 大于 1 时,绘制的是双曲线,等于 1 时,绘制的是抛物线,小于1 时,绘制的是椭圆。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
path.conicTo(size.width / 4, 3 * size.height / 4, size.width, size.height, 20);
canvas.drawPath(path, paint);
}
复制代码
七、arcTo
arcTo 方法是绘制弧线的,方法原型如下:
void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo)
复制代码
需要四个参数:
- rect: 圆弧所在矩形
- startAngle : 开始弧度
- sweepAngle : 需要绘制的弧度大小
- forceMoveTo : 如果“forceMoveTo”参数为false,则添加一条直线段和一条弧段。 如果“forceMoveTo”参数为true,则启动一个新的子路径,其中包含一个弧段。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
// Method to convert degree to radians
num degToRad(num deg) => deg * (Math.pi / 180.0);
Path path = Path();
path.arcTo(Rect.fromLTWH(size.width / 2, size.height / 2, size.width / 4, size.height / 4), degToRad(0), degToRad(90), true);
canvas.drawPath(path, paint);
}
复制代码
八、addRect
绘制矩形。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
// Adds a rectangle
path.addRect(Rect.fromLTWH(size.width / 2, size.height / 2, size.width / 4, size.height / 4));
canvas.drawPath(path, paint);
}
复制代码
九、addOval
绘制椭圆。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
// Adds an oval
path.addOval(Rect.fromLTWH(size.width / 2, size.height / 2, size.width / 4, size.height / 4));
canvas.drawPath(path, paint);
}
复制代码
十、addArc
绘制弧线类似与 arcTo
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
// Method to convert degree to radians
num degToRad(num deg) => deg * (Math.pi / 180.0);
Path path = Path();
// Adds a quarter arc
path.addArc(Rect.fromLTWH(0, 0, size.width, size.height), degToRad(180), degToRad(90));
canvas.drawPath(path, paint);
}
复制代码
十一、addPolygon
绘制多边形。可以指定多边形的顶点,并且最后一个参数是 ture 时,最后一个点和第一个点会连接,多边形闭合, false 时,不会闭合。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
// Adds a polygon from the starting point to quarter point of the screen and lastly
// it will be in the bottom middle. Close method will draw a line between start and end.
path.addPolygon([
Offset.zero,
Offset(size.width / 4, size.height / 4),
Offset(size.width / 2, size.height)
], false);
canvas.drawPath(path, paint);
}
复制代码
- false
- true
十二、addRRect
绘制圆角矩形,圆角弧度由最后一个参数控制。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Path path = Path();
path.addRRect(
RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2, size.height / 2, size.width / 4, size.height / 4), Radius.circular(16))
);
canvas.drawPath(path, paint);
}
复制代码
十三、path 实践绘制进度条
class CircleProgressBarPainter extends CustomPainter {
//背景
Paint _paintBackground;
//前景
Paint _paintForeground;
var currentValue;
CircleProgressBarPainter(this.currentValue) {
_paintBackground = Paint()
..color = Colors.blue
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 10.0
..isAntiAlias = true;
_paintForeground = Paint()
..color = Colors.red
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 10.0
..isAntiAlias = true;
}
@override
void paint(Canvas canvas, Size size) {
//画背景
canvas.drawCircle(Offset(size.width / 2, size.height / 2), size.width / 2,
_paintBackground);
Rect rect = Rect.fromCircle(
center: Offset(size.width / 2, size.height / 2),
radius: size.width / 2,
);
//画弧形进度
canvas.drawArc(rect, 0.0, currentValue * Math.pi / 180, false, _paintForeground);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
复制代码
使用
return Container(
width: width,
height: height,
padding: EdgeInsets.all(20),
child: CustomPaint(
child: Center(
child: Text(
(progressAnimation.value / 3.6).round().toString(),
style: TextStyle(fontSize: 24,color: Colors.blue),
),
),
painter: CircleProgressBarPainter(progressAnimation.value)
),
);
复制代码
效果: