前言
本文实现 Generative Artistry 教程的第 4 篇图形 Triangular mesh 效果如下。
没按顺序来,因为第二篇 Joy Division 我还没实现 😝
创建画布
首先创建一个用于绘制的画布,然后实现 TriangularMeshPainter 的 paint 方法。
class TriangularMesh extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: TriangularMeshPainter(),
);
}
}
class TriangularMeshPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {}
@override
bool shouldRepaint(TriangularMeshPainter oldDelegate) => false;
}
绘制端点
声明一个 gap 变量等比分割画布,在纵轴,横轴方向每隔 gap 长度使用 canvas.drawCircle 方法绘制圆点。
class TriangularMeshPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// 声明 line 和 lines 变量存储点和线
List line;
List lines = [];
double gap = size.width / 8;
// 设置点的绘制属性
Paint paint = Paint()
..color = Colors.black
..isAntiAlias = true;
// 纵轴方向
for (double y = gap / 2; y <= size.height; y += gap) {
line = [];
// 横轴方向
for (double x = gap / 4; x <= size.width; x += gap) {
line.add({"x": x, "y": y});
// 绘制圆,参数分别是圆点位置,圆的半径,绘制属性
canvas.drawCircle(Offset(x, y), 1, paint);
}
lines.add(line);
}
}
}
创建一个 Point 类,表示一个绘制的圆点。
class Point {
double x;
double y;
}
为了使点之间形成三角形,使用一个 odd 变量,在每次添加圆点的时候改变点的横轴值,形成交错效果。
@override
void paint(Canvas canvas, Size size) {
bool odd = false;
List<Point> line;
List<List<Point>> lines = [];
double gap = size.width / 8;
Paint paint = Paint()
..color = Colors.black
..isAntiAlias = true;
for (double y = gap / 2; y <= size.height; y += gap) {
// 每次添加圆点之前对 odd 取反
odd = !odd;
line = [];
for (double x = gap / 4; x <= size.width; x += gap) {
Point point = Point();
// 赋值圆点横轴位置时根据 odd 变量判断是否需要增加距离
point.x = x + (odd ? gap / 2 : 0);
point.y = y;
// 将点添加到 line 数组
line.add(point);
canvas.drawCircle(Offset(point.x, point.y), 1, paint);
}
lines.add(line);
}
}
绘制网格
接下来需要建立 3 个点之间的关系,从而绘制三角形。首先创建一个接收三角形三个坐标,并连接绘制它们的函数。
void _drawTriangle(Canvas canvas, Point pointA, Point pointB, Point pointC) {
// 使用一个路径链接 3 点并绘制这条路径
Path path = Path();
Paint line = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeJoin = StrokeJoin.bevel
..isAntiAlias = true;
path.moveTo(pointA.x, pointA.y);
path.lineTo(pointB.x, pointB.y);
path.lineTo(pointC.x, pointC.y);
path.lineTo(pointA.x, pointA.y);
// 参数分别是路径,路径的绘制属性
canvas.drawPath(path, line);
}
然后遍历所有存储的线,并组合相邻线的点以形成三角形。
@override
void paint(Canvas canvas, Size size) {
///...
List dotLine;
odd = true;
for (int y = 0; y < lines.length - 1; y++) {
odd = !odd;
dotLine = [];
for (var i = 0; i < lines[y].length; i++) {
dotLine.add(odd ? lines[y][i] : lines[y + 1][i]);
dotLine.add(odd ? lines[y + 1][i] : lines[y][i]);
}
for (int i = 0; i < dotLine.length - 2; i++) {
_drawTriangle(canvas, dotLine[i], dotLine[i + 1], dotLine[i + 2]);
}
}
}
创建圆点时加入随机量,形成不规则的三角形。
for (double x = gap / 4; x <= size.width; x += gap) {
Point point = Point();
double random = (Random().nextDouble() * .8 - .4) * gap;
point.x = x + random + (odd ? gap / 2 : 0);
point.y = y + (Random().nextDouble() * .8 - .4) * gap;
line.add(point);
}
添加颜色
最后在绘制三角形函数那里加上一些颜色,只需要设置路径的 color 和 style 绘制属性即可。
void _drawTriangle(Canvas canvas, Point pointA, Point pointB, Point pointC) {
Path path = Path();
Paint fill = Paint()
// 添加颜色,可以尝试设置不同的颜色
..color = Colors.black.withOpacity(Random().nextDouble() * .9)
// ..color = colors[Random().nextInt(colors.length)].withOpacity(.8)
// 将路径的绘制样式设置为 fill
..style = PaintingStyle.fill
..strokeJoin = StrokeJoin.bevel
..isAntiAlias = true;
Paint line = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeJoin = StrokeJoin.bevel
..isAntiAlias = true;
path.moveTo(pointA.x, pointA.y);
path.lineTo(pointB.x, pointB.y);
path.lineTo(pointC.x, pointC.y);
path.lineTo(pointA.x, pointA.y);
canvas.drawPath(path, fill);
// 新增一个路径绘制
canvas.drawPath(path, line);
}
大功告成!👏