Javascript 如何将“单击”添加到环形中的形状?
我将文本数组传递给我的circleCreate函数,该函数为每个文本创建一个楔子。我想做的是在每个楔子上添加一个点击事件,因此当用户点击楔子时,它会对每个楔子文本发出警报 但它不起作用。只有外圆是警告文本。它总是说同样的文字。两个内圆都未定义Javascript 如何将“单击”添加到环形中的形状?,javascript,canvas,kineticjs,Javascript,Canvas,Kineticjs,我将文本数组传递给我的circleCreate函数,该函数为每个文本创建一个楔子。我想做的是在每个楔子上添加一个点击事件,因此当用户点击楔子时,它会对每个楔子文本发出警报 但它不起作用。只有外圆是警告文本。它总是说同样的文字。两个内圆都未定义 var层=新的动能层(); 函数循环创建(vangle、vradius、vcolor、vtext){ startAngle=0; 端角=0; 对于(变量i=0;i
var层=新的动能层();
函数循环创建(vangle、vradius、vcolor、vtext){
startAngle=0;
端角=0;
对于(变量i=0;i
您的问题在于循环索引。试试这个:
(function(j) {
wedge.on('click', function() {
alert(vtext[j]);
});
})(i);
看
问题是当调用
click
处理程序时,i
具有循环结束时的值,因此vtext[i]
显然是未定义的。通过将其包装在闭包中,您可以在为单击处理程序运行循环时保存循环索引的值。这是异步JavaScript代码(如事件处理程序)遇到的典型问题。circleCreate()
函数中的for
循环使用一个变量i
,它为每个楔块递增该变量。使用i
创建楔块时,这很好:
angleDeg: vangle[i],
但如果在单击事件处理程序中使用它,则会失败:
alert(vtext[i]);
为什么呢
使用new Kinetic.wedge()
调用创建楔子时,这是直接在循环内部完成的。此代码同步运行;它使用i
的值,因为它在循环的这个特定迭代运行时存在
但是,单击
事件处理程序此时不运行。如果您从未单击,它可能根本不会运行。当您确实单击一个楔子时,它的事件处理程序将在原始循环完成运行很久之后被调用
那么,当事件处理程序运行时,i
的值是多少?它是代码最初运行时留在其中的任何值。当i
等于vangle.length
时,
的for
循环退出-换句话说,i
超过了数组的末尾,因此vangle[i]
是未定义的
您可以通过闭包轻松解决此问题,只需为每个循环迭代调用一个函数:
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);
}
}
对于孩子和孙子,也有类似但更长的数组对
将这些数组中的值与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' }
]
}
];
现在这比原来的要长,因为所有的角度:
和文本:
属性名,但是这些东西通过服务器和浏览器使用的gzip压缩非常好地压缩了
更重要的是,它有助于简化和澄清代码并避免错误。您是否注意到您的anglesGrandchildren
和孙子extarray
长度不一样?:-)
使用单个对象数组而不是并行数组可以防止这样的错误
要使用此数据,请删除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
清楚地表明角度和文本属于同一个slice对象,其中与原来的vangle[i]
和vtext[i]
我们希望vangle
和vtext
数组是正确的匹配数组,并且彼此正确对齐
我还使用了.forEach()
而不是来执行循环;由于您使用的是Canvas,我们知道您使用的是现代浏览器。一件好事是forEach()
使用函数调用,因此它会自动为您提供一个闭包
此外,我将x
和y
的计算移到循环之外,因为它们对于每个楔块都是相同的
下面是更新后的代码和数据的示例。因为在每次循环迭代中定义为事件处理程序的每个匿名函数将共享相同的作用域,所以每个函数将引用相同的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);
}
var层=新的动能层();
函数循环创建(vangle、vradius、vcolor、vtext){
startAngle=0;
端角=0;
对于(变量i=0;ifunction 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 );
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);
}