DOM は、Web フロントエンド分野の非常に重要な部分です。DOM は、HTML 要素を処理する場合だけでなく、グラフィック プログラミングでも使用されます。たとえば、SVG 描画では、さまざまなグラフィックスが DOM ノードの形式でページに挿入されます。これは、グラフィックスを DOM メソッドを使用して操作できることを意味します。たとえば、 要素がある場合、jquery を直接使用してクリック イベント $('#p1').click(function(){…})" を追加できます。この DOM 処理方法は HTML5 には適していません。Canvas では、別のメカニズムが使用されます。Canvas 上にどれだけ多くのグラフィックスが描画されても、実際には Canvas 自体が Canvas の一部であるため、それ自体が Canvas の一部になることはありません。個別に取得されるため、誰かに直接与えることはできません。グラフィックス
に JavaScript イベントを追加しました。
キャンバスの制限事項
Canvas では、すべてのグラフィックがフレーム上に描画されます。描画メソッドは描画されたグラフィック要素を戻り値として出力せず、js は描画されたグラフィック要素を取得できません。例:
cvs = document.getElementById( 'mycanvas' );
ctx = Canvas.getContext('2d');
theRect = ctx.rect(10, 10, 100, 100);
ctx.ストローク();
コンソール.log(theRect); //未定義
このコードは、canvas タグ内に四角形を描画します。まず、グラフィックスを描画する rect メソッドには戻り値がないことがわかります。ブラウザの開発者ツールを開くと、canvas タグ内にコンテンツが追加されていないこと、および js で取得された Canvas 要素と現在のコンテキストには、新しいグラフィックスを示すコンテンツが含まれていないこともわかります。
したがって、フロントエンドで一般的に使用される DOM メソッドは、キャンバスには適用できません。たとえば、上の Canvas 内の四角形をクリックすると、実際には Canvas 要素全体をクリックすることになります。
イベントを Canvas 要素にバインドします
イベントは Canvas 要素レベルまでしか到達できないため、さらに進んで、Canvas 内のどのグラフィックでクリックが発生したかを特定したい場合は、処理用のコードを追加する必要があります。基本的な考え方は、イベントを Canvas 要素にバインドし、イベントが発生したときにイベント オブジェクトの位置を確認し、その位置をカバーするグラフィックを確認するというものです。たとえば、上の例では、x 軸の 10 ~ 110、y 軸の 10 ~ 110 の範囲をカバーする長方形が描画されました。この範囲内でマウスがクリックされている限り、それは四角形をクリックしたと見なされ、四角形によって処理される必要があるクリック イベントを手動でトリガーできます。実際、アイデアは比較的単純ですが、実装はまだ少し複雑です。この判定処理の効率性を考慮するだけでなく、所々でイベントの種類を再判定したり、Canvas 内部のキャプチャやバブリングの仕組みを再定義したりする必要があります。
最初に行うことは、イベントを Canvas 要素にバインドすることです。たとえば、クリック イベントを Canvas 内のグラフィックにバインドする場合は、Canvas 要素を通じてイベントをプロキシする必要があります。
cvs = document.getElementById( 'mycanvas' );
cvs.addEventListener('click', function(e){
//...
}, false);
次に、イベント オブジェクトが発生する位置を決定する必要があります。イベント オブジェクト e の LayerX 属性と LayerY 属性は、Canvas 内部座標系の座標を表します。ただし、Opera はこの属性をサポートしておらず、Safari はこの属性を削除する予定であるため、いくつかの互換性のある記述メソッドを作成する必要があります:
function getEventPosition(ev){
var x, y;
if (ev.layerX || ev.layerX == 0) {
x = ev.layerX;
y = ev.layerY;
} else if (ev. offsetX || ev.offsetX == 0) { // Opera
x = ev.offsetX;
y = ev.offsetY;
}
return {x: x, y: y};
}
//注: 上記の関数を使用するには、Canvas 要素の位置を絶対位置に設定する必要があります。
イベント オブジェクトの座標位置がわかったので、キャンバス内のどのグラフィックスがこの座標をカバーしているかを決定する必要があります。
isPointInPath メソッド
Canvas の isPointInPath メソッドは、現在のコンテキスト グラフィックが特定の座標をカバーしているかどうかを判断できます。
cvs = document.getElementById( 'mycanvas' );
ctx = Canvas.getContext('2d');
ctx.rect(10, 10, 100, 100);
ctx.ストローク();
ctx.isPointInPath (50, 50); //true
ctx.isPointInPath(5, 5); //false
;
次に、四角形上でクリック イベントが発生するかどうかを決定するイベント判定を追加します。
cvs.addEventListener('click ', function (e){
p = getEventPosition(e);
if(ctx.isPointInPath(p.x, p.y)){
//四角形をクリックしました
}
}, false );
上記は Canvas イベントを処理する基本的な方法ですが、複数のグラフィックスが描画されている場合、isPointInPath メソッドは現在のコンテキスト内のパスのみを決定するため、上記のコードには制限があります。キャンバスのみ イベントは最後のグラフィックのコンテキストに基づいて判断できます。例:
cvs = document.getElementById('mycanvas');
ctx = Canvas.getContext('2d');
ctx.beginPath();
ctx.rect(10, 10, 100, 100);
ctx.ストローク();
ctx.isPointInPath(20, 20); //true
ctx.beginPath();
ctx.rect(110, 110, 100 , 100);
ctx.ストローク();
ctx.isPointInPath(150, 150); //true
ctx.isPointInPath(20, 20); //false
上記のコードからわかるように、isPointInPath メソッドは現在のコンテキスト内のグラフィックス パスのみを識別でき、以前に描画されたパスを遡って判断することはできません。この問題の解決策は、クリック イベントが発生したときに、すべてのグラフィックスを再描画し、描画されたグラフィックスごとに isPointInPath メソッドを使用して、イベントの座標がグラフィックスの範囲内にあるかどうかを判断することです。
ループの再描画とイベントのバブリング
循環再描画を実現するには、グラフィックスの基本パラメータを事前に保存する必要があります:
arr = [
{x:10, y:10, width:100, height:100},
{x:110, y: 110、幅: 100、高さ: 100}
];
cvs = document.getElementById('mycanvas');
ctx = Canvas.getContext('2d');
draw( );
functiondraw(){
ctx.clearRech(0, 0, cvs.width, cvs.height);
arr.forEach(function(v){
ctx.beginPath();
ctx.rect(v.x, v.y, v.width, v.height);
ctx.ストローク();
});
}
上記のコードは、2 つの四角形の基本パラメーターを事前に保存しており、draw メソッドが呼び出されるたびに、これらの基本パラメーターがループで呼び出され、2 つの四角形が描画されます。ここでは、再描画時にキャンバスをクリアするために、clearRect メソッドも使用されています。次に行うことは、イベント デリゲートを追加し、再描画時に各コンテキストで isPointInPath メソッドを使用することです。
cvs.addEventListener('click ', function (e){
p = getEventPosition(e);
draw(p);
}, false);
イベント発生時に座標を渡すイベントオブジェクトのメソッド処理を描画します。ここで描画メソッドにいくつかの小さな変更を加える必要があります:
functiondraw(p){
var who = [];
ctx.clearRech(0, 0, cvs.width, cvs.height);
arr.forEach(function(v, i){
ctx.beginPath( );
ctx.rect(v.x, v.y, v.width, v.height);
ctx.ストローク();
if(p && ctx.isPointInPath(p.x, p.y)){
// イベント座標が渡される場合、isPointInPath を使用して決定します
// 現在の環境が座標をカバーする場合、現在の環境のインデックス値を配列に入れます
who.push(i);
}
});
//配列内のインデックス値に従って、対応する要素を arr 配列内で見つけることができます。
誰を返します;
}
上記のコードでは、クリックイベントが発生するとdrawメソッドで再描画を行い、再描画処理中に各グラフィックがイベント座標を覆っているかどうかを確認し、判定が真であればグラフィックがクリックされたものとみなされます。 、グラフィックのインデックス値を配列に入れ、最後にその配列をdrawメソッドの戻り値として使用します。この処理メカニズムでは、Canvas 内に N 個のグラフィックスがあり、その一部が重なっていて、この重なった領域でクリック イベントが発生した場合、draw メソッドの戻り配列には N 個のメンバーが存在します。このとき、配列の最後のメンバーがキャンバスの先頭にあり、最初のメンバーが最後にあるのと少し似ています。先頭のメンバーは e.target であると考えることができます。 、その他のメンバーは e.target です。バブル プロセス中に渡されるノードです。もちろん、これは最も単純な処理方法にすぎません。実際に DOM 処理をシミュレートしたい場合は、グラフィックスの親子関係を設定する必要があります。
上記はCanvasイベントの基本的な処理方法です。実際のアプリケーションでは、グラフィックスパラメータをどのようにキャッシュするか、ループの再描画をどのように行うか、イベントのバブリングをどのように処理するかなど、実際の状況に応じて対処する必要があります。なお、clickは比較的扱いやすいイベントですが、比較的面倒なイベントはmouseover、mouseout、mousemoveです。一度マウスがCanvas要素に入ると必ずmousemoveイベントが発生するため、mouseoverとmouseoutを別々に設定したい場合に使用します。特定のグラフィックについては、マウスの移動ルートを記録し、グラフィックの開始ステータスと終了ステータスを設定する必要もあります。処理ステップが複雑になるにつれて、パフォーマンスの問題にさらに注意を払う必要があります。