3

各テキストのウェッジを作成するcircleCreate関数にテキスト配列を渡しています。私がやろうとしているのは、各ウェッジにクリック イベントを追加することです。そのため、ユーザーがウェッジをクリックすると、各ウェッジ テキストでアラートがスローされます。

しかし、それは機能していません。外側の円のみが警告テキストです。そしてそれはいつも同じテキストを言います。両方の内側の円は未定義のアラートです。

http://jsfiddle.net/Yushell/9f7JN/

var layer = new Kinetic.Layer();

function circleCreate(vangle, vradius, vcolor, vtext) {
    startAngle = 0;
    endAngle = 0;
    for (var i = 0; i < vangle.length; i++) {
        // WEDGE
        startAngle = endAngle;
        endAngle = startAngle + vangle[i];
        var wedge = new Kinetic.Wedge({
            x: stage.getWidth() / 2,
            y: stage.getHeight() / 2,
            radius: vradius,
            angleDeg: vangle[i],
            fill: vcolor,
            stroke: 'black',
            strokeWidth: 1,
            rotationDeg: startAngle
        });
        /* CLICK NOT WORKING
        wedge.on('click', function() {
            alert(vtext[i]);
        });*/
        layer.add(wedge);
    }
    stage.add(layer);
}
4

3 に答える 3

2

各ループ反復でイベント ハンドラーとして定義する各無名関数は同じスコープを共有するため、各関数は、表示しようとしているテキストの配列アドレスとして同じ var (i) を参照します。ループごとに var i を再定義しているため、i に割り当てられた最後の値が配列の長さになるため、クリック イベントごとに表示されるメッセージ配列の最後のテキスト メッセージが常に表示されます。

ここに解決策があります:

var layer = new Kinetic.Layer();

function circleCreate(vangle, vradius, vcolor, vtext) {
    startAngle = 0;
    endAngle = 0;
    for (var i = 0; i < vangle.length; i++) {
        // WEDGE
        startAngle = endAngle;
        endAngle = startAngle + vangle[i];
        var wedge = new Kinetic.Wedge({
            x: stage.getWidth() / 2,
            y: stage.getHeight() / 2,
            radius: vradius,
            angleDeg: vangle[i],
            fill: vcolor,
            stroke: 'black',
            strokeWidth: 1,
            rotationDeg: startAngle
        });
        (function(index) {
            wedge.on('click', function() {
                alert(vtext[i]);
            });
        })(i)
        layer.add(wedge);
    }
    stage.add(layer);
}
于 2013-06-13T17:00:52.333 に答える
2

これは、イベント ハンドラーなどの非同期 JavaScript コードで発生する典型的な問題です。関数のforループは、ウェッジごとにインクリメントcircleCreate()する変数を使用します。iこれはi、くさびを作成するために使用する場所で問題ありません。

            angleDeg: vangle[i],

ただし、clickイベント ハンドラー内で使用すると失敗します。

            alert(vtext[i]);

何故ですか?

呼び出しを使用してウェッジを作成するnew Kinetic.Wedge()と、これはループ内で直接行われます。このコードは同期的に実行されます。iループのこの特定の繰り返しが実行される瞬間に存在するの値を使用します。

ただし、clickその時点ではイベント ハンドラーは実行されません。クリックしないと、まったく実行されない場合があります。ウェッジをクリックすると、元のループの実行が終了してからかなり時間がたってから、そのイベント ハンドラーが呼び出されます。

iでは、イベント ハンドラー実行されるときの値は何でしょうか? 最初に実行されたときにコードが残した値です。このforループは、iが等しい場合に終了します。vangle.lengthつまり、iが配列の末尾を超えているため、vangle[i]ですundefined

ループの反復ごとに関数を呼び出すだけで、クロージャーを使用してこれを簡単に修正できます。

var layer = new Kinetic.Layer();

function circleCreate(vangle, vradius, vcolor, vtext) {
    startAngle = 0;
    endAngle = 0;
    for (var i = 0; i < vangle.length; i++) {
        addWedge( i );
    }
    stage.add(layer);

    function addWedge( i ) {
        startAngle = endAngle;
        endAngle = startAngle + vangle[i];
        var wedge = new Kinetic.Wedge({
            x: stage.getWidth() / 2,
            y: stage.getHeight() / 2,
            radius: vradius,
            angleDeg: vangle[i],
            fill: vcolor,
            stroke: 'black',
            strokeWidth: 1,
            rotationDeg: startAngle
        });
        wedge.on('click', function() {
            alert(vtext[i]);
        });
        layer.add(wedge);
    }
}

addWedge()ここで何が起こるかというと、関数を呼び出すと、iループの反復ごとに個別に の値が取得されます。ご存知のように、すべての関数は独自のローカル変数/パラメーターを持つことができ、i内部addWedge()はその関数に対してローカルであり、具体的には、その関数の個々の呼び出しに対してローカルです。addWedge()(は独自の関数であるため、そのi関数の内部は外部の関数と同じではないことに注意してください。これが混乱を招く場合は、別の名前を付けても問題ありません。)icircleCreate()

更新されたフィドル

より良い方法

とはいえ、データを構造化する別のアプローチをお勧めします。あなたのコードを読んでいるときに、角度とテキスト配列が私の目を引きました:

var anglesParents = [120, 120, 120];
var parentTextArray = ['Parent1', 'Parent2', 'Parent3'];

子と孫の配列には、似ていますがより長いペアがあります。

これらの配列の値は、 のvtext[i]およびvangle[i]参照で使用しますcircleCreate()

一般に、このような並列配列を使用する特定の理由がない限り、それらをオブジェクトの単一の配列に結合すると、コードがよりクリーンになります。

[
    { angle: 120, text: 'Parent1' },
    { angle: 120, text: 'Parent2' },
    { angle: 120, text: 'Parent3' }
]

ネストされたサークルの場合、これをさらに一歩進めて、3 つのリングすべてを組み合わせて、ネストされたリングのセット全体を表すオブジェクトの 1 つの大きな配列にすることができます。これらの配列がある場所:

var anglesParents = [120, 120, 120];
var anglesChildren = [120, 60, 60, 60, 60];
var anglesGrandchildren = [
    33.33, 20, 23.33, 43.33, 22.10, 25.26,
    12.63, 28, 32, 33, 27, 36, 14.4, 9.6
];
var grandchildrenTextArray = [
    'GrandCHild1', 'GrandCHild2', 'GrandCHild3', 'GrandCHild4',
    'GrandCHild5', 'GrandCHild6', 'GrandCHild7', 'GrandCHild8',
    'GrandCHild9', 'GrandCHild10', 'GrandCHild11', 'GrandCHild12',
    'GrandCHild13', 'GrandCHild14', 'GrandCHild15', 'GrandCHild16'
];
var childrenTextArray = [
    'Child1', 'Child2', 'Child3', 'Child4', 'Child5'
];
var parentTextArray = ['Parent1', 'Parent2', 'Parent3'];

それはそのようになります:

var rings = [
    {
        radius: 200,
        color: 'grey',
        slices: [
            { angle: 33.33, text: 'GrandChild1' },
            { angle: 20, text: 'GrandChild2' },
            { angle: 23.33, text: 'GrandChild3' },
            { angle: 43.33, text: 'GrandChild4' },
            { angle: 22.10, text: 'GrandChild5' },
            { angle: 25.26, text: 'GrandChild6' },
            { angle: 12.63, text: 'GrandChild7' },
            { angle: 28, text: 'GrandChild8' },
            { angle: 32, text: 'GrandChild9' },
            { angle: 33, text: 'GrandChild10' },
            { angle: 27, text: 'GrandChild10' },
            { angle: 36, text: 'GrandChild12' },
            { angle: 14.4, text: 'GrandChild13' },
            { angle: 9.6, text: 'GrandChild14' }
        ]
    },
    {
        radius: 150,
        color: 'darkgrey',
        slices: [
            { angle: 120, text: 'Child1' },
            { angle: 60, text: 'Child2' },
            { angle: 60, text: 'Child3' },
            { angle: 60, text: 'Child4' },
            { angle: 60, text: 'Child5' }
        ]
    },
    {
        radius: 100,
        color: 'lightgrey',
        slices: [
            { angle: 120, text: 'Parent1' },
            { angle: 120, text: 'Parent2' },
            { angle: 120, text: 'Parent3' }
        ]
    }
];

現在、これは元のプロパティ名よりも長くなっており、すべてのangle:プロパティtext:名が含まれていますが、サーバーとブラウザーが使用する gzip 圧縮により、非常にうまく圧縮されます。

さらに重要なことは、コードを単純化して明確にし、エラーを回避するのに役立ちます。anglesGrandchildrengrandchildrenTextArrayが同じ長さではないことに気付きましたか? :-)

並列配列の代わりにオブジェクトの単一配列を使用すると、そのようなエラーを防ぐことができます。

このデータを使用するには、circleCreate()関数とその呼び出しを削除します。

circleCreate(anglesGrandchildren, 200, "grey", grandchildrenTextArray);
circleCreate(anglesChildren, 150, "darkgrey", childrenTextArray);
circleCreate(anglesParents, 100, "lightgrey", parentTextArray);

それらを次のように置き換えます。

function createRings( rings ) {
    var startAngle = 0, endAngle = 0,
        x = stage.getWidth() / 2,
        y = stage.getHeight() / 2;

    rings.forEach( function( ring ) {
        ring.slices.forEach( function( slice ) {
            startAngle = endAngle;
            endAngle = startAngle + slice.angle;
            var wedge = new Kinetic.Wedge({
                x: x,
                y: y,
                radius: ring.radius,
                angleDeg: slice.angle,
                fill: ring.color,
                stroke: 'black',
                strokeWidth: 1,
                rotationDeg: startAngle
            });
            wedge.on('click', function() {
                alert(slice.text);
            });
            layer.add(wedge);
        });
    });

    stage.add(layer);
}

createRings( rings );

このコードは元のコードよりも実際には短くはありませんが、詳細の一部がより明確になっています。角度とテキストが同じスライス オブジェクトに属していることが明確に示されていますslice.angle。元のコードでは、arrays は正しく一致する配列であり、互いに適切に並んでいます。slice.textvangle[i]vtext[i]vanglevtext

ループ.forEach()の代わりにも使用しました。forCanvas を使用しているため、最新のブラウザを使用していることがわかります。良い点の 1 つforEach()は、関数呼び出しを使用するため、自動的にクロージャーが提供されることです。

また、すべてのウェッジで同じであるため、ループの計算xとループの外に移動しました。y

これは、この更新されたコードとデータを使用した最新のフィドルです。

于 2013-06-13T16:59:19.230 に答える
1

問題はループ インデックスにあります。これを試して:

    (function(j) {
        wedge.on('click', function() {
            alert(vtext[j]);
        });
    })(i);

こちらをご覧ください

問題は、clickハンドラーが呼び出されると、iループの最後にあった値を持つため、vtext[i]明らかに未定義であることです。クロージャーでラップすることにより、ハンドラーのループが実行された時点でのループ インデックスの値を保存できます。click

于 2013-06-13T16:59:18.013 に答える