简体   繁体   中英

Fabric.js animate object/image

Hello everyone this is code that ive been working with. It moves an object from A to B. What i am trying to do is have multiple points to which it would move. So from starting position A -> B, then from B -> C and so on. Maybe have some variable which would contain sets of coordinates, which would get fed into some animate function as parameters. But anything i tried resulted only in executing the last command, so it would move straight from A -> C, ignoring B. Any advice/demo appreciated.

Javascript

var canvas = new fabric.Canvas('scene', { backgroundColor: 'green'});

var triangle = new fabric.Triangle({
    width: 30
  , height: 30
  , fill: 'red'
  , left: 30
  , top: 0
});

canvas.add(triangle);

function animateTop() {
  triangle.animate('top', '+=55', {
    duration: 1000,
    onChange: canvas.renderAll.bind(canvas)
  });
}

function animateLeft() {
  triangle.animate('left', '+=100', {
    duration: 1000,
    onChange: canvas.renderAll.bind(canvas)
  });
}

function fireAnimation() {
  animateTop();
  animateLeft();
}

document.onreadystatechange = function () {
  if (document.readyState == "complete") {
    fireAnimation();
  }
}

HTML

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js"></script>
</head>
<body>
  <canvas id="scene" width="400" height="400" />
</body>
</html>

The problem you're facing is that the animation functions don't block each other, so when your fireAnimation() function runs, it's starting both animations at the same time, and each animation change is applied on each frame, which means that both animations occur simultaneously.

One way to resolve this would be to chain the animation calls using the animation .onComplete event. Doing this might lock you into a bit of a hellish situation if you need to update the order of your animations.

Given that you proposed perhaps having a list of animations to apply one after another you may be served better by creating a generic function that applies animations based on an input list of animation parameters like this (explanation in comments in code):

function startAnimationQueue(animationQueue){
    // queues up the animations for the shape

    var runningDuration = 0; // variable that adds up the animationQueue durations
    for (var i=0; i<animationQueue.length; i++){
        var animationDefinition = animationQueue[i];

        // Create a closure around the animationDefiniton so that each setTimeout gets sequential animationDefinition inputs
        var fn = (function(animationDefinition){
            return function(){
                triangle.animate('left', animationDefinition.left, {duration: animationDefinition.duration, onChange:canvas.renderAll.bind(canvas)})
                triangle.animate('top', animationDefinition.top, {duration: animationDefinition.duration, onChange:canvas.renderAll.bind(canvas)})
                // Note: you can animate additional attributes here if you want, just add additional attributes to the objects in
                //   the animationQueue list. You could also have one of those inputs be the object to be animated in case you want
                //   to animate multiple objects using the same queue.
                };
            })

        // Create the timeout that will apply the transformations sequentially
        // If you want you could set the window.setTimeout to a variable that you could destroy if you need 
        // to interrupt the animation queue after you create it (but before it finishes)
        window.setTimeout(fn(animationDefinition), runningDuration);

        // set the next setTimeout duration to be .duration later than the previous one
        // this makes the second animation fire after the first one completes
        runningDuration += animationDefinition.duration;
    }
}

document.onreadystatechange = function () {
  if (document.readyState == "complete") {

    // I put the canvas init stuff in here because of (I think) a failed race condition or something that caused
    // your original method to fail in Chrome
    window.canvas = new fabric.Canvas('scene');

    window.triangle = new fabric.Triangle({
        width: 30
      , height: 30
      , fill: 'red'
      , left: 30
      , top: 0
    });

    window.canvas.add(window.triangle);
    window.canvas.renderAll();

    // Create a list of animations to apply
    var animationQueue = [
        {"left": "+=0", "top": "+=100", "duration": 1000},
        {"left": "+=55", "top": "+=0", "duration": 2000}

    ]

    // Apply the animations in sequence using window.setTimeout
    startAnimationQueue(animationQueue);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM