カスタマイズ描画について
※本記事は下記のZenn本にまとめました。
キャンバス変換
キャンバス変換関数は下記5つある。
void skew(double sx, double sy)
void rotate(double radians)
void scale(double sx, [double sy])
void translate(double dx, double dy)
void transform(Float64List matrix4)
- translate(double dx, double dy)
並行移動 - scale(double sx, [double sy])
スケール - rotate(double radians)
回転する - skew(double sx, double sy)
斜め切り替え - transform(Float64List matrix4)
矩形移動
translate
translateはキャンパスのの中心を移動する関数です。
下記のソースで並行移動を確認しましょう。
// キャンバスの開始点を画面の中央に移動します
canvas.translate(size.width / 2, size.height / 2);
上記の中心点移動あるとないの違いは下記の図で一目瞭然です。黄色枠は基準です。
ソースコード:
class Demo extends StatefulWidget {
@override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
elevation: 0,
title: const Text("Demo"),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
color: Colors.yellow,
height: 100,
width: 100,
child: CustomPaint(
painter: CirclePainter(false),
),
),
Container(
color: Colors.yellow,
height: 100,
width: 100,
child: CustomPaint(
painter: CirclePainter(true),
),
),
],
),
),
);
}
}
class CirclePainter extends CustomPainter {
final bool show;
CirclePainter(this.show);
@override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..style = PaintingStyle.fill
..color = Colors.blue;
if (show) {
// キャンバスの開始点を画面の中央に移動します
canvas.translate(size.width / 2, size.height / 2);
}
canvas.drawCircle(const Offset(0, 0), 50, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
並行移動を用いてグリッドの描画もできます。
考え方は下記です。
- 描画スタートはキャンパスの中心へ移動
- 線を描く
- 中心をX軸方向へ並行移動し,横線を描く
- それを繰り返して処理する
- 中心点をリセットする
- 線を描く
- 中心をY軸方向へ並行移動し,縦線を描く
- それを繰り返して処理する
ソースコード:
// グリッドの幅
final double space = 20;
Paint _gridPint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..color = color;
void _drawGridRight(Canvas canvas, Size size) {
// キャンパスの保存
canvas.save();
// 横線を描く
for (int i = 0; i < size.width / 2 / space; i++) {
canvas.drawLine(Offset(0, 0), Offset(0, size.height / 2), _gridPint);
canvas.translate(space, 0);
}
// キャンパスリセット(原点リセット)
canvas.restore();
canvas.save();
// 縦線を描く
for (int i = 0; i < size.height / 2 / space; i++) {
canvas.drawLine(Offset(0, 0), Offset(size.width / 2, 0), _gridPint);
canvas.translate(0, space);
}
canvas.restore();
}
スケール
スケールを用いて鏡像を作ることは可能です。
では、スケールを用いて上記のグリッドを完成させましょう。
void _drawGrid(Canvas canvas, Size size) {
// 右下のグリッド
_drawGridRight(canvas, size);
canvas.save();
// X軸の镜像
canvas.scale(1, -1);
_drawGridRight(canvas, size);
// リセット
canvas.restore();
canvas.save();
// Y軸の镜像
canvas.scale(-1, 1);
_drawGridRight(canvas, size);
canvas.restore();
canvas.save();
// 原点の镜像
canvas.scale(-1, -1);
_drawGridRight(canvas, size);
canvas.restore();
}
描画効果は下記です。
X軸の鏡像 | Y軸の鏡像 | 原点の鏡像 |
---|---|---|
回転する
キャンパスを回転することで、ペンの位置を移動しなくても、円を描くことができます。
void _drawDot(Canvas canvas, Paint paint) {
const int count = 12;
canvas.save();
for (int i = 0; i < count; i++) {
var step = 2 * pi / count;
canvas.drawLine(
const Offset(80, 0),
const Offset(100, 0),
paint,
);
// キャンパスを回転する
canvas.rotate(step);
}
canvas.restore();
}
図形の描画
- 点
drawPoints、drawRawPoints - 線
drawLine - 矩形
drawRect、drawRRect、drawDRRect - 円
drawCircle,drawOval,drawArc
点
点のモデルは下記3種類あります。
enum PointMode {
points,
lines,
polygon,
}
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
// グリッド
_drawGrid(canvas, size);
// 点のPaint
var paint = Paint()
..color = Colors.green
..style = PaintingStyle.stroke
..strokeWidth = 10
..strokeCap = StrokeCap.round;
// 点を描く
_drawPointsWithPoints(canvas, paint);
}
void _drawPointsWithPoints(Canvas canvas, Paint paint) {
// 点の集合体
const List<Offset> points = [
Offset(-120, -20),
Offset(-80, -80),
Offset(40, 40),
Offset(0, 0),
Offset(40, -140),
Offset(80, -160),
Offset(120, -100),
];
canvas.drawPoints(PointMode.points, points, paint);
}
同じ点の集合体でモデルごとの表現は下記通りです。
PointMode.pointsはdrawRawPointsと同じ効果です。
PointMode.linesの場合、集合体が奇数個は最後の1つは無効です。
PointMode.polygonは線を順番で連結し、図形にします。
points | lines | polygon |
---|---|---|
上記の知識によって、drawPointsだけで折れ線グラフの作成は可能です。
void _drawPointsWithPoints(Canvas canvas, Paint paint) {
const List<Offset> points = [
Offset(-120, -20),
Offset(-80, -80),
Offset(-40, -60),
Offset(0, 0),
Offset(40, -140),
Offset(80, 100),
Offset(120, -100),
];
paint.strokeWidth = 10;
// 点を描く
canvas.drawPoints(PointMode.points, points, paint);
// 線を描く
paint.strokeWidth = 2;
canvas.drawPoints(PointMode.polygon, points, paint);
}
線
上記の方法以外、線を描く関数も用意されています。
座標のスタートとエンドの宣言し、点と点を繋ぎます。下記のソースから座標のXY軸を描くこと出来ました。
void _drawAxis(Canvas canvas, Size size) {
_gridPint
..color = Colors.green
..strokeWidth = 1.5;
canvas.drawLine(Offset(-size.width / 2, 0), Offset(size.width / 2, 0), _gridPint);
canvas.drawLine(Offset(0, -size.height / 2), Offset(0, size.height / 2), _gridPint);
canvas.drawLine(Offset(0, size.height / 2), Offset(0 - 7.0, size.height / 2 - 10), _gridPint);
canvas.drawLine(Offset(0, size.height / 2), Offset(0 + 7.0, size.height / 2 - 10), _gridPint);
canvas.drawLine(Offset(size.width / 2, 0), Offset(size.width / 2 - 10, 7), _gridPint);
canvas.drawLine(Offset(size.width / 2, 0), Offset(size.width / 2 - 10, -7), _gridPint);
}
矩形
- drawRect
矩形を宣言するコンストラクターは5つあります。
// 中心から矩形を描く
Rect.fromCenter
// 左上右下から矩形を描く
Rect.fromLTRB
// 左上幅高さから矩形を描く
Rect.fromLTWH
// 内接円から矩形を描く
Rect.fromCircle
// ポイントから矩形を描く
Rect.fromPoints
例から確認しましょう
void _drawRect(Canvas canvas) {
_gridPint
..color = Colors.blue
..strokeWidth = 1.5;
//中心から矩形を描く
Rect rectFromCenter = Rect.fromCenter(
center: const Offset(0, 0),
width: 160,
height: 160,
);
canvas.drawRect(rectFromCenter, _gridPint);
// 左上右下から矩形を描く
Rect rectFromLTRB = const Rect.fromLTRB(-120, -120, -80, -80);
canvas.drawRect(rectFromLTRB, _gridPint..color = Colors.red);
// 左上幅高さから矩形を描く
Rect rectFromLTWH = const Rect.fromLTWH(80, -120, 40, 40);
canvas.drawRect(rectFromLTWH, _gridPint..color = Colors.orange);
// 内接円から矩形を描く
Rect rectFromCircle = Rect.fromCircle(
center: const Offset(100, 100),
radius: 20,
);
canvas.drawRect(rectFromCircle, _gridPint..color = Colors.green);
// ポイントから矩形を描く
Rect rectFromPoints = Rect.fromPoints(
const Offset(-120, 80),
const Offset(-80, 120),
);
canvas.drawRect(rectFromPoints, _gridPint..color = Colors.purple);
}
- drawRRect
角丸矩形は同じく宣言するコンストラクターは5つあります。
// 中心から矩形を描く
RRect.fromRectXY
// 左上右下から矩形を描く
RRect.fromLTRBXY
// 左上幅高さから矩形を描く
RRect.fromLTRBR
// 内接円から矩形を描く
RRect.fromLTRBAndCorners
// ポイントから矩形を描く
RRect.fromRectAndCorners
例から確認しましょう,XYは角丸の中心です。
void _drawRRect(Canvas canvas) {
_gridPint
..color = Colors.blue
..strokeWidth = 1.5;
// fromRectXY
Rect rectFromCenter = Rect.fromCenter(
center: const Offset(0, 0),
width: 160,
height: 160,
);
canvas.drawRRect(RRect.fromRectXY(rectFromCenter, 40, 40), _gridPint);
// fromLTRBXY
canvas.drawRRect(
const RRect.fromLTRBXY(-120, -120, -80, -80, 10, 10),
_gridPint..color = Colors.red,
);
// fromLTRBR
canvas.drawRRect(
RRect.fromLTRBR(80, -120, 120, -80, const Radius.circular(10)),
_gridPint..color = Colors.orange,
);
// fromLTRBAndCorners
canvas.drawRRect(
RRect.fromLTRBAndCorners(
80,
80,
120,
120,
topLeft: const Radius.elliptical(10, 10),
topRight: const Radius.elliptical(10, 10),
bottomRight: const Radius.elliptical(10, 10),
bottomLeft: const Radius.elliptical(10, 10),
),
_gridPint..color = Colors.green,
);
// ポイントから矩形を描く
Rect rectFromPoints = Rect.fromPoints(
const Offset(-120, 80),
const Offset(-80, 120),
);
canvas.drawRRect(
RRect.fromRectAndCorners(
rectFromPoints,
bottomLeft: const Radius.elliptical(10, 10),
topLeft: const Radius.elliptical(10, 10),
topRight: const Radius.elliptical(10, 10),
bottomRight: const Radius.elliptical(10, 10),
),
_gridPint..color = Colors.purple,
);
}
outerとinner2つを定義しないといけないですが、制限としてはouterの領域はinnerより大きくしないといけないです。
void drawDRRect(
RRect outer,
RRect inner,
Paint paint,
)
例:
void _drawDRRect(Canvas canvas) {
_paint
..color = Colors.blue
..strokeWidth = 1.5;
Rect outRect = Rect.fromCenter(center: Offset(0, 0), width: 160, height: 160);
Rect inRect = Rect.fromCenter(center: Offset(0, 0), width: 100, height: 100);
canvas.drawDRRect(RRect.fromRectXY(outRect, 0, 0), RRect.fromRectXY(inRect, 20, 20), _paint);
}
円
- drawCircle(円)
- drawOval(楕円)
- drawArc(アーク)
void drawCircle(
Offset c,
double radius,// 半径
Paint paint,
)
void drawOval(
Rect rect,// 楕円の内接矩形
Paint paint,
)
void drawArc(
Rect rect,// 内接矩形
double startAngle,// 開始ラジアン
double sweepAngle,// 終了ラジアン
bool useCenter,// 中心に連結するか
Paint paint,
)
下記例で確認しましょう
void _drawRound(Canvas canvas) {
_paint.color = Colors.green;
canvas.save();
canvas.translate(-100, 0);
// 円形
canvas.drawCircle(const Offset(0, 0), 25, _paint);
canvas.restore();
// 楕円
canvas.save();
var rect = Rect.fromCenter(
center: const Offset(0, 0),
height: 40,
width: 50,
);
canvas.translate(-40, 0);
canvas.drawOval(rect, _paint);
canvas.restore();
// アーク
canvas.save();
canvas.translate(40, 0);
canvas.drawArc(rect, 0, pi / 2 * 3, true, _paint);
canvas.restore();
// 中心に連結しない
canvas.save();
canvas.translate(100, 0);
canvas.drawArc(rect, 0, pi / 2 * 3, false, _paint);
canvas.restore();
}