3

HTML5 キャンバスにパスをドラッグ アンド ドロップしたい。
シェイプをクリックすると、SVG がイベントを提供するようなものは見つかりませんでした。
ここでパス項目のイベントを処理する方法を知っている人はいますか? Javaスクリプトライブラリなしでこれを行いたいです。

以下は、パスを描画する現在のコードです。

var canvas = document.getElementById("html5Canvas");
var context = canvas.getContext("2d");
var drawing = false;

canvas.addEventListener("mousedown", startDraw, false);
canvas.addEventListener("mousemove", continueDraw, false);
canvas.addEventListener("mouseup", endDraw, false


function startDraw(event) 
{
    drawing = true;
    context.moveTo(event.clientX, event.clientY);    
}

function continueDraw(event) 
{
    if (drawing)
    {
        context.lineTo(event.clientX, event.clientY);    
        context.stroke();    
    }
}

function endDraw(event) 
{
    if (drawing)
    {
        context.lineTo(event.clientX, event.clientY);    
        context.stroke();    
        drawing = false;
    }
}

ありがとう。

4

1 に答える 1

11

準備

行クリックを検出するには、すべてのパス情報を記録する必要があります。

次の例は、元の投稿で提供されたコードの修正版であり、すべてのストロークを含む配列に (マウス ダウンとマウス ダウンの間の) 各ストロークを記録するストローク レコーダーが含まれています。

簡単にするために、ここではモードを変更するときにマウス クリックをリッスンします。クリック モードでは、ストローク コレクションを反復処理し、以前に記録したパスを再構築してから、マウスの位置がこれらの行のいずれかにあるかどうかを確認します。

コードは、パスを構築する前にヒット領域を使用してオーバーヘッドを削減することで最適化できますが、デモのために必要なコードのみが含まれています。

デモコード

オンラインデモはこちら

記録するには、線/ストロークを保存するものが必要です。

var lines = [], line;

モードを切り替えるときに使用するクリックのイベント リスナーを追加します。通常、代わりに mousedown/up イベントを共有することに注意してください。

canvas.addEventListener("click", checkLine, false);

行をより「クリック可能」にするために、ここでは太い線幅を使用します (デモ用に修正)。

context.lineWidth = 3;

記録するには、既存のコールバックを変更する必要があります。また、マウスを動かすたびに線が重なって再描画されるバグもあり、線が長いと最終的に描画が遅くなります。

また、キャンバスに対して相対的になるようにマウスの位置を調整する必要があります。

function startDraw(event) {

    /// if we are in "click" mode exit from here (for demo)
    if (mode.checked === true) return;

    /// adjust mouse position
    var pos = mouseXY(canvas, event);

    drawing = true;

    /// start a new path
    context.beginPath();
    context.moveTo(pos.x, pos.y);

    /// create a new stroke and push first position to it
    line = [];
    line.push([pos.x, pos.y]);
}

線全体を再描画しないように、描画する部分ごとにパスをリセットする必要があります (最終的なレンダリング/再描画では、もちろん線を一度に再描画しますが、描画中には再描画しません)。

function continueDraw(event) {
    if (drawing) {

        /// adjust mouse position
        var pos = mouseXY(canvas, event);

        /// complete one line segment started in mouse down
        context.lineTo(pos.x, pos.y);    
        context.stroke();

        /// reset path and start from where we ended this line
        context.beginPath();
        context.moveTo(pos.x, pos.y);

        /// store current point to stroke/line
        line.push([pos.x, pos.y]);
    }
}

最後に行が終了したら、ストロークを保存します。

function endDraw(event) {
    if (drawing)    {
        var pos = mouseXY(canvas, event);
        context.lineTo(pos.x, pos.y);    
        context.stroke();
        drawing = false;

        /// push stroke/line to line stack
        lines.push(line);
    }
}

これを使用してマウスの位置を調整します::

function mouseXY(c, e) {
    var r = c.getBoundingClientRect();
    return {x: e.clientX - r.left, y: e.clientY - r.top};
}

ラインクリックの確認

行をチェックするには、行コレクションを繰り返し処理し、各行をパスとして再構築する必要があります。これらのパスを描画する必要がないため、速度は問題ありません。パスが再構築されると、以下を使用して、パスに対して調整されたマウスの位置を確認しますisPointInStroke

function checkLine(e) {
    if (mode.checked === false) return;

    var i = 0, line, l, p, pos = mouseXY(canvas, e);

    /// make sure stroke has same width as originally recorded        
    context.lineWidth = 3;

    /// loop through line collection
    for(; line = lines[i]; i++) {

        /// reset path
        context.beginPath();

        /// begin stroke
        context.moveTo(line[0][0], line[0][1]);

        /// iterate through each point stored
        for(l = 1; p = line[l]; l++) {
            /// add a line
            context.lineTo(p[0], p[1]);
        }

        /// then we check the point
        if (context.isPointInStroke(pos.x, pos.y) === true) {
            alert('hit line ' + i); /// show "ID" of line clicked
            return;
        }
    }
}

複雑に重なっている線でも問題なく検出できます。

デモのスナップショット

(はい、知っています! 私はいつでもダリとムンクを倒すことができます! Xp )

于 2013-09-23T21:25:17.693 に答える