Using SVG paths in canvas to move objects

If to animate an object on canvas (and not only), you need to move it along some desired path, maybe even several, which can be chosen randomly or sequentially, then this can be done using svg paths. Let's start by starting a simple but green square along the path.

image


To do this, we make or borrow svg, with one or more paths.

Create an element using the document.createElementNS function . MDN informs us that the method has basic support in all modern browsers. Then add the path to the created element.

let path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute('d', 'M148.185,118.975c0,0-92.592,39.507-80.247,79.013,s79.012,143.21,129.629,124.691s64.198-113.856,120.988-100.755s118.518,30.384,116.049,109.397s-82.715,118.519-97.53,201.235,s-92.593,139.505,0,159.259');

Here, in the attributes, the first path came to my eyes from some svg file, copy-paste. Of course, this is not the only and moreover, not the most convenient way, but it is quite visual for use in the first example.

Now in the loop, we will get the coordinates of the waypoints and assign them to our object. For this we need two SVGGeometryElement methods :

path.getTotalLength() 

returns the calculated value of the total path length and

 path.getPointAtLength(index)

Gets the float argument number, and returns the SVGPoint object which has, we are interested, x and y coordinates. For argument values ​​less than zero or greater than the length of the path, the first or last points will be returned as a result, respectively.

When updating the frame, we get a point and use its coordinates for movement.

→ The complete code of the example in the codepen.

But, you can use a more interesting option to move the object along the coordinates of several paths, for example:

image


Again, take a svg file with several paths. The one that was used in the example is made in the Inscape editor. Now we need to get these paths, it is possible through parsing the object or, if svg was received as a text file, then the following function, using regular expressions, can be obtained as strings.

extractPathsfromSvg: function(svg){
        let results = svg.match(/<path\b([\s\S]*?)><\/path>/g);
        let paths = [];
        let len = results.length;
        for(let i = 0; i < len; i++){
            let str = results[i];
            let data = str.match(/[^\w]d="([\s\S]*?)"/);
            paths.push(data[1]);
        }
        return paths;
    }

After creating an array of paths, it remains to extract them in turn and process them in the same way as the only path for the movement of the square. getting interesting effects.
See the full example code below.

To add more control when the object moves along the coordinates of the path, you can use tweens. For the test cases, I took the first GreenSock library that came across, but it could be any other.

In the first case, when the square moves along a single path, we will create an intermediate helper object, and pass it on when creating the twin.

var helper = {progress: 0}
helper.update = function(value){
  point = path.getPointAtLength(totalLength * helper.progress);
  x = point.x;
  y = point.y;
  ctx.clearRect(0, 0, canvas.width, canvas.height); 
  ctx.drawImage(img, x, y );
}
var tw = new TweenLite.to(helper, 5, {progress: 0, });
tw.eventCallback("onUpdate", helper.update);

You can see the movement of the square along the path using tween, in the first example on the codepen, you can put a tick use tween.

When driving in several ways, we proceed as follows. As before, we will create a helper object with the progress property. We calculate the total length of all paths, and assign it to handler.progress. Let's create a traversed variable in which the paths already traversed will be summed up.

To get a point on the current path, subtract from helper.progress, which is changing in tween, the path already traveled is traversed. Use the coordinates of the point as usual.

var traversed = 0;
helper.progress = totalLenghtAllPath;
helper.update = function() {
      var localPoint = helper.progress - traversed;
       if(localPoint > curPath.getTotalLength()){
            traversed += curPath.getTotalLength();
            curPath = paths[next()];
            if(curPath){
                returnfalse;
            }
            localPoint = helper.progress - traversed;
        }
     /* код которому нужны координаты точки пути */
}
var tw = TweenLite.to(
        helper, 
         25, 
        {progress: totalLenghtAllPath, ease: Power2.easeOut }
);      
tw.eventCallback("onUpdate", helper.update); 

The code is simplified, the full code is here:


Also popular now: