これは、イベント ハンドラーなどの非同期 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
関数の内部は外部の関数と同じではないことに注意してください。これが混乱を招く場合は、別の名前を付けても問題ありません。)i
circleCreate()
更新されたフィドル
より良い方法
とはいえ、データを構造化する別のアプローチをお勧めします。あなたのコードを読んでいるときに、角度とテキスト配列が私の目を引きました:
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 圧縮により、非常にうまく圧縮されます。
さらに重要なことは、コードを単純化して明確にし、エラーを回避するのに役立ちます。anglesGrandchildren
とgrandchildrenTextArray
が同じ長さではないことに気付きましたか? :-)
並列配列の代わりにオブジェクトの単一配列を使用すると、そのようなエラーを防ぐことができます。
このデータを使用するには、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.text
vangle[i]
vtext[i]
vangle
vtext
ループ.forEach()
の代わりにも使用しました。for
Canvas を使用しているため、最新のブラウザを使用していることがわかります。良い点の 1 つforEach()
は、関数呼び出しを使用するため、自動的にクロージャーが提供されることです。
また、すべてのウェッジで同じであるため、ループの計算x
とループの外に移動しました。y
これは、この更新されたコードとデータを使用した最新のフィドルです。