这是您在使用异步 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]
is 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' }
]
对于您的嵌套环,我们可以更进一步,将所有三个环组合成一个大型对象数组,描述整个嵌套环集。你有这些数组的地方:
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[i]
,vtext[i]
我们希望vangle
和vtext
数组是正确匹配的数组,并且彼此正确排列。
我也使用.forEach()
了而不是for
循环;由于您使用的是 Canvas,我们知道您使用的是现代浏览器。一件好事是forEach()
使用函数调用,因此它会自动为您提供一个闭包。
此外,我将计算移到循环之外x
,y
因为它们对于每个楔形都是相同的。
这是这个更新的代码和数据的最新小提琴。