如何添加点击以形成循环?

时间:2013-06-13 16:49:08

标签: javascript canvas kineticjs

我将文本数组传递给我的 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);
}

3 个答案:

答案 0 :(得分: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]为{{1} }}

你可以通过简单地为每个循环迭代调用一个函数来轻松解决这个问题:

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() 与外部{{1}中的i不同如果这很令人困惑,可以给它一个不同的名字。)

Updated fiddle

更好的方法

这就是说,我建议采用不同的方法来构建数据。当我阅读你的代码时,角度和文本数组引起了我的注意:

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' } ] } ]; 属性名称都有,但这些内容与服务器和浏览器使用的gzip压缩非常吻合。

更重要的是,它有助于简化和澄清代码并避免错误。您是否注意到angle:text:的长度不一样? : - )

使用单个对象数组而不是并行数组可以防止出现这样的错误。

要使用此数据,请删除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.angleslice.text的情况下,我们希望vangle[i]vtext[i]数组是正确的匹配数组并且彼此正确排列。

我还使用了vangle而不是vtext循环;因为您使用的是Canvas,我们知道您使用的是现代浏览器。一个好处是.forEach()使用函数调用,因此它会自动为您提供闭包。

此外,我将forforEach()的计算移到了循环之外,因为它们对于每个楔形都是相同的。

以下是包含此更新代码和数据的latest fiddle

答案 1 :(得分: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);
}

答案 2 :(得分:1)

你的问题在于你的循环索引。试试这个:

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

请参阅here

问题在于,当click处理程序被调用时,i具有循环结束时的值,因此vtext[i]显然是未定义的。通过将其包装在闭包中,您可以在click处理程序的循环运行时保存循环索引的值。