0

I'm building a web-app using Two.js which generates shapes and then removes them again after their lifespan is up.

When making the shapes I push them into a new array which I then loop through every frame to check if the lifespan is up. If it is then the shape is removed and spliced from the array.

This works 99% of the time, but sometimes a shape does not get removed from the stage, even though it gets removed from the array. So it gets stuck and there is no reference to it so I can't remove it.

Also if I remove the 'if shape' check in the loop I get this error a lot: Uncaught TypeError: Cannot read property 'creationTime' of undefined which I'm sure means something is not right.

onPeak: (col) =>
    circle = @_two.makeCircle @_two.width, @_two.height, @_two.height*0.75
    circle.fill = col
    circle.lifeSpan = Math.floor @convertToRange(@_bpm, [60,600], [1000, 400])
    circle.creationTime = new Date().getTime()
    circle.noStroke()
    @_shapes.push circle


onTwoUpdate: () =>
    if @_shapes.length >= 1
        @removeShapes() 


removeShapes: () =>
    time = new Date().getTime()
    for shape in @_shapes
        if shape and time - shape.creationTime >= shape.lifeSpan
            shape.remove()
            @_shapes.splice shape.index, 1
4

1 回答 1

3

You're removing things from @_shapes while you're looping over it. Consider a simplified example:

a = [0..3]
for e in a
    if(e % 2 == 0)
        a.splice(e, 1)
    console.log(e, JSON.stringify(a))

That will give you this in the console:

0 "[1,2,3]"
2 "[1,2]"
undefined "[1,2]"
undefined "[1,2]"

Demo: http://jsfiddle.net/ambiguous/9fsYL/

You'll notice that things start out fine but as soon as you splice the array to remove an element, everything falls apart in a pile of nonsense. The undefineds appear in the console because the loop caches the array's length (which changes when remove things) so you end up running off the end of the array.

When you @_shapes.splice shape.index, 1, you shift everything after shape.index towards the beginning of the array but your for loop doesn't know that. The result is that you've removed the desired element and the loop won't see the next element.

A common solution to this problem is to iterate backwards. That way, when you remove something, you only affect the positions of things that you've already seen. For example, if you change the above loop to:

for e in a by -1

then you'll get something sensible in the console:

3 "[0,1,2,3]"
2 "[0,1,3]"
1 "[0,1,3]"
0 "[1,3]"

Demo: http://jsfiddle.net/ambiguous/Yngk8/

In your case, saying:

for shape in @_shapes by -1

should fix the problem.

于 2014-06-11T07:18:51.133 回答