LudoJS SVG Essentials

LudoJS comes with a SVG library which makes it easy to create an manipulate SVG elements.

In this coding practice session, we're going to

  • Get a reference to the SVG of a ludoJS View
  • Add a lot of circle elements to it
  • Animate those circles

What we will end up with is this.

Code start

Start with an HTML page where you include the Javascript and CSS required for LudoJS:

<html>
<head>
    <title>LudoJS SVG</title>
    <script type="text/javascript" src="api/jquery/jquery-3.1.0.min.js"></script>
    <script type="text/javascript" src="api/js/ludojs-minified.js"></script>
    <link rel="stylesheet" type="text/css" href="api/css/ludojs-all.css"/>

    <script type="text/javascript">
    // Our code will go here
    </script>
</head>
<body class="ludo-twilight">

</body>
</html>

Create a basic view

Now, create a basic view and render it to the <body> tag.

Add this code inside the <script> tag of head

$( document ).ready(function() {
    var v = new ludo.View({
        renderTo:document.body,
        layout:{
            width:'matchParent', height:'matchParent'
        }

    });

});

This will create a ludoJS View which will span the entire viewport of the <body> tag. The view will get it's styling from the twilight theme since the <body> tag has been assigned to the css class ludo-twilight.

Get a reference to the SVG Surface

To get a reference to the SVG element of a view, make a call to the getCanvas method.

This method will create an SVG element if it doesn't exists and append it to the ludoJS View. The SVG element will be of the same size as the parent View and will be resized automatically when the view is being resized.

So let's add the following code:

var svg = v.getCanvas();

We now have a reference to the SVG drawing surface.

Create SVG elements

We will now create a lot of <circle> elements and add them to the SVG. The circles will be of different size and color, and we will position them randomly.

To get a reference to the circles later, we will put them into an array. For the colors, we will create an instance of ludo.color.Color and use the randomColor method to generate random colors.

Let us create the array and an instance of the Color class:

var colorUtil = new ludo.color.Color();
var circles = [];

As mentioned, the circles will be positioned randomly inside the view. max position will be the width and height of the svg:

var maxX = svg.width;
var maxY = svg.height;

A View in LudoJS is made out of two nested divs. The SVG surface is appended to the inner div which we can get a reference to with getBody. So that's what we use to determine maxX and maxY.

Create SVG Circles

We are now going to create 30 <circle> elements and append them to the SVG. We do that with a for-loop:

for(var i=0;i<30;i++){

    var circle = c.$('circle', {
        r: Math.round(Math.random() * 20) + 3,
        fill: colorUtil.randomColor()
    });
    svg.append(circle);
    circles.push(circle);
}

To create an SVG node, we use the SVG dollar function. This functions is used to creates a new

ludo.svg.Node element. ludo.svg.Node in ludoJS is a wrapper for SVG DOM elements. It provides methods for easy manipulation of the elements properties, css and animations.

The $ function takes two arguments. The first one is a string for the tag name, example: circle, rect, path. The second one is an object with attributes which should be set on this SVG element. In our example, we set radius and a random fill color.

The radius will be a random value between 3 and 23.

After creating the <circle> element, we append it to the svg element and add it the circles array.

If you open your web page now, you should see all the circles on top of each other in the top left corner. The reason for that is that we haven't positioned them yet.

SVG nodes have attributes for positioning. Circles can be positioned with cx and cy(center x and center y). Rectangles(rect) can be positioned with with x and y and so on. If these values are not set, the will be assumed 0.

We are not going to set these attributes. Instead we are going to use the translate methods. The reason for this is that the translate methods are the same for all elements. We don't have to choose between x and cx, y and cy.

In LudoJS, we have two translate methods translate and setTranslate. Both methods takes two arguments, x and y.

circle.setTranslate(50,100);

is the same as setting cx to 50 and cy to 100 for the circle. the setTranslate method is used for absolute positioning, meaning that it will set the translate values directly. translate is for relative positioning, i.e. x and y will added to existing x and y.

circle.translate(25,40);
circle.translate(25,60);

is the same as circle.setTranslate(50,100). Most of the times, you will be using setTranslate.

Position our circle

Now, let's position our circles using the setTranslate method. x and y should be somewhere between 0 and max.

Inside the loop, add the following code:

circle.setTranslate(
    Math.round(Math.random() * maxX),
    Math.round(Math.random() * maxY)
);

If you have followed me so far, you should see a lot of circle elements positioned randomly on your page.

This is the code I have now if you want to make a comparison:

<html>
<head>
    <title>LudoJS SVG</title>
    <script type="text/javascript" src="api/jquery/jquery-3.1.0.min.js"></script>
    <script type="text/javascript" src="api/js/ludojs-minified.js"></script>
    <link rel="stylesheet" type="text/css" href="api/css/ludojs-all.css"/>

    <script type="text/javascript">
        $( document ).ready(function() {
            var v = new ludo.View({
                renderTo:document.body,
                layout:{
                    width:'matchParent', height:'matchParent'
                }

            });

            var svg = v.getCanvas();
            var colorUtil = new ludo.color.Color();
            var circles = [];

            var maxX = svg.width;
            var maxY = svg.height;

            for(var i=0;i<30;i++){

                var circle = svg.$('circle', {
                    r: Math.round(Math.random() * 20) + 3,
                    fill: colorUtil.randomColor()
                });
                circles.push(circle);
                svg.append(circle);

                circle.setTranslate(
                        Math.round(Math.random() * maxX),
                        Math.round(Math.random() * maxY)
                );
            }
        });

    </script>
</head>
<body class="ludo-twilight">

</body>
</html>

SVG Animation

SVG Elements in LudoJS has an animate method. This method is similar to the animate functions in jQuery.

It takes two arguments:

1) Properties to animate, example: radius and translate values for the circle. 2) Options for the animation, example: duration, easing, function to call on complete.

What we are going to to do is to create a function which takes a circle as argument and executes the animation on this circle. We are going to pass all circles to this function so that all circles will be animated simultaneously.

Below the for loop, add this:

function animateCircle(circle){

}
jQuery.each(circles, function(i, circle){
    animateCircle(circle);
});

the animateCircle function should find a new radius and coordinates for the circle and start the animation.

Add the following code to the animateCircle function.

circle.animate({
    translate:[Math.round(Math.random() * maxX), Math.round(Math.random() * maxY)],
    r: Math.round(Math.random() * 40) + 3

});

This code tells the node to animate translate and r(radius). translate requires an array with x and y. radius will be animated to a value between 3 and 43.

Note that when we're animating translate, the animate function will call the setTranslate method of the node, i.e. it will update the translated x and y directly and not add to existing x and y. If you want relative translation, you should animated it like this:

translate:["+50", "+100"]

i.e. pass a "+" or "-" sign before the translated x and y values.

If you refresh your page now, you should see all your circles being animated one time.

Animation: complete function

What we want is to restart the animation once it's completed. To do this, we need to specify a complete function for the options object of the animation. In our code above, we did not create any option object, so default options was used.

the complete function shold call animateCircle with the circle as argument.

One way to do this is to put create a new function and store it a variable

var fn = function(){
    animatedCircle(circle);
}

Then we set the option.complete property to fn

function animateCircle(circle){

    var fn = function(){
        animateCircle(circle);
    };
    circle.animate({
        translate:[Math.round(Math.random() * maxX),Math.round(Math.random() * maxY)],
        r: Math.round(Math.random() * 40) + 3

    },{
        complete:fn
    });
}

You should now have continuously animations.

Once we're looking at the animation's options object, let us change the easing and the duration of the animation:

circle.animate({
    translate:[Math.round(Math.random() * maxX),Math.round(Math.random() * maxY)],
    r: Math.round(Math.random() * 40) + 3

},{
    complete:fn,
    easing: ludo.svg.easing.outCubic,
    duration: (Math.random() * 1000) + 800
});

Here, the duration is randoom between 0.8 seconds and 1.8 seconds, and the easing is changed from the default outSine to outCubic.

If you want an overview of the easing methods, I suggest you take a look at the SVG Animation demo. It demonstrates how the different easing algorithms works in practice.

As a final touch, let us modify the fn function so that it restarts the animation after a random delay.

var fn = function(){
    animateCircle.delay(Math.round(Math.random() * 2000) + 300, this, circle)
};

This will restart the animation after a random delay from 0.3 to 2.3 seconds. (The delay function is from Mootools and is a convenient alternative to setTimeout)

My final code now looks like this:

<html>
<head>
    <title>LudoJS SVG</title>
    <script type="text/javascript" src="api/jquery/jquery-3.1.0.min.js"></script>
    <script type="text/javascript" src="api/js/ludojs-minified.js"></script>
    <link rel="stylesheet" type="text/css" href="api/css/ludojs-all.css"/>

    <script type="text/javascript">
        $( document ).ready(function() {
            var v = new ludo.View({
                renderTo:document.body,
                layout:{
                    width:'matchParent', height:'matchParent'
                }

            });

            var svg = v.getCanvas();
            var colorUtil = new ludo.color.Color();
            var circles = [];

            var maxX = svg.width;
            var maxY = svg.height;

            for(var i=0;i<30;i++){

                var circle = svg.$('circle', {
                    r: Math.round(Math.random() * 20) + 3,
                    fill: colorUtil.randomColor()
                });
                circles.push(circle);
                svg.append(circle);

                circle.setTranslate(
                        Math.round(Math.random() * maxX),
                        Math.round(Math.random() * maxY)
                );
            }

            function animateCircle(circle){

                // in case the view has been resized.
                var maxX = svg.width;
                var maxY = svg.height;

                var fn = function(){
                    animateCircle.delay(Math.round(Math.random() * 2000) + 300, this, circle)
                };

                // Put radius in a variable and use the variable for x and y to make sure that
                // the circles are not partially animated out of view
                var r = Math.round(Math.random() * 40) + 3;

                circle.animate({
                    translate:[
                        r + Math.round(Math.random() * (maxX-(r *2))),
                        r + Math.round(Math.random() * (maxY-(r *2)))
                    ],
                    r: r

                },{
                    complete:fn,
                    easing: ludo.svg.easing.outCubic,
                    duration: (Math.random() * 1000) + 800
                });
            }

            jQuery.each(circles, function(i, circle){
                animateCircle(circle);
            });
        });

    </script>
</head>
<body class="ludo-twilight">

</body>
</html>

Exercise

  • Modify the code to use different shapes like rect, polygon and ellipsis.
Generated 2016-12-22 13:09:07