Controls
editor.controlsコントロール UI のヘッドレス側です。ここでは DOM を生成も所有もしません。「何を描くべきか」を報告し、手元にある要素をハンドルとして機能させます。
これが ui ではなく core にあるのは意図的です。既定 UI は React 専用ではないため、操作ロジックが既定 UI の隣にあると、React バインディングと自作コントロールがそれぞれ独自のコピーを必要とします。3つの実装を一致させ続けることになります。実装は1つです。
getSelectionBox()
getSelectionBox(): SelectionBoxDescriptor | null選択 UI が今どうあるべきかを返します。何も選択されていなければ null で、そのときコントロール DOM が存在しない理由でもあります。
interface SelectionBoxDescriptor {
bounds: OrientedBounds // ワールド空間
isSingle: boolean
hasLocked: boolean
handles: readonly HandleDescriptor[]
}
interface HandleDescriptor {
id: HandleId // 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'rotate'
position: Vec // 枠内の 0..1。パーセント指定にそのまま使える
cursor: string
label: string // メッセージ表を通して解決済み
}bounds.rotation は単一選択では図形に追随し、複数選択では 0 です。
選択にロックされた図形が含まれる場合、handles は空です。単一選択で、その util が canRotate: false を宣言している場合は回転ハンドルが省かれます。
bindHandle()
bindHandle(element: HTMLElement, handle: HandleId): () => voidelement を該当ハンドルとして機能させます。すべてを解除する関数を返すので、保持して破棄時に呼んでください。
設定するもの:
role="button"、メッセージ表からのaria-label、data-hc-handle、tabindexがなければ"0"- インラインの
pointer-events: autoとtouch-action: none
登録するもの:
pointerdown— ポインタをキャプチャし、イベントが Canvas 面へ届くのを止め、onHandlePointerDownで現在のツールへ回送するkeydown— 矢印キーでハンドルを操作。1単位、⇧ で10単位。onHandleNudge経由
オーバーレイは pointer-events: none で、ハンドルだけが個別に受け取りを再開します。それによりブラウザが所有権を裁定します。ハンドルの上ならハンドル、それ以外なら Canvas。JavaScript のヒットテストが決めているわけではありません。
アクセシビリティ記述子
getA11yShapeDescriptors(): A11yShapeDescriptor[]
getA11ySummary(): { total: number; visible: number }interface A11yShapeDescriptor {
id: ShapeId
label: string // ShapeUtil.getAccessibleLabel、なければ種別名
selected: boolean
locked: boolean
}記述子は表示範囲のみを対象とします。図形ごとに出力すると5,000ノードが DOM に並び、アーキテクチャが依拠する有界なノード数を失います。総数が隠れないよう、リストと一緒に要約も提示してください。
定数
const RESIZE_HANDLES: readonly HandleId[]
// ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']ハンドルの位置とカーソル:
| ハンドル | position | カーソル |
|---|---|---|
nw | { x: 0, y: 0 } | nwse-resize |
n | { x: 0.5, y: 0 } | ns-resize |
ne | { x: 1, y: 0 } | nesw-resize |
e | { x: 1, y: 0.5 } | ew-resize |
se | { x: 1, y: 1 } | nwse-resize |
s | { x: 0.5, y: 1 } | ns-resize |
sw | { x: 0, y: 1 } | nesw-resize |
w | { x: 0, y: 0.5 } | ew-resize |
rotate | { x: 0.5, y: 0 } | grab |
rotate は n と位置を共有しており、スタイルシートが --hc-rotate-distance で上へ逃がしています。
使い方
完全な実装例はコントロールを自作するに、これらを包むフックは React にあります。