Search code examples
javascriptsvgsvg-animatesvg.js

Create animation to make object move at different speeds along a continous path in svg.js


The fiddle here which is a SVG.js tutorial moves a square back and forth in a spiral.

    var canvas = SVG('drawing').size('100%', '100%').viewbox(0,0,800,1000)
      , rect = canvas.rect(100, 100)
      , path = canvas.path("m 357.64532,453.84097 c 17.62007,8.02216 -2.12058,27.70935 -13.33334,29.28571 -30.3859,4.27185 -48.34602,-29.97426 -45.23807,-55.9524 5.5594,-46.46879 56.1311,-70.59787 98.57145,-61.19043 62.28294,13.8058 93.32728,82.57702 77.1428,141.19051 C 453.21679,585.29693 365.67122,623.42358 290.97859,600.26951 196.98554,571.13248 151.71003,464.56996 181.93108,373.84089 218.53281,263.95583 344.23687,211.49702 450.97875,248.84102 576.77037,292.84963 636.43303,437.76771 591.93099,560.50775 540.55162,702.21597 376.3736,769.09583 237.6452,717.41234 80.01319,658.68628 5.9069261,475.21736 64.788247,320.50751 130.84419,146.94643 333.62587,65.607117 504.31214,131.69819 693.80625,205.0718 782.38357,427.18225 709.07382,613.84113")
      , length = path.length()
      
    path.fill('none').stroke({width:1, color: '#000'})
    
    rect.animate(8000, '<>').during(function(pos, morph, eased){
        var p = path.pointAt(eased * length)
        rect.center(p.x, p.y)
    }).loop(true, true)
html, body, #drawing{
  width:100%;
  height:100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.4/svg.min.js"></script>
<div id="drawing"></div>

My requirement is to make the square move slower first (move 0.65*path_length for a large animation time) and next make the square move faster in the rest of the path (move 0.35*path_length for a smaller animation time)

You can find the new fiddle below, which doesn't work as expected.

var canvas = SVG('drawing').size('100%', '100%').viewbox(0,0,800,1000)
  , rect = canvas.rect(100, 100)
  , path = canvas.path("m 357.64532,453.84097 c 17.62007,8.02216 -2.12058,27.70935 -13.33334,29.28571 -30.3859,4.27185 -48.34602,-29.97426 -45.23807,-55.9524 5.5594,-46.46879 56.1311,-70.59787 98.57145,-61.19043 62.28294,13.8058 93.32728,82.57702 77.1428,141.19051 C 453.21679,585.29693 365.67122,623.42358 290.97859,600.26951 196.98554,571.13248 151.71003,464.56996 181.93108,373.84089 218.53281,263.95583 344.23687,211.49702 450.97875,248.84102 576.77037,292.84963 636.43303,437.76771 591.93099,560.50775 540.55162,702.21597 376.3736,769.09583 237.6452,717.41234 80.01319,658.68628 5.9069261,475.21736 64.788247,320.50751 130.84419,146.94643 333.62587,65.607117 504.31214,131.69819 693.80625,205.0718 782.38357,427.18225 709.07382,613.84113")
  , length = path.length()
 
path.fill('none').stroke({width:1, color: '#000'})

rect.animate(8000, '<>').during(function(pos, morph, eased){
    length1 = 0.65*length
    var p = path.pointAt(eased * length1)
    rect.center(p.x, p.y)
}).animate(2000, '<>').during(function(pos, morph, eased){
    length2 = 0.35*length
    var p = path.pointAt(eased * length2)
    rect.center(p.x, p.y)
})
html, body, #drawing{
  width:100%;
  height:100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.4/svg.min.js"></script>
<div id="drawing"></div>

The problem is that the second animate call starts the square at position 0,0 instead of the point where the square stops after the first animate call.

Using the move function as shown below, to position the square at the updated location (final location of the previous animation) also doesn't have an effect in changing the position of the square.

var canvas = SVG('drawing').size('100%', '100%').viewbox(0,0,800,1000)
  , rect = canvas.rect(100, 100)
  , path = canvas.path("m 357.64532,453.84097 c 17.62007,8.02216 -2.12058,27.70935 -13.33334,29.28571 -30.3859,4.27185 -48.34602,-29.97426 -45.23807,-55.9524 5.5594,-46.46879 56.1311,-70.59787 98.57145,-61.19043 62.28294,13.8058 93.32728,82.57702 77.1428,141.19051 C 453.21679,585.29693 365.67122,623.42358 290.97859,600.26951 196.98554,571.13248 151.71003,464.56996 181.93108,373.84089 218.53281,263.95583 344.23687,211.49702 450.97875,248.84102 576.77037,292.84963 636.43303,437.76771 591.93099,560.50775 540.55162,702.21597 376.3736,769.09583 237.6452,717.41234 80.01319,658.68628 5.9069261,475.21736 64.788247,320.50751 130.84419,146.94643 333.62587,65.607117 504.31214,131.69819 693.80625,205.0718 782.38357,427.18225 709.07382,613.84113")
  , length = path.length()
 
path.fill('none').stroke({width:1, color: '#000'})

rect.animate(8000, '<>').during(function(pos, morph, eased){
    length1 = 0.65*length
    var p = path.pointAt(eased * length1)
    rect.center(p.x, p.y)
}).after(function(){
	rect.move(rect.transform().x, rect.transform().y)
}).animate(2000, '<>').during(function(pos, morph, eased){
	length2 = 0.35*length
    var p = path.pointAt(eased * length2)
    rect.center(p.x, p.y)
})
html, body, #drawing{
  width:100%;
  height:100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.4/svg.min.js"></script>
<div id="drawing"></div>

Is it possible to have the object move in two different speeds along a continuous path?


Solution

  • Here's one way:

    You need to map 'pos' values from the range 0 to 1, to the new two-part range you desire.

    • Map values in the range (0 -> 0.8) to the range (0 -> 0.65)
    • Map values in the range (0.8 -> 1.0) to the range (0.65 -> 1.0)

    var canvas = SVG('drawing').size('100%', '100%').viewbox(0,0,800,1000)
      , rect = canvas.rect(100, 100)
      , path = canvas.path("m 357.64532,453.84097 c 17.62007,8.02216 -2.12058,27.70935 -13.33334,29.28571 -30.3859,4.27185 -48.34602,-29.97426 -45.23807,-55.9524 5.5594,-46.46879 56.1311,-70.59787 98.57145,-61.19043 62.28294,13.8058 93.32728,82.57702 77.1428,141.19051 C 453.21679,585.29693 365.67122,623.42358 290.97859,600.26951 196.98554,571.13248 151.71003,464.56996 181.93108,373.84089 218.53281,263.95583 344.23687,211.49702 450.97875,248.84102 576.77037,292.84963 636.43303,437.76771 591.93099,560.50775 540.55162,702.21597 376.3736,769.09583 237.6452,717.41234 80.01319,658.68628 5.9069261,475.21736 64.788247,320.50751 130.84419,146.94643 333.62587,65.607117 504.31214,131.69819 693.80625,205.0718 782.38357,427.18225 709.07382,613.84113")
      , length = path.length()
     
    path.fill('none').stroke({width:1, color: '#000'})
    
    rect.animate(10000, '<>').during(function(pos, morph, eased) {
      // Note: we are using the eased position ('eased') rather than the linear position ('pos')
      if (eased <= 0.8) {
        // Convert pos in the range (0 -> 0.8) to the range (0 -> 0.65)
        var adjustedPos = eased * 0.65 / 0.8;
      } else {
        // Convert pos in the range (0.8 -> 1.0) to the range (0.65 -> 1.0)
        var adjustedPos = 0.65 + ((eased - 0.8) * 0.35 / 0.2);
      }
      var p = path.pointAt(adjustedPos * length)
      rect.center(p.x, p.y)
    });
    html, body, #drawing{
      width:100%;
      height:100%;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.4/svg.min.js"></script>
    <div id="drawing"></div>