Animate drawing of path on HTML5 canvas

假如想象 提交于 2019-11-29 16:51:01

Here is a potential answer (special thanks to @markov00 re same technique in SVG). It works by manipulating the path dashOffset and dash attributes. There is an excellent explanation of the technique here in a post by Jake Archibald which also includes an interactive experiment with sliders which I found very useful.

I have tried to make the demo as lightweight as possible and just show the technique - though I added a slider and some UI to help understand the process. The use of jquery is only for the UI parts which are not needed for the technique.

Couple of points:

  • The demo here uses a path from 3 straight line segments. But I tried curves and combination paths and the technique works in those cases too - so any path should work.
  • I found that using a close-path command (z) on the path caused the path length function to be short on the true distance. This appears as an amount of the path remaining stroked or gapped at either end, with the size depending on the jump between first & last to close the path.
  • The path length is virtually always going to be decimal so don't try to do everything as integers as you will ultimately find your stroke is slightly overlong or short.

To adopt this for animation and easing etc, take the couple of lines from the slider change event and stick them inside the frame callback, manipulating the maths to suit your case.

// Set up the canvas / stage
var stage = new Konva.Stage({container: 'container1', width: 320, height: 180});

// Add a layer
var layer = new Konva.Layer({draggable: false});
stage.add(layer);

// show where the start of the path is.
var circle = new Konva.Circle({
  x: 66,
  y: 15,
  radius: 5,
  stroke: 'red'
 })
 layer.add(circle);

// draw a path.
    var path = new Konva.Path({
      x: 0,
      y: 0,
      data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
      stroke: 'green'
    });

// get the path length and set this as the dash and dashOffset. 
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);

layer.add(path)
stage.draw();

// Some UI bits
$('#dist').attr('max', parseInt(pathLen)); // set slider max to length of path
$('#pathLen').html('Path : ' + pathLen); // display path length

// jquery event listener on slider change
$('#dist').on('input', function(){

  // compute the new dash lenth as original path length - current slider value. 
  // Means that dashLen initially = path len and moves toward zero as slider val increases.
  var dashLen = pathLen - $(this).val();;
  path.dashOffset(dashLen);   // set new value
  layer.draw();               // refresh the layer to see effect

  // update the UI elements      
  $('#dashLen').html('Dash: ' + dashLen);
  $('#pathPC').html(parseInt(100-(100 * (dashLen/pathLen)), 10) + '%');

})
.info 
{
padding-left: 20px;

}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div class="slidecontainer">
  <input class='slider' id='dist' type="range" min="0" max="100" value="0" class="slider" id="myRange"/>
  <span class='info' id='pathPC'></span>
  <span class='info' id='pathLen'></span>
  <span class='info' id='dashLen'></span>
</div>
<div id='container1' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>

My solution with animation:

    var width = window.innerWidth;
    var height = window.innerHeight;

    // Set up the canvas / stage
    var stage = new Konva.Stage({
        container: 'container',
        width: width,
        height: height
    });

    // Add a layer
    var layer = new Konva.Layer({
        draggable: false
    });
    stage.add(layer);

    // show where the start of the path is.
    var circle = new Konva.Circle({
        x: 66,
        y: 15,
        radius: 5,
        stroke: 'red'
    })
    layer.add(circle);

    // draw a path.
    var path = new Konva.Path({
        x: 0,
        y: 0,
        data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
        stroke: 'green'
    });

    // get the path length and set this as the dash and dashOffset. 
    var pathLen = path.getLength();
    path.dashOffset(pathLen);
    path.dash([pathLen]);

    // make some animation with stop
    var anim = new Konva.Animation(function (frame) {
        var dashLen = pathLen - frame.time / 5;
        path.dashOffset(dashLen);
        if (dashLen < 0) {
            anim.stop();
        }
    }, layer);

    anim.start();

    layer.add(path)
    stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>

And here is an alternative to @Roxane's animated version but using a tween.

var width = window.innerWidth;
    var height = window.innerHeight;

    // Set up the canvas / stage
    var stage = new Konva.Stage({
        container: 'container',
        width: width,
        height: height
    });

    // Add a layer
    var layer = new Konva.Layer({
        draggable: false
    });
    stage.add(layer);

    // show where the start of the path is.
    var circle = new Konva.Circle({
        x: 66,
        y: 15,
        radius: 5,
        stroke: 'red'
    })
    layer.add(circle);

    // draw a path.
    var path = new Konva.Path({
        x: 0,
        y: 0,
        data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
        stroke: 'green'
    });

    // get the path length and set this as the dash and dashOffset. 
    var pathLen = path.getLength();
    path.dashOffset(pathLen);
    path.dash([pathLen]);

    layer.add(path);  // have to add to layer for tweening.
    
    // create the tween
    var tween = new Konva.Tween({
        node: path,
        dashOffset: 0,
        easing: Konva.Easings['BounceEaseOut'],
        duration: 1.5
    });
     tween.play(); // execute the tween

    stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!