2

Canvasを使用して、HTML 5 にさまざまなフォントで一連の文字を描画する Web アプリケーションを構築していますfillText。ユーザーはそのキャンバスのどこかをクリックし、どの文字をクリックしたか (または文字をクリックしたかどうか) を確認する必要があります。

私はする必要があると思います:

  1. 各文字のベクター パスを取得します (これを行う方法がわかりません)。
  2. 簡単な衝突検出アルゴリズムを使用して、クリック ポイントがレター パス内にあるかどうかを確認します。

私が見逃しているこれを行うための簡単な機能はありますか? それとも、このようなもののためのライブラリですか?ライブラリがない場合、特定のフォントの文字のパスを取得して自分でチェックするにはどうすればよいですか?

Oユーザーが文字の真ん中をクリックしてヒットとして登録したくないので、境界ボックスだけでなく、文字の実際の形状を使用する必要があります。

この方向のヒントをいただければ幸いです。

4

2 に答える 2

3

論理

カスタム ロジックを提供しない限り、キャンバス上で個別の文字を処理することはできません。キャンバスに描画されたものはすべて、ピクセルのスープにマージされます。

残念ながら、テキストを純粋なパスとして追加することはできないため、ピクセル値を確認する必要があります。それ以外の場合は、テキストを新しいパスに追加し、isPointInPath各文字に対してメソッドを使用するだけです。

1 つのアプローチ

SO で完全なソリューションを提供することはできませんが、キャンバス上の単一の文字をクリックするための基本的なロジックを提供するために構築できる基盤を次に示します。

  • 各文字はオブジェクトを含めて保存されます。その位置、サイズ、フォント、および文字だけでなく、長方形のヒット領域もあります (以下を参照)
  • これらのオブジェクトを使用して配列を定義し、レンダリング関数に渡します
  • クリックを登録すると、配列を反復処理し、長方形のヒット領域に対してテストし、内部にある場合はピクセルをチェックします (*)

*) 重なっている文字を区別するには、優先度でチェックする必要があります。この char を別のキャンバスにレンダリングして、この char のみのピクセルを取得することもできます。デモではこれを示していませんが、アイデアは得られます。

デモ

var ltrs = []; /// stores the letter objects

/// Create some random objects

for(;i < 20; i++) {

    /// build the object
    var o = {char: alpha[((alpha.length - 1) * Math.random())|0],
             x:    ((w - 20) * Math.random())|0,
             y:    ((h - 20) * Math.random())|0,
             size: (50 * Math.random() + 16)|0,
             font: fonts[((fonts.length - 1) * Math.random())|0]};

             /// store other things such as color etc.

    /// store it in array
    ltrs.push(o);
}

次に、これらの文字をレンダリングする関数があります (デモを参照)。

次に、クリックを処理するときに、オブジェクト配列を反復処理し、最初に境界をチェックして、現在の文字を確認します (ここでピクセルを選択しても、文字を識別できません)。

demo.onclick = function(e) {

    /// adjust mouse position to be relative to canvas
    var rect = demo.getBoundingClientRect(),
        x = e.clientX - rect.left,
        y = e.clientY - rect.top,
        i = 0, o;
    
    /// iterate
    for(;o = ltrs[i]; i++) {

        /// is in rectangle? "Older" letters has higher priority here...
        if (x > o.x && x < (o.x + o.rect[2]) &&
            y > o.y && y < (o.y + o.rect[3])) {

            /// it is, check if we actually clicked a letter
            /// This is what you would adopt to be on a separate canvas...    
            if (checkPixel(x, y) === true) {
                setLetterObject(o, '#f00')
                return;
            }
        }
    }
}

ピクセル チェックは簡単です。x/y 位置で単一のピクセルを選択し、そのアルファ値 (または無地の背景を使用する場合は色)をチェックします。

function checkPixel(x, y) {
    var data = ctx.getImageData(x, y, 1, 1).data;
    return (data[3] !== 0);
}

オンラインデモはこちら

更新されたチェック ピクセル機能:

この更新されたチェックでは、同じ領域で文字が重なっている場合でもチェックできます。

文字を描画するための別のキャンバスを作成します。これにより、文字が分離され、ピクセルを選択すると、その特定の文字からのみピクセルを取得できます。また、オフスクリーン キャンバスは、チェック中に背景ではなく文字のピクセルのみを設定するため、背景色が何であるかは問題ではありません。オーバーヘッドは最小限です。

function checkPixel(o, x, y) {

    /// create off-screen canvas        
    var oc = document.createElement('canvas'),
        octx = oc.getContext('2d'),
        data,
        oldX = o.x,
        oldY = o.y;
   
    /// default canvas is 300x150, adjust if letter size is larger *)
    //oc.width = oc.height = 200;
    
    /// this can be refactored to something better but for demo...
    o.x = 0;
    o.y = 0;

    setLetterObject(octx, o, '#000');
    
    o.x = oldX;
    o.y = oldY;

    data = octx.getImageData(x - oldX, y - oldY, 1, 1).data;
    return (data[3] !== 0);
}

*) キャンバスを作成するときのデフォルトのサイズは 300x150 です。新しいビットマップの再割り当てを避けるために、メモリは既に割り当てられているためそのままにしておき、そこから 1 つのピクセルを選択するだけで済みます。文字のピクセル サイズがデフォルト サイズよりも大きい場合は、もちろん、文字が収まるように再割り当てする必要があります。

このデモでは、x と y の位置を一時的にオーバーライドします。プロダクションでは、setLetterObjectメソッドがこれを何らかの方法でオーバーライドできるようにする必要があります。ただし、最も重要なことは原理を理解することなので、デモではそのままにしておきます。

于 2013-09-21T18:18:19.280 に答える
0

最善の選択肢は、実際にできる最も正確なピクセルを実際に使用することだと思います (ユーザーがクリックするとピクセルが表示されることを思い出してください)。

色を直接使用することはできないため (同じ色のテキスト オブジェクトが多数存在する可能性があるため (同じ色の他のプリミティブも存在する可能性があるため)、代わりに別の「選択」キャンバスを使用できます)。

基本的に、再描画関数でメイン キャンバスにオブジェクトを描画すると、まったく同じサイズの別の非表示キャンバスにもオブジェクトが描画されますが、エンティティごとに固有の色を使用して描画されます。したがって、キャンバス上に最大 1600 万のエンティティ (24 ビット) を保持でき、カラー コードとエンティティ自体の間のマップを保持することで、どのエンティティがクリックされたかを即座に知ることができます。ちなみに、この種のマップは、正確にピッキングを高速化するために、CAD アプリケーションでよく使用されます。

唯一ややこしい部分は、キャンバスに描画するときにアンチエイリアスを無効にするポータブルな方法がないことです。そのため、選択したキャンバスから返される色が、使用した色の 1 つとは異なる可能性があります。さらに悪いことに、エンティティの境界をクリックすると、別の無関係なエンティティが選択されたと見なされます。

これは、ディスプレイが非常に混雑していて、ピックが基本的にランダムでない限り、非常にまれなイベントです。

于 2013-09-21T20:40:55.220 に答える