这是在处理异步JavaScript代码(如事件处理程序)时会遇到的典型问题。在您的
circleCreate()
函数中,
for
循环使用一个变量
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
的值。如您所知,每个函数都可以有自己的本地变量/参数,而
addWedge()
中的
i
是该函数的局部变量-特别是每个单独的函数调用的局部变量。(请注意,因为
addWedge()
是自己的函数,所以该函数内部的
i
与外部的
circleCreate()
函数中的
i
不同。如果这让人困惑,将其命名为其他名称即可。)
更新后的代码片段
更好的方法
我建议采用不同的方法来构建您的数据结构。当我阅读您的代码时,注意到了角度和文本数组:
var anglesParents = [120, 120, 120];
var parentTextArray = ['Parent1', 'Parent2', 'Parent3'];
对于子代和孙代,有类似但更长的数组对。
您可以使用这些数组中的值与circleCreate()
中的vtext[i]
和vangle[i]
引用一起使用。
通常情况下,除非有特定原因要使用这种并行数组,否则如果将它们合并为单个对象数组,则代码将变得更加清晰:
[
{ angle: 120, text: 'Parent1' },
{ angle: 120, text: 'Parent2' },
{ angle: 120, text: 'Parent3' }
]
对于您的嵌套圆形,我们可以进一步将所有三个环组合成一个大型对象数组,描述整个嵌套环集。在这里,您有这些数组:
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` 和 `slice.text` 明确表明角度和文本属于同一个扇区对象,在原始代码中,我们只能希望 `vangle` 和 `vtext` 数组是正确匹配且彼此正确对齐的。
我还使用了 `forEach()` 而不是 `for` 循环;由于您正在使用 Canvas,因此我们知道您正在使用现代浏览器。一个好处是 `forEach()` 使用函数调用,因此它自动为您提供闭包。
此外,我将 `x` 和 `y` 的计算移到了循环外部,因为它们对于每个楔形图都是相同的。
这是带有更新后的代码和数据的
最新 fiddle。