Canvas の内部要素は、DOM 要素ほど便利にインタラクティブなイベント リスナーを追加できません。これは、Canvas には要素の概念がなく、Canvas によって描画される単なるグラフィックスであるためです。これは、インタラクティブな開発に必要な障害です。グラフィックのクリック イベントを監視するという考え方は非常に簡単で、キャンバス要素自体のクリック イベントをリッスンし、クリック座標がどのグラフィック内にあるかを判断するだけです。そうすれば、グラフィックのクリック イベントを偽装して実現できます。この記事では、座標点がキャンバス グラフィック内にあるかどうかを判断する 3 つの方法を紹介します。
合意この記事で紹介した 3 つの方法は、キャンバス内で不規則な形状や不規則な位置を持つグラフィック クリック イベントを識別するのに適しています。規則的な形状や位置を持つシーンの場合は、より単純な実装が必要ですが、ここでは説明しません。
ピクセル法ピクセル検出メソッドの考え方は、キャンバス内に複数のグラフィックス (複数ある場合) をオフスクリーンに個別に描画し、getImageData() メソッドを使用してピクセル データを取得して保存することです。 Canvas 要素がクリック イベントをリッスンすると、クリック座標を使用して、クリックが発生したキャンバス上のピクセルを直接計算し、以前に保存したグラフィック データをトラバースして、このピクセルのアルファ値が 0 であるかどうかを確認できます。 0 の場合は、着地点が現在の図形内にないことを意味します。それ以外の場合は、点がこの図形に到達したことを意味します。
クリック座標に基づいてクリックされたピクセル番号を取得するメソッド:
ピクセル番号 = (縦座標-1) * キャンバス幅 + 横座標
たとえば、幅 5 のキャンバス上の座標 (3,3) をクリックすると、図に示すように、上記の式に従って取得されるピクセル数は (3-1) * 5 + 3 = 18 となります。
Canvas によってエクスポートされたグラフィック データは、各ピクセルを 4 つの数値の配列に rgba 順に格納するため、指定したピクセルのアルファ値にアクセスしたい場合は、この配列の pIndex * 4 + 3 の値を読み取るだけで済みます。この値が 0 でない場合は、ピクセルが表示されている、つまりグラフィックがクリックされていることを意味します。
この方法は、最も直接的なアイデアと最も正確な結果を持ち、グラフィックスの形状に関する要件がないと思われる方法ですが、グラフィックスをキャンバス上で移動する必要がある場合、この方法には致命的な制限があります。結果が正確であることを保証するには、データ キャッシュを頻繁に作成する必要があり、getImageData() メソッドのパフォーマンスが深刻なボトルネックになります。したがって、キャンバス グラフィックが静的な場合、このメソッドは非常に適していますが、そうでない場合は、このメソッドの使用は適していません。
角度法角度判定法の原理は理解しやすいですが、点が多角形の内側にある場合、その点と多角形のすべての頂点との間の角度の合計は正確に 360 度になります。
計算プロセスは次の 3 つのステップに変換できます。
1. 既知のポリゴン頂点と既知の座標が与えられた場合、座標と頂点をペアで組み合わせて 3 点キューを形成します。
2. 既知の 3 点間の角度を求めるには、Yu Xuan の定理を使用できます。
3. 挟角の合計が 360° であるかどうかを判断します。
各ステップは単純で、次のように実装されます。
// 2 点間の距離を計算します const getDistence = function (p1, p2) { return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2. y) * (p1.y - p2.y))};// angle メソッドは、ポイントがポリゴンの内側にあることを決定します const checkPointInPolyline = (point, PolylinePoints) => { let totalA = 0; const A = ポイント; for (let i = 0; i < PolylinePoints.length; i++) { let B, C; if (i === PolylinePoints.length - 1) { B = { x: PolylinePoints[i][0] ]、y: ポリラインポイント[i][1] }; C = { x: ポリラインポイント[0][0] }; else { B = { x: ポリラインポイント[i][0], y: ポリラインポイント[i][1] }; C = { x: ポリラインポイント[i + 1][0], y: ポリラインポイント[i + 1][1] }; } //角度を計算します const angleA = Math.acos((Math.pow(getDistence(A, C), 2) + Math.pow(getDistence(A, B), 2) - Math.pow(getDistence(B, C), 2)) / (2 * getDistence(A, C) * getDistence(A, B))) totalA += angleA } //合計を判定角度は totalA === 2 * Math.PI} を返します。この方法の制限の 1 つは、形状が凸多角形でなければならないことです。多角形が凸多角形でない場合は、計算前に凸多角形に切り出す必要があり、計算が複雑になります。
同様の考え方は、点が多角形の内側にある場合、その点と多角形のすべての頂点によって形成される三角形の面積の合計が多角形の面積に等しくなるはずです。最初にポリゴンの面積を計算するのは非常に面倒なので、このメソッドを直接渡すことができます。
レイ法レイ法は、うまく説明できないのですが、とても簡単な方法です。点と多角形の一辺との交点の数が奇数であれば、その点は多角形の内側にあります。左側など、任意の側のフォーカス ポイントの数を数える必要があるだけであることに注意してください。この方法は多角形の種類を制限せず、凸多角形、凹多角形、またはリングさえも許容されます。
実装も非常に簡単です。
const checkPointInPolyline = (point, PolylinePoints) => { //Ray メソッド let leftSide = 0; const A = point; for (let i = 0; i < PolylinePoints.length; i++) { let B, C; == ポリラインポイント.長さ - 1) { B = { x: ポリラインポイント[i][0], y: ポリラインポイント[i][1] };ポリラインポイント[0][0], y: ポリラインポイント[0][1] }; } else { B = { x: ポリラインポイント[i][0], y: ポリラインポイント[i][1] }; : PolylinePoints[i + 1][0], y: PolylinePoints[i + 1][1] } } //左交差点を判定 let sortByY = [By, Cy].sort((a,b) => ab) if (sortByY[0] < Ay && sortByY[1] > Ay){ if(Bx<Ax || Cx < Ax){ leftSide++ } } } return leftSide % 2 === 1}レイ法には特殊なケースがあり、ポイントが多重変形のエッジ上にある場合には特別な処理が必要になります。しかし、エンジニアリングでは、ユーザーがたまたまグラフィックの境界をクリックした場合、プログラムはユーザーがクリックしていないと判断し、通過できるため、処理する必要はないと思います。
要約する上記の 3 つの方法はいずれも、キャンバス上の不規則なグラフィックのクリック検出を実現できます。その中で、ピクセル法の利点は、形状を選択しないことと、静的なシーンで一定のパフォーマンス上の利点があることです。角度法は理論上の価値があるだけで、工学的に最も実用的ではないと言われるべきです。制限がほとんどなく、実装は簡単で、ほとんどの場合、ray メソッドを理解するだけで済みます。