SVG Library in JavaScript

I have already written a couple of small SVG libraries, one in C and one in Python. However, the most useful and widespread use of the SVG format is within web pages so in this post I will begin to develop a JavaScript SVG library.

This first version has quite modest ambitions: just the ability to draw a few simple shapes as well as text. However, SVG is far more capable than most people realise so I will add further functionality in the future with the aim of creating a far more comprehensive library.

What is SVG?

Before actually starting to cut code I'll give a brief overview of SVG, as it perhaps isn't as well know as, say, JPEG or PNG. Obviously, if you already understand SVG skip the rest of this section.

Image formats such as JPEG and PNG are bitmap or raster images: they consist of just rows and columns of pixels and are best suited to photographs or complex computer-generated artworks. SVG is completely different as it is a vector graphics format (SVG stands for Scalable Vector Graphics). Instead of pixels, the image is made up of individual elements such as shapes (rectangles, circles etc.), lines, text and so on. This makes it more suitable for simpler images such as graphs, diagrams or logos. SVG is XML based and therefore contains a hierarchy of human-readable tags. Just to give a flavour the following will draw a blue circle with a 2-pixel black border. The circle's radius is 32 pixels, and the coordinates of the centre are 128, 128.

SVG example

<circle stroke='black' stroke-width='2px' fill='blue' r='32' cy='128' cx='128' />

SVG can be used in two ways. The first way is to embed it in an HTML page, where the individual elements form part of the DOM and can therefore be manipulated by JavaScript (eg for animations), and any text in the image can be read by search engines. This is what we will be doing with this library.

The second way is to create a stand-alone file. This can be put in an image tag on a page but cannot then be scripted or picked up by search engines.

The Plan

For this project I will create a class with a number of static methods to create or otherwise manipulate an SVG image. These methods are:

  • fill
  • line
  • rectangle
  • circle
  • ellipse
  • text
  • clear

Start Coding!

The project consists the following two JavaScript files, as well as an HTML file, a CSS file and a banner graphic.

  • svglibrary.js
  • svglibrary_demo.js

The files can be downloaded as a zip file, or you can clone or download the Github repo.

Source Code Links

ZIP File
GitHub

This is the source code for svglibrary.js.

svglibrary.js

class SVGLibrary
{
    static clear(id)
    {
        document.getElementById(id).innerHTML = "";
    }


    static circle(id, strokeColor, strokeWidth, fillColor, r, cx, cy)
    {
        const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");

        circle.setAttributeNS(null, "cx", cx);
        circle.setAttributeNS(null, "cy", cy);
        circle.setAttributeNS(null, "r", r);
        circle.setAttributeNS(null, "stroke", strokeColor);
        circle.setAttributeNS(null, "fill", fillColor);
        circle.setAttributeNS(null, "stroke-width", strokeWidth);

        document.getElementById(id).appendChild(circle);
      }


    static fill(id, color)
    {
        document.getElementById(id).style.backgroundColor = color;
    }


    static line(id, strokeColor, strokeWidth, x1, y1, x2, y2)
    {
        const line = document.createElementNS("http://www.w3.org/2000/svg", "line");

        line.setAttributeNS(null, "x1", x1);
        line.setAttributeNS(null, "y1", y1);
        line.setAttributeNS(null, "x2", x2);
        line.setAttributeNS(null, "y2", y2);
        line.setAttributeNS(null, "stroke", strokeColor);
        line.setAttributeNS(null, "stroke-width", strokeWidth);

        document.getElementById(id).appendChild(line);
    }


    static ellipse(id, cx, cy, rx, ry, fillColor, strokeColor, strokeWidth)
    {
        const ellipse = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");

        ellipse.setAttributeNS(null, "cx", cx);
        ellipse.setAttributeNS(null, "cy", cy);
        ellipse.setAttributeNS(null, "rx", rx);
        ellipse.setAttributeNS(null, "ry", ry);
        ellipse.setAttributeNS(null, "stroke", strokeColor);
        ellipse.setAttributeNS(null, "fill", fillColor);
        ellipse.setAttributeNS(null, "stroke-width", strokeWidth);

        document.getElementById(id).appendChild(ellipse);
    }


    static rectangle(id, w, h, x, y, fillColor, strokeColor, strokeWidth)
    {
        const rect = document.createElementNS("http://www.w3.org/2000/svg", 'rect');

        rect.setAttributeNS(null, 'x', x);
        rect.setAttributeNS(null, 'y', y);
        rect.setAttributeNS(null, 'height', h);
        rect.setAttributeNS(null, 'width', w);
        rect.setAttributeNS(null, 'fill', fillColor);
        rect.setAttributeNS(null, 'stroke', strokeColor);
        rect.setAttributeNS(null, 'stroke-width', strokeWidth);

        document.getElementById(id).appendChild(rect);
    }


    static text(id, x, y, fontFamily, fontSize, fillColor, strokeColor, text)
    {
        const XLabel = document.createElementNS("http://www.w3.org/2000/svg", "text");

        XLabel.setAttributeNS(null, "x", x);
        XLabel.setAttributeNS(null, "y", y);
        XLabel.setAttributeNS(null, "fill", fillColor);
        XLabel.setAttributeNS(null, "font-family", fontFamily);
        XLabel.setAttributeNS(null, "font-size", fontSize);

        const textNode = document.createTextNode(text);
        XLabel.appendChild(textNode);
        document.getElementById(id).appendChild(XLabel);
    }
}

The library assumes the existence of one or more SVG elements on the page, each with an id attribute. This id is passed as an argument to each of the functions; the class therefore has no state and merely works on the element passed to each method.

fill

Here we simply set the background color to the required value.

line, rectangle, circle, ellipse

These methods all work in the same way: they create an element of the appropriate type, set its attributes from the method arguments, and then add the element as a child of the parent SVG.

text

This method is slightly more complex as it needs to create a text element with the supplied attributes, and also a text node to embed within that element.

clear

This method simply empties the SVG element.

The following file contains a few functions to try out the library.

svglibrary_demo.js

window.onload = function()
{
    const w = document.getElementById("svgdiv").offsetWidth;
    const h = document.getElementById("svgdiv").offsetHeight;

    SVGLibrary.fill("svg1", randomColor());

    const functions = [randomRectangle, randomEllipse, randomCircle];

    const shapeCount = randomBetween(128, 256);

    for(let i = 0; i < shapeCount; i++)
    {
        functions[randomBetween(0, 2)](w, h);
    }
}


function randomBetween(min, max)
{
    return Math.floor(Math.random() * (max - min + 1) + min);
}


function randomColor()
{
    const r = randomBetween(0, 255);
    const g = randomBetween(0, 255);
    const b = randomBetween(0, 255);

    return `rgb(${r}, ${g}, ${b})`;
}


function randomRectangle(w, h)
{
    const fillColor = randomColor();
    const strokeColor = randomColor();
    const width = randomBetween(16, 64);
    const height = randomBetween(16, 64);
    const x  = randomBetween(0, w);
    const y  = randomBetween(0, h);

    SVGLibrary.rectangle("svg1", width, height, x, y, fillColor, strokeColor, 1);
}


function randomEllipse(w, h)
{
    const fillColor = randomColor();
    const strokeColor = randomColor();
    const rx = randomBetween(8, 32);
    const ry = randomBetween(8, 32);
    const cx  = randomBetween(0, w);
    const cy  = randomBetween(0, h);

    SVGLibrary.ellipse("svg1", cx, cy, rx, ry, fillColor, strokeColor, 1);
}


function randomCircle(w, h)
{
    const fillColor = randomColor();
    const strokeColor = randomColor();
    const r = randomBetween(8, 32);
    const cx  = randomBetween(0, w);
    const cy  = randomBetween(0, h);

    SVGLibrary.circle("svg1", strokeColor, 1,  fillColor, r, cx, cy);
}

window.onload

The purpose of this function is to draw a random number of rectangles, ellipses and circles of random sizes, in random positions and in random colours.

Firstly we pick up the width and height of the SVG element, and then fill the drawing ie. set the background colour. The three relevant functions are added to an array and we then initialize a variable to a random amount specifying the number of shapes to draw.

Finally within a loop we call one of these functions at random.

randomBetween

The previous section had a record number of the word "random" and to streamline things I have included a short function to return a random integer between two values.

randomColor

Here the above function is put to use to return a random number in a format suitable for use in SVG.

randomRectangle, randomEllipse and randomCircle

These three functions generate random values for the arguments to their respective SVG library methods before calling the methods.

Open svglibrary.htm in your browser. Welcome to the Psychedelic Sixties.