Design, CG Graphics & Inspiration
LibCanvas Basics. The Theory

LibCanvas Basics. The Theory

One of the most frequently asked question about LibCanvas now is “How to get started?” I agree that the threshold of entering into this library is slightly higher than in the simple canvas-library, so in this topic I will cover the basic notions of LibCanvas, such general concepts and principles as drawing primitive shapes, mouse and keyboard events, animation, enhanced context and behavior.

I will try to describe all this with many examples and as simple as possible. I hope the article will provide answers to the questions: What is LibCanvas? Why do we need it and what are its advantages? Where to start? In this article I’ll share with you only a theory, and how to apply this knowledge in practice will be disclosed in the second part – LibCanvas Basics. The Practice.

Overview

LibCanvas is a framework for working with Canvas and related technologies, which can be used to develop games and other interactive applications. It is based on AtomJS – a lightweight JavaScript framework, a bit similar to MooTools and jQuery. There is a quite good English documentation of AtomJS and if you’ve previously used MooTools, it won’t be a problem for you to cope with AtomJS.

The latest version of LibCanvas can be obtained in a repository, GitHub also has a number of current examples from very simple to quite complex. Many of the principles can be understood by studying these examples.

Core

All the code is stored in the LibCanvas namespace. It is good that the library does not clutter the global namespace. Nevertheless, there is a disadvantage – rather verbose syntax in the end:

var circle = new LibCanvas.Shapes.Circle( 100, 100, 50 );
var circle = new LibCanvas.Shapes.Circle( 222, 222, 22 );

This can be corrected using the static method LibCanvas.extract (). It globalizes the LibCanvas itself, so that in your application you can use short class names:

LibCanvas.extract();

var circle = new Circle( 100, 100, 50 );
var circle = new Circle( 222, 222, 22 );

Another alternative is using the aliases:

var Circle = LibCanvas.Shapes.Circle;

var circle1 = new Circle( 100, 100, 50 );
var circle2 = new Circle( 222, 222, 22 );

LibCanvas.Context2D

There is a built-in LibCanvas context. It’s very easy to call it:

var context = document.getElementsByTagName('canvas')[0].getContext('2d-libcanvas');
// or:
var context = atom.dom('canvas').first.getContext('2d-libcanvas');

Please note that the original ‘2 d ‘context, still available and untouched, that’s why you can use it safely for your applications:

var context = atom.dom('canvas').first.getContext('2d');

‘2 D-libcanvas’ context is backward compatible with the original context (as all code written for the context of ‘2 d ‘will work in the context of the ‘2 d-libcanvas’), but it has the following advantages:

1. Chainable – all methods can be called with the help of a chain. This method became especially popular with the advent of jQuery:

context
	.set({
		  fillStyle: 'black',
		strokeStyle: 'red'
	})
	.  fillRect(20, 20, 40, 40)
	.strokeRect(50, 50, 100, 100);

2. Named arguments – now you can send not just a set of characters, but also a hash:

context.drawImage(img, 10, 15, 40, 45, 20, 25, 50, 55);
// vs
context.drawImage({
	image: img,
	crop : [10, 15, 40, 45],
	draw : [20, 25, 50, 50]
});

3. Shapes – you can transfer shapes, and not only numbers. This is particularly useful when you have an application with the created objects:

//draw image:
	context.drawImage( image, rect.from.x, rect.from.y, rect.width, rect.height );
	// vs
	context.drawImage( image, rect );

// Fill the rectangle with the persistence of the canvas:
	context.save();
	context.fillStyle = 'red';
	context.fillRect( rect.from.x, rect.from.y, rect.width, rect.height )
	context.restore();
	// vs:
	context.fill( rect, 'red' );

4. API Extension – there is an entire series of useful things. First, is more comfortable working with paths, text, images, transformations, etc.:

// Image with the center at a certain point,  rotated around the axis:
	// original ctx:
	context.save();
	context.translate(this.position.x, this.position.y);
	context.rotate(this.angle);
	context.translate(-this.image.width/2, -this.image.height/2);
	context.drawImage(this.image, 0, 0);
	context.restore();
	
	// vs
	context.drawImage({
		image : this.image,
		center: this.position,
		angle : this.angle
	});

// The text:
	context.text({
		text: 'Test string \n with line breaks \n is here'
		padding: [ 30, 50 ],
		size: 20,
		align: 'center'
	})

// Rotate the canvas around the axis:
	context.translate( point.x,  point.y);
	context.rotate(angle);
	context.translate(-point.x, -point.y);

	// vs:
	context.rotate( angle, point );

// Draw the path
	context.beginPath( );
	context.moveTo( mt.x, mt.y );
	context.lineTo( lt.x, lt.y );
	context.bezierCurveTo( bc1.x, bc1.y, bc2.x, bc2.y, bc.x, bc.y );
	context.quadraticCurveTo( qc1.x, qc1.y, qc.x, qc.y );
	context.closePath();
	
	// vs
	context
		.beginPath( mt )
		.lineTo( lt );
		.curveTo( bc, bc1, bc2 )
		.curveTo( qc, qc1 )
		.closePath();

// Clip the circle:
	var circle = new Circle( 130, 120, 50 );

	context.beginPath();
	context.arc( circle.center.x, circle.center.y, circle.radius, 0, Math.PI * 2 );
	context.closePath();
	context.clip();
	
	// vs:
	context.clip( circle );
	
// Clear the canvas:
	context.clear( 0, 0, canvas.width, canvas.height );
	// vs
	context.clearAll();

And so on. I think you can see yourself the convenience of the built-in context.

LibCanvas Object

When designing LibCanvas, an object LibCanvas.Canvas2D is created. With the first argument you should give a reference to a necessary canvas element (css-selector, dom-object, etc). And with the second you can give some additional settings – fps limiting, clearing before drawing, preload images, and others.

var libcanvas = new LibCanvas('#my-canvas');

libcanvas instanceof LibCanvas; // true
libcanvas instanceof LibCanvas.Canvas2D; // true

// it is possible to get the extended context in the property:
libcanvas.ctx instanceof LibCanvas.Context2D; // true

Each frame consists of two stages. First stage is data rendering. It runs every time and is solely responsible for the mathematical operations – moving objects, collisions, etc. In this layer, there shouldn’t be any redrawing. The second stage is the render. It has the part that is responsible for redrawing the entire screen and it will be executed only in case of any changes on the screen. This can be indicated on the stage of calculation by calling the libcanvas.update () method .

To add a function to the calculation stage is possible by means of the libcanvas.addFunc () method, and adding a function to the render stage can be implemented by using the libcanvas.addRender () method. Also, during the rendering stage the draw methods of the passed objects are called. The code looks like this:

libcanvas
	.addFunc(function () {
		scene.recount();
		if (scene.somethingChanged()) {
			libcanvas.update();
		}
	})
	.addRender(function () {
		// will be called only after calling libcanvas.update();
		scene.drawAll();
	});

A lot of applications are static most of the time, with the redrawing only in moments of user’s actions. This will significantly reduce the load of the processor.

In practice addRender is used rarely, because it’s very convenient to draw the objects by the draw () method (see below).

Always redraw something on the screen only with the presence of changes. In many applications, this basic mechanism is not enough, but it’s better than nothing.

Point

LibCanvas.Point is one of the basic objects. It is used very often,it is a component of all shapes and it’s very easy to it use out of them. It has methods for determining the distances between two points, angle, by multiplying the points and getting the all neighbors.

// // Rotate point A by 60 degrees around the point B:
	var A = new Point(10, 10),
	    B = new Point(20, 20);

	A.rotate( (60).degree(), B );

// count the sum of all neighbors of cells in the matrix:
	var sum = 0 +
		matrix[p.y-1][p.x-1] + matrix[p.y-1][p.x] + matrix[p.y-1][p.x+1] +
		matrix[p.y  ][p.x-1] +                      matrix[p.y  ][p.x+1] +
		matrix[p.y+1][p.x-1] + matrix[p.y+1][p.x] + matrix[p.y+1][p.x+1] ;

	// vs
	var sum = point.neighbours.reduce(function(value, p) { return value + matrix[p.y][p.x]; }, 0);

Shapes

The shapes are contained in the subspace of LibCanvas.Shapes names.* and being globalized till the short aliases. The best-known shapes are Rectangle, Circle and Line. When using LibCanvas you should be aware that the shapes themselves do not have the external appearance – the color or shade. The object that uses the shape is responsible for the appearence, such as LibCanvas.Ui.Shaper, and theshapes themselves contain only mathematical operations – like going all the path, intersections, and if a point is inside of the shape, etc. They are the astral, but not the physical body.

This allows us to separate the behavior from the appearance. For example, we have a platform in arkanoid. In fact it is a picture, but we can perform all the actions as with a simple shape:

var Unit = atom.Class({
	initialize: function (rectangle, image) {
		this.shape = rectangle;
		this.image = image;
	},
	collision: function (anotherUnit) {
		return this.shape.intersect( anotherUnit.shape );
	},
	draw: function () {
		this.libcanvas.ctx.drawImage( this.image, this.shape );
	}
});

Rectangle is the most important shape. It is not only used when drawing rectangles, and for basic math operations, but also in many other LibCanvas methods. It may be, for example, the method context.drawImage, which accepts the arguments for cutting and drawing a rectangle or a tile engine, in which every element is a small Rectangle.

When any method requires Rectangle-like argument – it can accept any argument, similar to a rectangle. For example:

context.drawImage({
	image: image,
	crop: {
		from: { x: 15, y: 10 },
		size: { width: 50, height: 100 }
	},
	draw: [10,20,100,200]
});

In this case, crop and draw will be given within a Rectangle (or other appropriate shape), but in terms of performance (for multiple redrawing of the canvas), as well as in terms of application architecture – the best method – is the creation of objects during the application initialization.

var Item = atom.Class({
	initialize: function (image) {
		this.image = image;
		this.cropRect = new Rectangle(15, 10,  50, 100);
		this.drawRect = new Rectangle(10, 20, 100, 200);
	},
	draw: function () {
		context.drawImage({
			image: this.image,
			crop : this.cropRect,
			draw : this.drawRect
		});
	}
});

The other shapes are used similarly:

// Arc:
context.arc({
	circle: new Circle( 100, 100, 50 ),
	angle : [ (45).degree(), (135).degree() ]
});

// Line:
context.stroke( new Line([13, 13], [42, 42]), 'red' );

Behaviors

The next part is LibCanvas.Behaviors .*. Each of them is just an impurity, which adds some functionality or behavior to your class. For example, Animatable adds the animate method that allows you to change the object’s properties smoothly, and Drawable allows objects of your class to be added for the drawing to the LibCanvas object.
Incidentally, Drawable is the basis of drawing in LibCanvas. A mixture of Drawable and Shapes .* will let you draw any shape on the canvas, and adding other behaviors will give the shape some additional functionality.

var Item = atom.Class({
	Implements: [ Drawable, Draggable ],
	initialize: function (shape) {
		this.shape = shape;
	},
	draw: function () {
		this.libcanvas.ctx.stroke( this.shape, 'red' );
	}
});

libcanvas.addElement(
	new Item( new Rectangle(50, 50, 100, 100) ).draggable()
);

In fact such pattern for drawing shapes had to be created very often, that’s why it is already implemented Ui.Shaper:

libcanvas.createShaper({
	shape : new Rectangle(50, 50, 100, 100),
	stroke: 'red'
}).draggable();

Keyboard and Mouse

Working with the keyboard is quite simple. It’s quite enough to call libcanvas.listenKeyboard () for application initialization, and if it is necessary you can use the libcanvas.getKey (keyName) to see the key status:

update: function () {
	if( this.libcanvas.getKey('aup') ) {
		this.move();
	}
}

Working with the mouse stands apart. First, if you want to use the mouse in your application then you should always call the libcanvas.listenMouse (). With the aim of optimization, the mouse events are not evaluated before its call, because there are applications which do not need a mouse. You can now easily subscribe to the mouse events, adding an element to an object Mouse:

this.libcanvas.mouse.subscribe( element );

It is important for one of the shapes to be the property value of the shape element (LibCanvas.Shapes .*), zIndex property realised the class atom.Class.Events. In practice, this is all hidden behind the behaviors and when you call, for example, the draggable () method of Draggable behavior, the object is automatically subscribed to the mouse events. If you only need to listen to the mouse events, it is sufficient to implement the MouseListener behavior and call the listenMouse method of the item. However, we still have the most important point – the item should have the Shape property with a figure inside. When there are responses from the mouse events at your object, you can subscribe to any of the following events:

/*
	- click
	- mouseover
	- mousemove
	- mouseout
	- mouseup
	- mousedown
	- away:mouseover
	- away:mousemove
	- away:mouseout
	- away:mouseup
	- away:mousedown
*/ // For example:

element
	.listenMouse()
	.addEvent('click', function () {
		alert('element clicked');
	});

Conclusion

Here I’ve described the basics of theoretical development on LibCanvas. It lacks a lot of interesting features and principles, but its purpose – to explain the ideology and to show the reader how to begin working with it.

You might also be interested in..

HTML5 Canvas: Frequently Asked Questions
Creating a Framework For Canvas: Objects and Mouse
Fifteen puzzle in LibCanvas
20 Free HTML5 Games
How to create a Progress bar with HTML5 Canvas
Meet The Future – HTML5 Demos

SHARE THIS POST

Pavel is 21 year old web developer from Ukraine. In his spare time he writes articles about the basics of LibCanvas, AtomJS and JavaScript

Subscribe for the hottest posts

Subscribe to our email newsletter for useful tips and freebies.