はじめに
ペイントツールを作るために、良さそうなパッケージをいくつか使ってみて、
図形の回転はどんな実装をしているのか気になったので、実装してみました
class MyPainter extends CustomPainter {
const MyPainter({
required this.angle,
});
final double angle;
@override
void paint(Canvas canvas, Size size) {
final defaultPaint = Paint()
..strokeWidth = 2
..color = const Color(0xFF000000)
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
// Flutterのキャンバスは、左上が(0, 0)の原点となる
// プラスのx座標は右方向、プラスのy座標は下方向への移動となる
// 100x100のRectを作成(左上の座標が(100, 100)となる)
final rect = Rect.fromLTRB(100, 100, 200, 200);
// 現在の状態を保存
canvas.save();
// Rectの中心を原点とする
const double centerX = 150; // (100 + 200) / 2
const double centerY = 150; // (100 + 200) / 2
canvas.translate(centerX, centerY);
// キャンバスを回転する
canvas.rotate(angle);
// 描画位置に影響するため、原点を戻す
canvas.translate(-centerX, -centerY);
// 四角形を描画
canvas.drawRect(const Rect.fromLTRB(100, 100, 200, 200), defaultPaint);
canvas.restore();
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class MyPainterPage extends StatelessWidget {
MyPainterPage({super.key});
final angle = ValueNotifier<double>(0);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: ColoredBox(
color: Colors.orange,
child: GestureDetector(
onScaleUpdate: (details) {
angle.value = details.rotation;
},
child: ValueListenableBuilder(
valueListenable: angle,
builder: (context, angle, _) {
return CustomPaint(
size: const Size(double.infinity, double.infinity),
painter: MyPainter(angle: angle),
);
},
),
),
),
),
),
);
}
}
ポイント解説
Flutterのペイントの原点について
Flutterのペイントの原点は、デフォルトは左上(0, 0)となっている
プラスのx座標は右方向、プラスのy座標は下方向への移動となる
つまり、以下のように(10, 10)の座標に点を描画すると、左上から右に10、下に10移動した座標に点が描画される
canvas.drawPoints(PointMode.points, [const Offset(10, 10)], paint);
Rect.fromLTRBについて
下記のように実装すると、fromLTRBの引数は、下記に相当する。
L: 左の辺のx座標
T: 上の辺のy座標
R: 右の辺のx座標
B: 下の辺のy座標
final rect = Rect.fromLTRB(100, 100, 200, 200);
canvas.drawRect(rect, defaultPaint);
キャンバスの原点の移動について
canvas.translate(x, y);
で原点を移動できる。
Flutterのペイントの描画は原点を基準に計算されるので、原点を移動すると描画位置が変わる。
下記のコードの場合、(10+10, 10+10)のポイントに点が描画される
canvas.translate(10, 10);
canvas.drawPoints(PointMode.points, [const Offset(10, 10)], paint);
キャンパスの原点の移動は、以下の場合に使用することが多いと思います。
- 描画する座標を単純にする時
final rectWidth = 100;
final rectHeight = 100;
final posX = 100;
final posY = 100;
// final rect = Rect.fromLTRB(posX, posY, posX + rectWidth, posY + rectHeight);
// canvas.drawRect(rect, defaultPaint);
canvas.translate(posX, posY);
final rect = Rect.fromLTRB(0, 0, rectWidth, rectHeight);
- 図形の回転の中心を変更する時
次の章で解説します
キャンバスの回転について
canvas.rotate(angle)
でキャンバスを回転できます。
キャンバスを回転させて、図形を描画することで、キャンバスを回転した分、図形も回転します。
例えば、下記のようにキャンバスを斜めにして、まっすぐな線を引くことで、斜めの線が引けます。
ChatGPTに聞いたところ、他にも図形を回転させる方法がありました。
① 座標計算(回転行列) を使う
② drawVertices() を使う
③ Matrix4 (Transform) を使う
キャンバスの回転の使い所としては、複数の図形をすべて回転させるケースと、描画する座標を単純にするケースで使用するみたいです。
キャンバスの状態と復元について
canvas.save()
でキャンバスの回転状態と原点の位置が保存され、canvas.restore()
でキャンバスの回転状態と原点の位置が一つ前の状態に戻ります。
サンプルコードだと下記の意味になります。
// 原点(0, 0) 回転なしのデフォルトの状態を保存
canvas.save();
// 省略
// 四角形描画
// 原点(0, 0) 回転なしのデフォルトの状態に復元
canvas.restore();