Drawing an SVG file Back

This document is to talk about how to draw an SVG file with canvas in JavaScript.

Preview

Just drag and drop an SVG file onto the box shown below with a dark read border:

Read data from an SVG file

Before drawing an SVG file, you need to read data from an SVG file with FileReader in JavaScript.

var fileReader = new FileReader();

FileReader is a Web API, which has given some methods for you to read a file.readAsText is one of methods, supported by this Web API, for reading file with its contents, and onload is the event triggered when a file has been read:

fileReader.onload = function (e) {
    /** contents of an SVG file */
    var contents = e.target.result;
};

fileReader.readAsText(file);

Of course, if you would like to complete a interaction of dragging and dropping a file into a canvas to read an SVG file, you can set up an event handler for listening to the drop event of this canvas like this:

/** Drop Event Handler */
canvas.addEventListener('drop', function (e) {
    /** e is where we can extract out the `file` objecj */
    var file = e.dataTransfer.files[0];
})

Find out what we can draw

In an SVG file, many kinds of tags has been defined for rendering vector shapes like circle, rect, polyline, line, path, etc. path elements are actually what we are going to draw, because any shape/primitive in an SVG is able to be converted into path. Later posts will mention how to do this in JavaScript.

Since we are going to draw paths, data of these paths in an SVG file should be extracted firstly:

var paths = contents.match(/<path([\s\S]+?)\/>/g);

Generally, the attribute d is represented as data of a path. To make it useful, I have converted this data into a legal DOM, SVGPathElement.

if (paths) {
    var pathNodes = [];
    var pathLen = paths.length;

    for (var i = 0; i < pathLen; i++) {
        /** create a legal DOM node, SVGPathElement */
        var pathNode = document.createElementNS('http://www.w3.org/2000/svg', 'path');

        /** use a temporary div elements for reading the attribute `d` */
        var tmpDiv = document.createElement('div');
        tmpDiv.innerHTML = path[i];

        /** set it into the legal one */
        pathNode.setAttribute('d', tmpDiv.childNodes[0]
            .getAttribute('d')
            .trim()
            .split('\n').join('')
            .split('    ').join('')
        );

        /** store it into an array */
        pathNodes.push(pathNode);
    }
}

Extract points to draw

If we have a legal SVG path element, we can call getPointAtLength() to extract points to draw:

var pointsArr = [];
var pathLen = pathNodes.length;

for (var j = 0; j < pathLen; j++) {
    var index = pointsArr[].push([]);
    var pointsLen = pathNodes[j].getTotalLength();

    for (var k = 0; k < pointsLen; k++) {
        /** extract points from a path */
        pointsArr[index].push(pathNodes[j].getPointAtLength(k));
    }
}

With positions data of points, you an draw them with canvas like this:

/** draw the path with a given index */
function drawPath(index) {
    var ctx = canvas.getContext('2d');
    ctx.beginPath();

    /** set path */
    ctx.moveTo(pointsArr[index][0].x, pointsArr[index][0].y);

    for (var i = 1; i < pointsArr[index].length; i++) {
        ctx.lineTo(pointsArr[index][i].x, pointsArr[index][i].y);
    }

    /** render */
    ctx.stroke();
}

Calibration parameters

Something wrong has been talked about in a later post.

results matching ""

    No results matching ""