6
<img src="circle.png" onclick="alert('clicked')"/>

circle.png が 400x400 ピクセルの透明な背景画像で、中央に円があるとします。

私が今持っているのは、画像領域全体 (400x400px) をクリックできるということです。私が望むのは、円(非透明ピクセル)のみがクリック可能であることです。

もちろん、この例では<map>タグと円形の領域を使用できることはわかっていますが、実際の画像の透明度を考慮し、あらゆる種類の画像 (つまり、非規則的な形状) で機能する一般的なソリューションを探しています。

私が見ることができる最も複雑な方法は、各ピクセルのアルファに基づいて画像の輪郭をトレースし、パスに変換 (おそらく単純化) し、マップとして適用することです。

これを行うためのより効率的/簡単な方法はありますか?

4

3 に答える 3

17

タグを使用するcanvasと、特定のスポットの下にあるピクセルのカラー値を決定できます。イベント データを使用して座標を決定し、透過性を確認できます。あとは、イメージをキャンバスにロードするだけです。

まず、それを処理します。

  var ctx = document.getElementById('canvas').getContext('2d');
  var img = new Image();
  img.onload = function(){
    ctx.drawImage(img,0,0);
  };
  img.src = [YOUR_URL_HERE];

この最初のビットは canvas 要素を取得し、Imageオブジェクトを作成します。画像が読み込まれると、キャンバスに描画されます。とても簡単です!ただし、イメージがコードと同じドメインにない場合は、同じドメイン ポリシーのセキュリティに違反します。画像のデータを取得するには、画像をローカルでホストする必要があります。画像をbase64でエンコードすることもできますが、これはこの回答の範囲を超えています。(これを行うためのツールについては、この URLを参照してください)。

次に、クリック イベントをキャンバスにアタッチします。そのクリックが入ると、透明度をチェックし、透明でないクリック領域に対してのみ動作します。

    if (isTransparentUnderMouse(this, e))
        return;
    // do whatever you need to do
    alert('will do something!');

マジックは function で発生します。これには、ターゲット キャンバス要素 (クリック ハンドラーのスコープ内) とイベント データ (この例では e) のisTransparentUnderMouse2 つの引数が必要です。this今、私たちは肉に来ます:

var isTransparentUnderMouse = function (target, evnt) {
    var l = 0, t = 0;
    if (target.offsetParent) {
        var ele = target;
        do {
            l += ele.offsetLeft;
            t += ele.offsetTop;
        } while (ele = ele.offsetParent);
    }
    var x = evnt.page.x - l;
    var y = evnt.page.y - t;
    var imgdata = target.getContext('2d').getImageData(x, y, 1, 1).data;
    if (
        imgdata[0] == 0 &&
        imgdata[1] == 0 &&
        imgdata[2] == 0 &&
        imgdata[3] == 0
    ){
        return true;
    }
    return false;
};

まず、問題の要素の正確な位置を取得するために、いくつかの作業を行います。その情報を使用して canvas 要素に渡します。はgetImageData、とりわけ、data指定した場所の RGBA を含むオブジェクトを提供します。

これらの値がすべて 0 の場合、透明度を確認しています。そうでない場合は、何らかの色が存在します。-編集- コメントに記載されているように、実際に確認する必要がある値はimgdata[3]、上記の例の最後の値だけです。値は r(0)g(1)b(2)a(3) で、透明度は a、alpha によって決まります。この同じアプローチを使用して、rgba データがわかっている任意の不透明度で任意の色を見つけることができます。

ここで試してみてください: http://jsfiddle.net/pJ3MD/1/

(注:私の例では、前述のドメイン セキュリティのため、base64 でエンコードされた画像を使用しました。base64 エンコードを使用する予定がない限り、コードのその部分は無視できます)

楽しみのためにスローされたマウスカーソルへの変更を含む同じ例: http://jsfiddle.net/pJ3MD/2/

ドキュメンテーション

于 2012-06-27T15:29:59.353 に答える
4

これは、HTML5 キャンバスを使用して行うことができます。イメージをキャンバスに描画し、クリック ハンドラーをキャンバスにアタッチし、ハンドラーで、クリックされたピクセルが透明かどうかを確認します。

于 2012-06-27T15:14:02.627 に答える
1

この素晴らしい答えをありがとうクリス

キャンバスのスケーリングを処理するために、コードに数行を追加しました

そのため、私が今行っているのは、画像が描かれている正確なピクセルサイズのキャンバスを作成することです。のように (画像 220px*120px の場合):

<canvas width="220" height="120" id="mainMenu_item"></canvas>

css を使用してキャンバスをスケーリングします。

#mainMenu_item{ width:110px; }

調整された isTransparentUnderMouse 関数は次のようになります。

    var isTransparentUnderMouse = function (target, evnt) {

    var l = 0, t = 0;
    if (target.offsetParent) {
        var ele = target;
        do {
            l += ele.offsetLeft;
            t += ele.offsetTop;
        } while (ele = ele.offsetParent);
    }

    var x = evnt.pageX - l;
    var y = evnt.pageY - t;

    var initialWidth = $(evnt.target).attr('width');
    var clientWidth = evnt.target.clientWidth;
    x = x * (initialWidth/clientWidth);

    var initialHeight = $(evnt.target).attr('height');;
    var clientHeight = evnt.target.clientHeight;
    y = y * (initialHeight/clientHeight);

    var imgdata = target.getContext('2d').getImageData(x, y, 1, 1).data;

    if (
        imgdata[0] == 0 &&
        imgdata[1] == 0 &&
        imgdata[2] == 0 &&
        imgdata[3] == 0
    ){
        return true;
    }
    return false;
};
于 2013-01-29T09:26:52.430 に答える