Fantasy football and Raphaël: the vector graphic JavaScript library





The new Premier League football season beckons and we’ve all got dreams of becoming the Alex Ferguson of the office by winning this season’s fantasy football competition. But convincing wannabe managers to join your online fantasy football site means your site must offer the best features.

Potential players expect to be able control every aspect of the game; like performing substitutions, transferring players and choosing the team colours. It’s the technology behind features that interests me. Within this blog post we’ll look at one technology that could be used when developing the feature to allow players to customise their team’s kit.

Let’s first look at the requirements for a kit customisation feature. The basics first! A player must be able to choose the shirt and shorts colours. It sounds simple, but, this selection alone could drive game logic. For example, if a player chooses the shade of red worn by the Red Devils should we apply bias akin to being guaranteed eight minutes overtime when losing at home? Or, if the shade of red matches that of the true kings of Europe from Anfield should we fix their maximum position to be the top of the mid-table? Joking aside the technology to dynamically draw a football kit within the context of a browser hasn’t been as accessible as one would assume. Historically, web developers would look to plugins such as Adobe’s Flash to achieve such tasks. Thankfully the day of the plugin has passed and we now have the graphical capabilities built into modern browsers. One such library called Raphaël.

Raphaël is a small JavaScript vector graphics library on the web. If you want to create your own specific chart or image crop and rotate widget, for example, you can achieve it simply and easily with this library. It’s fair to say Raphaël is not the only technology one could choose to slay this dragon. A suitable alternative would be HTML5’s Canvas. Enough chatter, let’s look at the implementation.

We’ll start by looking at the finished article:

Shirt: Stripes: Shorts: 

This is created with the following HTML:

<html>
	<head>
		<title>Football kit designer</title>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<link rel="stylesheet" type="text/css" href="spectrum.css" media="all" />
		<script src="jquery-1.9.1.min.js"></script>
		<script src="spectrum.js"></script>
		<script src="raphael-min.js"></script>
		<script src="kit.js"></script>
	</head>
	<body>
		Shirt:&nbsp;<input type="color" id="myColorFieldShirt" />
		Strips:&nbsp;<input type="color" id="myColorFieldStripes" />
		Shorts:&nbsp;<input type="color" id="myColorFieldShorts" />
	</body>
</html>

The above extract creates three input elements within the body of the document. These act as colour-pickers for the kit’s shirt, strip and shorts colour. The code for the colour pickers is beyond the scope of the article; but, for those interested the lines of interest are highlighted below:

<html>
	<head>
		<title>Football kit designer</title>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<link rel="stylesheet" type="text/css" href="spectrum.css" media="all" />
		<script src="jquery-1.9.1.min.js"></script>
		<script src="spectrum.js"></script>
		<script src="raphael-min.js"></script>
		<script src="kit.js"></script>
	</head>
	<body>
d		Shirt:&nbsp;<input type="color" id="myColorFieldShirt" />
		Strips:&nbsp;<input type="color" id="myColorFieldStripes" />
		Shorts:&nbsp;<input type="color" id="myColorFieldShorts" />
	</body>
</html>

The content within the body doesn’t reveal any clues as to how the kit is rendered. During the initial load a ‘load’ event is triggered. This results in the execution of custom JavaScript code. The code executed on the ‘load’ event is located with the “kit.js” file which is referenced in the head section:

<script type="text/javascript" src="kit.js"></script>

Before we drill into this code we first need to recognise the importance of the other two includes:

<script type="text/javascript" src="jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="raphael-min.js"></script>

These allow us to leverage the power of the popular JavaScript libraries JQuery and Raphaël. Getting back to the code that is executed on the ‘load’ event. Within the “kit.js” file we’ll find the following:

$(document).ready(function(){

	var paper = Raphael(10, 50, 400, 400),
		team = new Team(paper), i,
		pickers = {
			"items": [
				{'id': '#myColorFieldShirt', 'index': 0},
				{'id': '#myColorFieldShorts', 'index': 3},
				{'id': '#myColorFieldStripes', 'index': 4}
			]
		};

	team.prepareKit();

	for(i = 0; i < pickers.items.length ; i++) {
		prepareColorPickers(team,
			pickers.items[i].id,
			pickers.items[i].index);
	}

	team.drawKit();

});

The code can be dissected into four steps. The first step is variable declaration:


var paper = Raphael(10, 50, 400, 400),
	team = new Team(paper), i,
	pickers = {
		"items": [
			{'id': '#myColorFieldShirt', 'index': 0},
			{'id': '#myColorFieldShorts', 'index': 3},
			{'id': '#myColorFieldStripes', 'index': 4}
		]
	};

This creates an object that harnesses the Raphaël library which creates a canvas element within the page. We then create a Team object. The Team object’s code is also included within the “kit.js”. This object contains the code that utilises the Raphaël library to draw the kit. The final declaration is a JSON data-structure which is used to configure the colour-pickers.

The second part of the initialisation code is the called to the ‘preparekit’ method:

team.prepareKit();

This method is a defined within the Team object:

function Team(Paper) {

    var iShirt = 0,
        iLeftArm = 1,
        iRightArm = 2,
        iShorts = 3,
        iStripes = 4,
        iTrim = 5;

    this.kit = [],
    this.Paper = Paper,
    this.kitStyle = "stripes",
    this.prepareKit = function() {

        var leftArm, shirt, rightArm, shorts, stripes, hoops, trim,
            kitTopColor = "#DE172C",
            kitShortsColor = "#000",
            kitStripColor = "#00ff00";

        shirt = {
            path: "M59,16L59,112L140,112L143,18,L118,12,L99,38, L80,13,L60,15",
            stroke: "black",
            fill: kitTopColor
        };

        leftArm = {
            path: "M61,15L55,15L18,43L33,57L59,52",
            stroke: "black",
            fill: kitTopColor
        };

        rightArm = {
            path: "M142,18L150,21L165,31L174,38L179,43L164,57L142,52",
            stroke: "black",
            fill: kitTopColor
        };

        shorts = {
            path: "M61,117L58,128L52,150L45,187L90,195L99,171L108,195L156,186L151,170L145,140L138,117L61,117",
            stroke: "black",
            fill: kitShortsColor
        };

        stripes =  {
            path: "M68,15L68,111L80,111L80,14ZM93,35L93,111L105,111L105,36L96,42ZM117,14L117,111L129,111L129,14Z",

            stroke: kitStripColor,
            fill: kitStripColor
        };

        trim = {
            path: "M78,13L78,20,L100,47L119,20L119,13L99,38L80,13ZM18,42L33,57L27,58L13,45ZM178,43L164,57L170,58L180,48L178,42Z",

            stroke: "black",
            fill: kitShortsColor
        };

        this.kit.push(
            shirt, leftArm, rightArm, shorts, stripes, trim);
    },
    this.drawKit = function() {

        var counter = 0;

        for(counter; counter < this.kit.length; counter++) {

           this.Paper.path(
               this.kit[counter].path).attr(
                   this.kit[counter]);
        }

        this.Paper.text(130, 180, "12").attr({
            fill: "#000", "stroke-width": "1",
            stroke: "#fff"
        }).rotate(-10);

    },
    this.setShirtColor =  function(color) {

        this.kit[iShirt].fill = color.toHexString();

        this.kit[iStripes].fill = color.toHexString();
        this.kit[iStripes].stroke = color.toHexString();
        this.kit[iLeftArm].fill = color.toHexString();
        this.kit[iRightArm].fill = color.toHexString();

    },
    this.setStripeColor = function(color) {

        this.kit[iStripes].fill = color.toHexString();
        this.kit[iStripes].stroke = color.toHexString();
        this.kit[iLeftArm].fill = color.toHexString();
        this.kit[iRightArm].fill = color.toHexString();
    },
    this.setShortColor = function(color) {
        this.kit[iShorts].fill = color.toHexString();
    };
};

This object is fairly large; but, the content is quite simple. The prepareKit function simply sets-up objects that define the attributes required to draw the kit. For example the shirt is define as follows:

        shirt = {
            path: "M59,16L59,112L140,112L143,18,L118,12,L99,38, L80,13,L60,15",
            stroke: "black",
            fill: kitTopColor
        };

The most important part of the shirt object is the path property. This simply defines a polygon using SVG format. Rather than covering this in depth I’ll divert interested parties to the official reference. The other properties simply set the fill and line (stroke) colour of the polygon.

The third part of the initialisation links the colour-pickers to the Team object. Using the events built into the colour-pickers we can instruct the Team object to draw the kit using the new colours e.g.
football-kit-designer-javascript

The final part of the initialisation function is the call to the ‘drawKit’ function which is also part of the Team object:


    this.drawKit = function() {

        var counter = 0;

        for(counter; counter < this.kit.length; counter++) {

           this.Paper.path(
               this.kit[counter].path).attr(
                   this.kit[counter]);
        }

        this.Paper.text(130, 180, "12").attr({
            fill: "#000", "stroke-width": "1",
            stroke: "#fff"
        }).rotate(-10);

    }

This function simply iterates around the kit array which was created within the ‘prepareKit’ function. As discussed above this array includes objects that define how to draw the kit’s shirt, stripes and shorts. The object’s properties are passed as parameters to a function provided within the Raphaël library e.g.

           this.Paper.path(
               this.kit[counter].path).attr(
                   this.kit[counter]);

This takes the path, stroke and fill properties and uses Raphaël to draw the polygon for each of the objects within the array. Simple! Good luck with this year’s fantasy football!

P.s. Keep an eye out for the Mediademon free fantasy football league, we’ll post sign-up details in the near future.