web-dev-qa-db-ja.com

onclickとonmouseoverをcanvas要素に追加します

onclickonmouseover、およびonmouseoutイベントをcanvas要素の個々の形状に追加したいと思います。

私はこれをSVGでさまざまな方法で実行しようとしましたが、すべての主要なブラウザーで単一の方法が機能するわけではないことがわかりました。

たぶん、onclickやおそらく他のイベントをキャンバスの形に追加する簡単な方法はありますか?

誰かがonclickを追加する方法を教えてもらえますか?

これが私のコードです:

canvas
{
  background:gainsboro;
  border:10px ridge green;
}
<canvas id="Canvas1"></canvas>
var c=document.getElementById("Canvas1");

var ctx=c.getContext("2d");
ctx.fillStyle="blue";
ctx.fillRect(10,10,60,60);

var ctx=c.getContext("2d");
ctx.fillStyle="red";
ctx.fillRect(80,60,60,60);

// these need an onclick to fire them up. How do I add the onclick
function blue()
{
  alert("hello from blue square")
}

function red()
{
  alert("hello from red square")
}
12
Mr SVG

これは、個々のキャンバスシェイプにイベントを追加するための最低限のフレームワークです

プレビューは次のとおりです。 http://jsfiddle.net/m1erickson/sAFku/

SVGとは異なり、キャンバスに図形を描画した後は、その図形を識別する方法はありません。

キャンバスには、個々の形状はなく、ピクセルでいっぱいのキャンバスがあります。

個々のキャンバスシェイプを識別して「使用」できるようにするには、そのシェイプのすべての基本的なプロパティを覚えておく必要があります。

長方形を識別するために必要なプロパティは次のとおりです。

  • x位置、
  • y位置、
  • 幅、
  • 高さ。

また、長方形のいくつかの基本的なスタイリングプロパティを覚えておく必要があります。

  • 塗りつぶしの色、
  • ストロークカラー、
  • ストローク幅。

そこで、独自の基本的なプロパティをすべて記憶する長方形の「クラス」オブジェクトを作成する方法を次に示します。

「クラス」という用語に慣れていない場合は、形状を定義するために使用できる「クッキーカッター」と考えてください。

次に、「cookie-cutter」クラスを使用して、その形状の複数のコピーを作成できます。

さらに良い...クラスは、作成する各コピーの基本的なプロパティを変更できるほど柔軟です。

長方形の場合、1つのクラスを使用して、幅、高さ、色、および位置が異なる多数の長方形を作成できます。

ここで重要なのは、クラスが非常に柔軟で再利用可能であるため、クラスを作成することです!

これは、カスタム長方形に関するすべての基本情報を「記憶」するrectクラスです。

// the rect class 

function rect(id,x,y,width,height,fill,stroke,strokewidth) {
    this.x=x;
    this.y=y;
    this.id=id;
    this.width=width;
    this.height=height;
    this.fill=fill||"gray";
    this.stroke=stroke||"skyblue";
    this.strokewidth=strokewidth||2;
}

このクラスを再利用して、必要な数の新しい長方形を作成できます...また、さまざまなニーズを満たすために、新しい長方形にさまざまなプロパティを割り当てることができます。

(プロパティを入力して)実際の長方形を作成すると、クラスのすべての「クッキーカッター」コピーに独自のプロパティのプライベートセットがあります。

「cookie-cutter」クラスを使用して、キャンバス上に描画する1つ以上の実際の長方形を作成する場合、結果の実際の長方形は「オブジェクト」と呼ばれます。

ここでは、1つのクラスから3つの実際の長方形オブジェクトを作成します。各実オブジェクトに異なる幅、高さ、色を割り当てました。

var myRedRect = new rect("Red-Rectangle",15,35,65,60,"red","black",3);

var myGreenRect = new rect("Green-Rectangle",115,55,50,50,"green","black",3);

var myBlueRect = new rect("Blue-Rectangle",215,95,25,20,"blue","black",3);

それでは、draw()関数を追加して、クラスにキャンバス上に自分自身を描画する機能を与えましょう。ここに、キャンバスコンテキストの描画コマンドとスタイリングコマンドを配置します。

rect.prototype.draw(){
    ctx.save();
    ctx.beginPath();
    ctx.fillStyle=this.fill;
    ctx.strokeStyle=this.stroke;
    ctx.lineWidth=this.strokewidth;
    ctx.rect(x,y,this.width,this.height);
    ctx.stroke();
    ctx.fill();
    ctx.restore();
}

Draw()関数を使用してキャンバスに長方形を描画する方法は次のとおりです。 2つの長方形オブジェクトがあり、2つの四角形をキャンバスに表示するには、両方で.draw()を実行する必要があることに注意してください。

var myRedRect = new rect("Red-Rectangle",15,35,65,60,"red","black",3);
myRedRect.draw();

var myBlueRect = new rect("Blue-Rectangle",125,85,100,100,"blue","orange",3);
myBlueRect.draw();

次に、rectクラスに、ポイント(マウス)がそのrect内にあるかどうかを通知する機能を与えます。ユーザーがマウスイベントを生成するとき、このisPointInside()関数を使用して、マウスが現在rect内にあるかどうかをテストします。

// accept a point (mouseposition) and report if it’s inside the rect

rect.prototype.isPointInside = function(x,y){
    return( x>=this.x 
            && x<=this.x+this.width
            && y>=this.y
            && y<=this.y+this.height);
}

最後に、rectクラスを通常のブラウザのマウスイベントシステムに関連付けることができます。

JQueryにキャンバス上のマウスクリックをリッスンするように依頼します。次に、そのマウスの位置をrectオブジェクトにフィードします。 rectのisPointInside()を使用して、クリックがrect内にあったかどうかを報告します。

// listen for click events and trigger handleMouseDown
$("#canvas").click(handleMouseDown);

// calc the mouseclick position and test if it's inside the rect
function handleMouseDown(e){

    // calculate the mouse click position
    mouseX=parseInt(e.clientX-offsetX);
    mouseY=parseInt(e.clientY-offsetY);

    // test myRedRect to see if the click was inside
    if(myRedRect.isPointInside(mouseX,mouseY)){

        // we (finally!) get to execute your code!
        alert("Hello from the "+myRedRect.id);
    }
}

// These are the canvas offsets used in handleMouseDown (or any mouseevent handler)
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;

ええと...それがあなたがキャンバスの形を「覚えている」方法とあなたの質問のコードを実行する方法です!

alert("hello from blue square")

これは、さまざまな長方形を作成し、マウスクリックを報告する最低限の「クラス」です。

このテンプレートを開始点として使用して、あらゆる種類のキャンバス形状ですべてのマウスイベントをリッスンできます。

ほとんどすべてのキャンバスの形状は、長方形または円形です。

長方形のキャンバス要素

  • 矩形
  • 画像
  • テキスト
  • ライン(はい!)

円形のキャンバス要素

  • サークル
  • アーク
  • 正多角形(はい!)

不規則なキャンバス要素

  • カーブ(キュービック&クワッドベジエ)

IsPointInside()は、円の場合は次のようになります。

// check for point inside a circlular shape
circle.prototype.isPointInside = function(x,y){
    var dx = circleCenterX-x;
    var dy = circleCenterY-y;
    return( dx*dx+dy*dy <= circleRadius*circleRadius );
}

不規則な形状のキャンバス要素でもisPointInsideを持つことができますが、通常は複雑になります。

それでおしまい!

少し拡張されたコードとフィドルは次のとおりです。 http://jsfiddle.net/m1erickson/sAFku/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;

    //
    var rect = (function () {

        // constructor
        function rect(id,x,y,width,height,fill,stroke,strokewidth) {
            this.x=x;
            this.y=y;
            this.id=id;
            this.width=width;
            this.height=height;
            this.fill=fill||"gray";
            this.stroke=stroke||"skyblue";
            this.strokewidth=strokewidth||2;
            this.redraw(this.x,this.y);
            return(this);
        }
        //
        rect.prototype.redraw = function(x,y){
            this.x=x;
            this.y=y;
            ctx.save();
            ctx.beginPath();
            ctx.fillStyle=this.fill;
            ctx.strokeStyle=this.stroke;
            ctx.lineWidth=this.strokewidth;
            ctx.rect(x,y,this.width,this.height);
            ctx.stroke();
            ctx.fill();
            ctx.restore();
            return(this);
        }
        //
        rect.prototype.isPointInside = function(x,y){
            return( x>=this.x 
                    && x<=this.x+this.width
                    && y>=this.y
                    && y<=this.y+this.height);
        }


        return rect;
    })();


    //
    function handleMouseDown(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);

      // Put your mousedown stuff here
      var clicked="";
      for(var i=0;i<rects.length;i++){
          if(rects[i].isPointInside(mouseX,mouseY)){
              clicked+=rects[i].id+" "
          }
      }
      if(clicked.length>0){ alert("Clicked rectangles: "+clicked); }
    }


    //
    var rects=[];
    //
    rects.Push(new rect("Red-Rectangle",15,35,65,60,"red","black",3));
    rects.Push(new rect("Green-Rectangle",60,80,70,50,"green","black",6));
    rects.Push(new rect("Blue-Rectangle",125,25,10,10,"blue","black",3));

    //
    $("#canvas").click(handleMouseDown);


}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
21
markE

より最新の回答を追加しました:この質問が投稿されてから、キャンバス内のローカルクリックを検出するために使用できる2つの新しい手法があります素子:

  • _Path2D_:パスは個別のPath2Dオブジェクトに保存し、isPointInPath()を使用してチェックできます。
  • addHitRegion:イベントシステムと統合されているため、リージョンのイベントオブジェクト自体を確認できます。

Path2Dの例

_ var path1 = new Path2D();
 path1.rect(x1, y1, w, h);    // add sub-path to Path2D object

 var path2 = new Path2D();
 path2.rect(x2, y2, w, h);    // add sub-path to Path2D object

 // now we can iterate through the objects to check which path we
 // clicked without the need to rebuild each path as we did in the past
 if (ctx.isPointInPath(path1, x, y)) { ... }
_

Path2Dの詳細を読む ここpolyfill も存在します。

addHitRegionの例

_// define a region using path
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.addHitRegion({id: "test"});

// now we can check in the event if the region was hit by doing:
canvas.addEventListener("mousemove", function(event){
  if(event.region) {
    // a region was hit, id can be used (see specs for options)
  }
});
_

addHitRegion()ここ についてもっと読む。

FirefoxとChromeの両方がフラグを介してこれを有効にすることをサポートしているので、まだ少し早いことに注意してください。他のブラウザもそれに続くことを願っています。

1
user1693593

つまり、シェイプはオブジェクトとして公開されていないため、キャンバス内のシェイプにリスナーを追加することはできません。これを実装する最も簡単な方法は、キャンバス上で単一のリスナーを使用し、キャンバスに描画されたすべてのオブジェクトをループして正しいオブジェクトを見つけることです。

This 回答は、ライブラリRaphaelを使用してこれを実装する方法を説明しています。これには、他にも多くの利点があります。

ライブラリを使用したくない場合、これは非常に短い例です。

rects = [{ color : "blue", Origin : { x : 10, y : 10 }, size : { width : 60, height: 60}},
         { color : "red", Origin : { x : 80, y : 60 }, size : { width : 60, height: 60}}]

function onClick(event) {
    var length = rects.length;

    for (var i = 0; i < length; ++i) {
        var obj = rects[i];
        if (event.x > obj.x && event.x < obj.Origin.x + obj.size.width &&
            event.y > obj.y && event.y < obj.Origin.y + obj.size.height) {
            console.log("Object clicked: " + obj);
        }
    }

注:オブジェクトがたくさんある場合、このアプローチは少し遅くなる可能性があります。これは、2D空間データ構造を使用することで対処できます。

1
Hugo Tunius

CanvasとSVGの主な違いは、Canvasは、ピクセル配列に結果として生じる変更以外に、描画された形状に関する情報を保持しないことです。

したがって、1つのオプションは、マウスクリックハンドラーの対応するピクセルカラー値によって形状を認識することです。

function onClick(event) {
  var data = ctx.getImageData(event.x, event.y, 1, 1);
  var red = data[0];
  var green = data[1];
  var blue = data[2];
  var color = red << 16 | green << 8 | blue;

  if (color == 0x0000ff) {
    blue();
  } else if (color == 0x0ff0000) {
    red();
  }
}

このアプローチを使用して同じ色の複数のオブジェクトのクリックを追跡する場合は、追跡可能にするために各形状の色をわずかに変更する必要があります。

別のホストから画像を追加する場合、このアプローチは機能しません。この場合、同じOriginポリシーがgetImageDataを妨げるためです。

0
Stefan Haustein