flutter路径的用法(下)
本节目标:
[1]. 了解路径的 [封闭] [重置] [偏移] 操作。
[2]. 了解路径的 [矩形边距] 和 [检测点是否在路径中]。
[3]. 了解路径的 [路径变换] 和 [路径联合]。
[4]. 了解路径测量的用法和作用。
一、路径操作
路径的操作是路径使用的重要一环,很多路径的
特效
和复杂路径的拼合
都会使用它们。
1.close、reset、shift
path#close
:用于将路径尾点和起点,进行路径封闭
。path#reset
:用于将路径进行重置,清除路径内容
。path#shift
:指定点Offset将路径进行平移,且返回一条新的路径
。
---->[p06_path/12_close_reset_shift/paper.dart]----
Path path = Path();
Paint paint = Paint()
..color = Colors.purpleAccent
..strokeWidth = 2
..style = PaintingStyle.stroke;
path
..lineTo(100, 100)
..relativeLineTo(0, -50)
..close();
canvas.drawPath(path, paint);
canvas.drawPath(path.shift(Offset(100, 0)), paint);
canvas.drawPath(path.shift(Offset(200, 0)), paint);
2. contains
和getBounds
Paint#contains
可以判断点Offset在不在路径之内(如下图紫色区域
),
这是个非常好用的方法,可以根据这个方法做一些触点判断
或简单的碰撞检测
。Paint#getBounds
可以获取当前路径所在的矩形区域,(如下橙色区域
)
---->[p06_path/13_getBounds_contains/paper.dart]----
Path path = Path();
Paint paint = Paint()
..color = Colors.purple
..style = PaintingStyle.fill;
path
..relativeMoveTo(0, 0)
..relativeLineTo(-30, 120)
..relativeLineTo(30, -30)
..relativeLineTo(30, 30)
..close();
canvas.drawPath(path, paint);
print(path.contains(Offset(20, 20)));
print(path.contains(Offset(0, 20)));
Rect bounds = path.getBounds();
canvas.drawRect(
bounds,
Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 1);
3.Path#transform
: 路径变换
对于对称性图案,当已经有一部分单体路径,可以根据一个
4*4的矩阵
对路径进行变换。
可以使用Matrix4对象
进行辅助生成矩阵。能很方便进行旋转、平移、缩放、斜切
等变换效果。
---->[p06_path/14_getBounds_contains/paper.dart]----
Path path = Path();
Paint paint = Paint()
..color = Colors.purple
..style = PaintingStyle.fill;
path
..relativeMoveTo(0, 0)
..relativeLineTo(-30, 120)
..relativeLineTo(30, -30)
..relativeLineTo( 30,30)
..close();
for(int i = 0; i < 8; i++){
canvas.drawPath(path.transform(Matrix4.rotationZ(i*pi/4).storage), paint);
}
4. combine
: 路径联合
Patn#combine
用于结合两个路径,并生成新路径
,可用于生成复杂的路径。
一共有如下五种联合方式,效果如下图:
---->[p06_path/15_combine/paper.dart]----
Path path = Path();
Paint paint = Paint();
paint
..color = Colors.purple
..style = PaintingStyle.fill;
path
..relativeMoveTo(0, 0)
..relativeLineTo(-30, 120)
..relativeLineTo(30, -30)
..relativeLineTo( 30,30)
..close();
var pathOval =Path()..addOval(Rect.fromCenter(center: Offset(0, 0),width: 60,height: 60));
canvas.drawPath(Path.combine(PathOperation.difference, path, pathOval), paint);
canvas.translate(120, 0);
canvas.drawPath(Path.combine(PathOperation.intersect, path, pathOval), paint);
canvas.translate(120, 0);
canvas.drawPath(Path.combine(PathOperation.union, path, pathOval), paint);
canvas.translate(-120*3.0, 0);
canvas.drawPath(Path.combine(PathOperation.reverseDifference, path, pathOval), paint);
canvas.translate(-120, 0);
canvas.drawPath(Path.combine(PathOperation.xor, path, pathOval), paint);
二、路径测量的使用
computeMetrics
是路径中一个非常实用的操作,可以根据这个方法获取很多有价值的信息,比如路径上某点在路径的位置
、角度
,路径长度等。通过这些和动画结合
,可以做出环路经运动
、路径绘制动画
等效果。
1.认识Path#computeMetrics
通过
path.computeMetrics()
,可以获取一个可迭代PathMetrics
类对象 它迭代出的是PathMetric
对象,也就是每个路径的测量信息。也就是说通过path.computeMetrics()
你获得是一组路径的测量信息。注意:如下path.addOval
之后,PathMetrics
类对象中元素变为两个。
---->[p06_path/16_computeMetrics/paper.dart]----
Path path = Path();
path
..relativeMoveTo(0, 0)
..relativeLineTo(-30, 120)
..relativeLineTo(30, -30)
..relativeLineTo(30, 30)
..close();
path.addOval(Rect.fromCenter(center: Offset.zero,width: 50,height: 50));
通过
PathMetrics
对象可以获得路径长度 length
、路径索引 contourIndex
及isClosed
路径是否闭合isClosed
。
PathMetrics pms = path.computeMetrics();
Tangent t;
pms.forEach((pm) {
print("---length:-${pm.length}----contourIndex:-${pm.contourIndex}----contourIndex:-${pm.isClosed}----");
});
---->[打印日志]----
---length:-332.2391357421875----contourIndex:-0----contourIndex:-true---------
---length:-156.0674285888672----contourIndex:-1----contourIndex:-true---------
2 .路径测量获取路径某位置信息
比如我想要在路径一半的地方绘制一个小球,如果通过自己计算的话,非常困难。
幸运的是通过路径测量,实现起来就非常方便。甚至还能得到改点的角度
、速度
信息。
下面通过pm.length * 0.5
表示在路径长度50%
的点的信息。
Paint paint = Paint()
..color = Colors.purple
..strokeWidth = 1
..style = PaintingStyle.stroke;
Path path = Path();
path
..relativeMoveTo(0, 0)
..relativeLineTo(-30, 120)
..relativeLineTo(30, -30)
..relativeLineTo(30, 30)
..close();
path.addOval(Rect.fromCenter(center: Offset.zero, width: 50, height: 50));
PathMetrics pms = path.computeMetrics();
pms.forEach((pm) {
Tangent tangent = pm.getTangentForOffset(pm.length * 0.5);
print(
"---position:-${tangent.position}----angle:-${tangent.angle}----vector:-${tangent.vector}----");
canvas.drawCircle(
tangent.position, 5, Paint()..color = Colors.deepOrange);
});
canvas.drawPath(path, paint);
---->[打印日志]----
---position:-Offset(0.0, 90.0)----angle:-0.7853981633974483----vector:-Offset(0.7, -0.7)----
---position:-Offset(-25.0, 0.0)----angle:-1.5707963267948966----vector:-Offset(0.0, -1.0)----
3. 路径测量和动画结合
虽然动画在后面章节才讲述,这样可以先看一下。
路径测量和动画
搭档起来,才能更看出它的价值,下面将实现小球沿路径动画,使用动画控制器
让数字在 3 秒内从 0 运动到 1,达到动画效果。
---->[p06_path/17_computeMetrics_anim/paper.dart]----
class Paper extends StatefulWidget {
@override
_PaperState createState() => _PaperState();
}
class _PaperState extends State<Paper> with SingleTickerProviderStateMixin {
AnimationController _ctrl;
@override
void initState() {
super.initState();
_ctrl = AnimationController(duration: Duration(seconds: 3), vsync: this)
..forward();
}
@override
void dispose() {
_ctrl.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
painter: PaperPainter(progress: _ctrl),
),
);
}
}
class PaperPainter extends CustomPainter {
final Animation<double> progress;
PaperPainter({this.progress}) : super(repaint: progress);
@override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
Paint paint = Paint()
..color = Colors.purple
..strokeWidth = 1
..style = PaintingStyle.stroke;
Path path = Path();
path
..relativeMoveTo(0, 0)
..relativeLineTo(-30, 120)
..relativeLineTo(30, -30)
..relativeLineTo(30, 30)
..close();
path.addOval(Rect.fromCenter(center: Offset.zero, width: 50, height: 50));
PathMetrics pms = path.computeMetrics();
pms.forEach((pm) {
Tangent tangent = pm.getTangentForOffset(pm.length * progress.value);
canvas.drawCircle(
tangent.position, 5, Paint()..color = Colors.deepOrange);
});
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(PaperPainter oldDelegate) => oldDelegate.progress!=progress;
}
到此为止讲述完了Path
相关的所有Api,
不过知道怎么用是一切的开始
,绘制的能力和灵感并非那么轻易可得,需要你不断的使用和练习。在后面的章节中将向你展示光怪陆离
的绘制世界,为什么世界如此多彩,那当然是因为它有颜色啦 ~,下一章将带你重新认识,这不起眼却美化万物的颜色
。