はじめに
本記事はreact-konvaで楕円描画を実装したい方、SVGで楕円描画を実装したい方(またはSVGの円弧コマンド(Aコマンド)について学びたい方)向けの参考になればと。
react-konvaの基本編で詳しく解説しているので、まずはこちらもみてみてね。
https://qiita.com/yukinonyukinon/items/4cc4b0a36dfa20723c0d
今回作るもの
楕円描画ツールを2つの方法で実装していく:
- Ellipseコンポーネント:シンプルな実装
- SVGパス(円弧コマンド):より柔軟で正確な描画
SVGパスの方がちょっと複雑だけど、マウス位置に楕円のラインが正確に追従するため使いやすいきがする・・
ソースコード全量
gitのリンクこちら
デモアプリ
環境
今回はReact19・TypeScriptを利用している
"react": "19.1.0",
"typescript": "^4.9.5",
"konva": "9.3.20",
"react-konva": "^19.0.4",
Konva・react-konvaのインストール
npm install react-konva konva --save
konvaのEllipsesで楕円を描く
■ 楕円を描画する仕組み
縦横それぞれの半径と中心の位置を指定してあげると楕円を書くことができる
<Ellipse
// 水平方向の半径
radiusX={80}
// 垂直方向の半径
radiusY={30}
// 中心の位置
x={10}
y={10}
stroke="black"
/>
楕円データは以下のようにもつことにする。
{
x: number;
y: number;
radiusX: number;
radiusY: number;
}
(参考)https://konvajs.org/docs/shapes/Ellipse.html
■ クリック&ドラッグでサイズ決定する
操作の流れは以下(正円を描画するときと同じである)
クリック地点:楕円の中心として固定
ドラッグ:マウスを動かした距離が楕円のx,yそれぞれの半径になる
距離の設定
中心地点(Mouse Down地点)から現在のマウス位置までの距離をx,yそれぞれの半径として、マウスを動かすたび更新し続ける。
ちなみに、abs関数は絶対値を返す関数。これで中心点よりマウスが左上側にあっても負の値にならない。
const handleMouseMove = () => {
// 半径を更新
const dx = point.x - ellipse.x;
const dy = point.y - ellipse.y;
const radiusX = Math.abs(dx);
const radiusY = Math.abs(dy);
// Konva用の楕円データ
const ellipseData = {
...ellipse,
radiusX: radiusX,
radiusY: radiusY,
};
setEllipse(ellipseData);
}
複数楕円を書きたい場合も円や線と同様、配列で管理すればいいだけであるので省略。
■ ソースコード全量
gitのリンクはこちら
SVG Pathを使って楕円を描く
■ そもそも・・・SVGパスの基本
SVGパスでは以下のようなコマンドと座標を指定しながら書きたい図形を描画していく。
MはMoveToコマンド:Mの位置が始点となり描画がはじまる
LはLineToコマンド:直線が描画できる
以下は、(0,0)から始まり(50,50)までの直線が赤い線で描画される。
<svg width="320" height="320" xmlns="http://www.w3.org/2000/svg">
{/* 直線 始点M(0,0) から Line L(100,100) */}
<path d="M0 0 L50 50" stroke="red" />
</svg>
この直線を組み合わせて、四角を書いてみる。
<path d="M 100 100 L 150 100 L 150 150 L 100 150 L 100 100" />
以下のようなイメージで四角形が描画されることとなる。
①M100 100:座標(100,100)からスタート
②L150 100:直線で座標(150,100)まで移動
③L150 150:直線で座標(150,150)まで移動
④L100 150:直線で座標(100,150)まで移動
⑤L100 100:直線で座標(100,100)まで移動

上記書き方でもOKだが、Hは水平方向コマンド、Vは垂直方向コマンド、などさまざまな便利コマンドが用意されているみたい。
<path d="M 100 100 H 150 V 150 H 100 V 100" />
ソースコード
参考)https://developer.mozilla.org/ja/docs/Web/SVG/Tutorials/SVG_from_scratch/Paths
■ 楕円をSVGでかいてみる
次はいよいよ楕円をかいてみる。
楕円を描くには円弧コマンドを利用して書いていくみたい。
円弧コマンドは以下のようにたくさんの引数が必要とのこと(ムズカシイ...)
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
それぞれどんな意味なのか調べてみたら以下のような役割を持つらしい。
rx: x方向の半径
ry: y方向の半径
x-axis-rotation:回転角
large-arc-flag:大弧フラグ
sweep-flag:方向フラグ
x:終点x
y:終点y
x-axis-rotation:回転角
それぞれ以下の角度を指定しており、角に応じて傾いていく
赤:0
青:45
緑:90

large-arc-flag:大弧フラグ
同じ楕円上で2点を結ぶ弧は、2つ存在するためどちらを描くか選べるフラグ
赤:短い弧(flg=0)
青:長い弧(flg=1)

sweep-flag:方向フラグ
これは結構文字のまま、2点に対してどっち側の方向に円弧を書くかという意味らしい。
実際に楕円にするにはAコマンド2つ組み合わせて1つの楕円を書き表す
{/* A 半径x 半径y 回転角 大弧フラグ 方向フラグ 終点x 終点y*/}
<path d="M100 150 A40 20 0 1 1 180 150 A40 20 0 1 1 100 150" />
参考)https://developer.mozilla.org/ja/docs/Web/SVG/Tutorials/SVG_from_scratch/Paths#%E5%86%86%E5%BC%A7
Gitサンプルソースコード)https://github.com/rainkorainko/react-konva/blob/main/src/intro/Intro05_SVGElipses.tsx
■ マウスドラッグに合わせた楕円をSVGでかいてみる(react-konva)
実際にマウスのドラッグに合わせて楕円を描画していく。
konva内でSVGパスを利用する基本の形
SVGPathのデータをそのまま入れてあげれば描画可能である。
<Stage width={500} height={500} >
<Layer>
<Path data={"M180 150 A40 20 0 1 1 100 150"} stroke="black" />
</Layer>
</Stage>
マウスドラッグ時
半径がマウスドラッグに合わせて大きくなるよう更新していく。
point.x point.y
:マウスの現在地点
startPoint.x startPoint.y
:クリックした最初の地点
1つ目の円弧Aはクリック地点からマウス現在地点まで、2つ目はマウス現在地点から最初のクリック地点までの戻りを表している。
const dx = point.x - startPoint.x;
const dy = point.y - startPoint.y;
// dxdyの2点間は楕円の直径となるため半分に
const radiusX = Math.abs(dx / 2);
const radiusY = Math.abs(dy / 2);
const pathData = `M${startPoint.x},${startPoint.y}
A${radiusX},${radiusY} 0 1,1 ${point.x},${point.y}
A${radiusX},${radiusY} 0 1,1 ${startPoint.x},${startPoint.y}`;
ソースコード全量
gitソースコード全量はこちら