2

I am using eaeljs and tweenjs to animate multiple lines getting drawn from a position to a position through a point, making it a curved line with an img attached to the front of the line. I am using the tween plugin MotionGuidePlugin for this. This is supposed to happen inside a init()-function. No mouse-event or user-based event triggers this.

My problem is that my canvas gets really slow after some time. The lines get more "pixel-ly", and the animation skips a lot of the frames - creating a straight line instead of a curve. I have researched this problem on Stack Overflow, and have found out that this is because a new Graphics are drawn for every frame. The solution seems to be to clear the Graphics or cache it, but I do not know how to implement this with my current code:

createjs.MotionGuidePlugin.install();

var shape = new createjs.Shape();
var bar = { x: arrowStartPosX, y: arrowStartPosY, oldx: arrowStartPosX, oldy: arrowStartPosY };
stage.addChild(shape);

var image = new createjs.Bitmap("arrow.png");
image.alpha = 0;
stage.addChild(image);

createjs.Ticker.addEventListener("tick", tick);

run();

function getMotionPathFromPoints (points) {
    console.log(points);
    var i, motionPath;
    console.log(points.length);
    for (i = 0, motionPath = []; i < points.length - 1; ++i) {
        if (i === 0) {
            motionPath.push(points[i].x, points[i].y);
        }
        else if(i === 1){
            motionPath.push(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y);
        } else {
            i++;
            motionPath.push(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y);
        }
    }
    return motionPath;
}

function run() {
    var posXStart = arrowStartPosX + 200;
    var points = [
        new createjs.Point(arrowStartPosX, arrowStartPosY),
        new createjs.Point(posXStart, middlePosY),
        new createjs.Point(arrowStartPosX, arrowEndPosY)
    ];

    createjs.Tween.get(bar).wait(timeCount-400).to({
        guide: {path: getMotionPathFromPoints(points)}
    }, 1900);

    createjs.Tween.get(image).to({rotation: -70}, 0)
    .wait(timeCount-400).to({alpha: 1}, 0)
    .to({rotation: -290, 
    guide:{ path:[startImgPosX, startImgPosY, middleImgPosX, middleImgPosY, endImgPosX, endImgPosY], 
    orient: "auto"}},1900);
}

function tick() {
    shape.graphics
    .setStrokeStyle(2, 'round', 'round')
    .beginStroke("#000000")
    .curveTo(bar.oldx, bar.oldy, bar.x, bar.y)
    .endStroke();

    bar.oldx = bar.x;
    bar.oldy = bar.y;
}

The lines should be drawn, and when finished, they should stay in place. Can anyone explain to me how to fix my code, and make the Canvas perform normally again?

Update:

I have now created a FIDDLE for my problem. If you do nothing in the fiddle for some time, you'll see that the JSFiddle webpage gets slower, as my own webpage does.

  • Can you post this as a Fiddle or similar? – gskinner Feb 15 '16 at 19:23
  • 1
    Sure, here: [Fiddle](http://jsfiddle.net/kristinannabel/km89jbn2/). If you do nothing for a while in the fiddle, you'll see that the JSFiddle webpage gets really slow, as does my webpage – Kristin Annabel Feb 16 '16 at 10:21

1 Answers1

3

Vectors can be very expensive, and your demo shows that you are adding a ton of small lines. Every time you update the stage, the graphics have to be redrawn. You won't be able to do this effect for long without caching, especially on a low-powered device.

Depending on your end goal, you can cache the shape, then draw in the new content as it gets created.

// Cache up front
var shape = new createjs.Shape();
shape.cache(0,0,100,1000);

// Clear the graphics before drawing new content
shape.graphics.clear()
    .setStrokeStyle(3, 'round', 'round')
    .beginStroke("#aaa")
    .curveTo(bar.oldx, bar.oldy, bar.x, bar.y)
    .endStroke();

// Update the cache with "source-over" to just add the new content.
    shape.updateCache("source-over");
    stage.update();

Here is a fiddle update: http://jsfiddle.net/lannymcnie/km89jbn2/2/

This approach will never slow down.

Cheers.

Lanny
  • 11,244
  • 1
  • 22
  • 30
  • Thanks for the answer and Fiddle! I tried this out in my code, but it still slowed down. It might be because of a lot of other canvas specific code. Adding the stage.update() makes the page go drastically slower... – Kristin Annabel Feb 17 '16 at 16:28
  • Does your app slow down without this code entirely? If this approach has no effect, then it is either caused by something else, or your implementation is not correct. Do you have additional `stage.update()` calls? Try and reduce those to as few as possible. Usually I set up a single tick event listener that updates the stage. If you want to conditionally update it, you can just set a boolean flag that operations which require a stage update toggle when required. – Lanny Feb 17 '16 at 18:25
  • So, I made it much quicker by adding a if-statement inside the tick-event, so the graphics would stop when the animation is complete. This, and removing all stage.update() from the code makes it much faster. But at some point, when enough lines are drawn, it slowly gets slower. I do not know if I have some kind of memory leak or something. And no, my code does not slow down if I remove this code entirely, so it must be something with the line draw and/or animation. – Kristin Annabel Feb 17 '16 at 18:55
  • If you see it getting slower over time with Graphics, that is usually an indication that the instructions are piling up. The cache approach I mentioned should work. If you turn off the cache/updateCache call, it should show the new line segments, which indicates what is being drawn each tick: http://jsfiddle.net/lannymcnie/km89jbn2/3/ – Lanny Feb 17 '16 at 20:44