web-dev-qa-db-ja.com

HTMLキャンバス内の特定のポイントのマウスオーバーを検出しますか?

Canvas用の分析データ視覚化エンジンを構築し、カーソルの下にあるデータポイントの詳細なメトリックを表示するために、データ要素の上にツールチップのようなホバーを追加するように要求されました。

単純なバー&ガントチャート、ツリーグラフ、および単純な正方形の領域または特定の対象ポイントを持つノードマップの場合、絶対配置されたDIVを:hover属性でオーバーレイすることでこれを実装できましたが、円グラフなどのさらに複雑な視覚化がありますベゼイア曲線で定義された数百の別々のエリアを持つ交通流レンダリング。

どういうわけかオーバーレイを添付したり、ユーザーが特定の閉じたパスの上にマウスを置いたときにイベントをトリガーしたりすることは可能ですか?

ホバーを指定する必要がある各領域は、次のように定義されます。

context.beginPath();
context.moveTo(segmentRight, prevTop);
context.bezierCurveTo(segmentRight, prevTop, segmentLeft, thisTop, segmentLeft, thisTop);
context.lineTo(segmentLeft, thisBottom);
context.bezierCurveTo(segmentLeft, thisBottom, segmentRight, prevBottom, segmentRight, prevBottom);
/*
 * ...define additional segments...
 */
// <dream> Ideally I would like to attach to events on each path:
context.setMouseover(function(){/*Show hover content*/});
// </dream>
context.closePath();

このようなオブジェクトへのバインドは、FlashまたはSilverlightで実装するのはほとんど簡単ですが、現在のCanvas実装には、既存のJavascript APIを直接使用し、他のAjax要素と統合するという利点があるため、Flashを混在させないことを望んでいます。

何か案は?

32
ryandenki

Mousemoveイベントを処理して、イベントからX、Y座標を取得できます。次に、ポイントがパス上にあるかどうかをテストするには、おそらくすべてのパスを反復処理する必要があります。私には 同様の問題 があり、使用できるコードが含まれている可能性があります。

この方法でループすることは、特にIEでは遅くなる可能性があります。速度を上げる可能性がある1つの方法-これはハックですが、非常に効果的です-は、各パスが描画される色を変更して、人間が気づかないようにして、各パスが描画されるようにします別の色。パスの色を検索し、マウスの下のピクセルの色を検索するためのテーブルがあります。

21
Sam Hasler

シャドウキャンバス

私がマウスオーバーを検出するために他の場所で見た最良の方法は、非表示のクリアされたキャンバスに検出したい描画の部分を繰り返すことです。次に、ImageDataオブジェクトを格納します。次に、ImageData配列で対象のピクセルを確認し、アルファ値が0より大きい場合はtrueを返します。

// slow part
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillRect(100,100,canvas.width-100,canvas.height-100);
var pixels = ctx.getImageData(0,0,canvas.width,canvas.height).data;

// fast part
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (pixels[idx]) { // alpha > 0
  ...
}

利点

  • コンテキストメソッドを繰り返すだけなので、何でも検出できます。これは、PNGアルファ、クレイジーな複合シェイプ、テキストなどで機能します。
  • 画像がかなり静止している場合は、関心のある領域ごとに1回だけ実行する必要があります。
  • 「マスク」は遅いですが、ピクセルを調べるのは簡単です。したがって、「速い部分」はマウスオーバー検出に最適です。

短所

  • これは記憶の独り占めです。各マスクはW * H * 4の値です。キャンバス領域が小さい場合、またはマスクする領域が少ない場合は、それほど悪くありません。 Chromeのタスクマネージャを使用してメモリ使用量を監視します。
  • 現在、ChromeおよびFirefox)のgetImageDataに関する既知の問題があります。変数を無効にすると、結果はすぐにガベージコレクションされないため、頻繁に実行すると、メモリが急速に増加します。 。最終的にはガベージコレクションが行われ、ブラウザがクラッシュすることはありませんが、少量のRAMを搭載したマシンに負担がかかる可能性があります。

メモリを節約するためのハック

ImageData配列全体を格納するのではなく、アルファ値を持つピクセルを覚えておけばよいのです。メモリを大幅に節約しますが、マスクプロセスにループを追加します。

var mask = {};
var len = pixels.length;
for (var i=3;i<len;i+=4) if ( pixels[i] ) mask[i] = 1;

// this works the same way as the other method
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (mask[idx]) {
  ...
}
13
jcampbelly

これはメソッドctx.isPointInPathを使用して実行できますが、ExCanvas for IEでは実装されていません。しかし、別の解決策は、私がこの小さなライブラリで行ったように、HTMLマップを使用することです: http://phenxdesign.net/projects/phenx-web/graphics/example.htm インスピレーションを得ることができますが、それでも少しバグがあります。

7
Fabien Ménager

キャンバスに描かれたアイテムと一致するように、エリアに設定された適切な座標でイメージマップをオーバーレイすることをお勧めします。このようにして、ツールチップと他の多くのDOM /ブラウザー機能を無料で利用できます。

1
Quickredfox

正方形のグリッド(Excelスプレッドシートのセルのような)のマウスクリックを検出する必要がありました。高速化するために、少数のセルが残るまでグリッドを再帰的に半分に分割しました。たとえば、100x100グリッドの場合、最初の4つの領域は4つの象限を構成する50x50グリッドにすることができます。次に、これらをさらに4つに分割できます(したがって、それぞれ25x25の16の領域が提供されます)。これには少数の比較が必要であり、最後に25x25グリッドを各セルに対してテストできます(この例では625の比較)。

1
Sid

エリック・ローウェルの「HTML5 CANVAS COOKBOOK」という本があります。その本には、「キャンバスとの相互作用:形状と領域へのイベントリスナーのアタッチ」という名前の章があります。 mousedown、mouseup、mouseover、mouseout、mousemove、touchstart、touchendおよびtouchmoveイベントを実装できます。それを読むことを強くお勧めします。

1
Shekhar

キャンバスに描画するオブジェクト(パス)はキャンバス内で同じオブジェクトとして表現されないため、これは(少なくとも、それほど簡単ではありませんが)実行できません。つまり、これは単なる2Dコンテキストであり、その上に何かを描画すると、それがどのように描画されたかを完全に忘れてしまいます。それは単なるピクセルのセットです。

マウスオーバーなどを見るには、何らかのベクターグラフィックキャンバス、つまりSVGが必要か、既存のものの上に独自に実装する必要があります(Sam Haslerが提案したものです)。

0
Maxim Sloyko