HTML5 Canvas Navigation menu with Fire

Have you ever thought about creating some interactive navigation menu in HTML5, directly at canvas element? Yes, this is strange idea, but it is quite possible that similar ideas are attended you. So, I made up my mind to prepare our first pure html5 canvas menu (basically – this is some set of buttons). We will create these buttons with a fire affect at the bottom. And you will be able to set custom click actions for menu elements.

Now you can test prepared demonstration, and download the sources.

Live Demo
download in package

Preview

HTML5 Canvas Menu - Preview

Ok, download the example files and lets start coding togetger!


Step 1. HTML Markup

Basically, our HTML markup is very easy today:

01 <!DOCTYPE html>
02 <html lang="en" >
03     <head>
04         <meta charset="utf-8" />
05         <title>HTML5 Canvas Navigation menu with Fire | Script Tutorials</title>
06         <link rel="stylesheet" href="css/main.css" type="text/css" />
07         <script src="js/vector2d.js"></script>
08         <script src="js/fire_menu.js"></script>
09     </head>
10     <body>
11         <canvas id="panel" width="1000px" height="100px">HTML5 compliant browser required</canvas>
12         <img id="image" src="images/bg.jpg"  />
13     </body>
14 </html>

There is only single canvas element. Plus – one image directly after this canvas.

Step 2. JS

Now, we should create a new empty file ‘js/fire_menu.js’ and put next code inside:

001 // Button object
002 function Button(x, y, w, h, state, image, text) {
003     this.x = x;
004     this.y = y;
005     this.w = w;
006     this.h = h;
007     this.state = state;
008     this.imageShift = 0;
009     this.image = image;
010     this.text = text;
011 }
012 // Draw Button function
013 function drawButton(ctx, button) {
014     // draw button image
015     ctx.drawImage(button.image, 0, button.imageShift, button.w, button.h, button.x, button.y, button.w, button.h);
016     // and text
017     ctx.fillText(button.text, button.x + button.w / 2, 5 + button.y + button.h / 2);
018 }
019 // Get mouse position function
020 function getMousePosition(e){
021     if (!e){
022         var e = window.event;
023     }
024     if (e.pageX || e.pageY){
025         return new vector2d(e.pageX, e.pageY);
026     else if (e.clientX || e.clientY){
027         return new vector2d(e.clientX, e.clientY);
028     }
029 }
030 // Inner variables
031 var canvas, ctx;
032 var data_width;
033 var data_height;
034 var colors = [];
035 var out_data = [];
036 var buttons = [];
037 // Fill new array with certain value
038 function fill_new_array(len, val) {
039     var rv = new Array(len);
040     while (--len >= 0) {
041         rv[len] = val;
042     }
043     return rv;
044 }
045 // Prepare palette function
046 function prepare_palette() {
047     for (var i = 0; i < 64; ++i) {
048         colors[i + 0] = {r: 0, g: 0, b: i << 1, a: i};
049         colors[i + 64] = {r: i << 3, g: 0, b: 128 - (i << 2), a: i+64};
050         colors[i + 128] = {r: 255, g: i << 1, b: 0, a: i+128};
051         colors[i + 192] = {r: 255, g: 255, b: i << 2, a: i+192};
052     }
053 }
054 // Draw Main scene
055 function drawScene() {
056     ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // clear canvas
057     // Draw fire
058     var data_cnt = data_width * (data_height - 1);
059     for (var i = 0; i < data_width; i++) {
060         out_data[data_cnt + i] = (0.6 > Math.random()) ? 255 : 20;
061     }
062     for (var y = 0; y < 100; y++){
063         for (var x = 10; x < data_width - 10; x++){
064             var s = data_cnt + x;
065             var temp_data = out_data[s] + out_data[s + 1] + out_data[s - 1] + out_data[s - data_width];
066             temp_data >>= 2;
067             if (temp_data > 1){
068                 temp_data -= 1;
069             }
070             temp_data <<= 0;
071             out_data[s - data_width] = temp_data;
072             var id = s << 2;
073             img_data.data[id + 0] = colors[temp_data].r;
074             img_data.data[id + 1] = colors[temp_data].g;
075             img_data.data[id + 2] = colors[temp_data].b;
076             img_data.data[id + 3] = colors[temp_data].a;
077         }
078         data_cnt -= data_width;
079     }
080     ctx.putImageData(img_data, 0, 0);
081     // Prepare font
082     ctx.font = '26px DS-Digital';
083     ctx.fillStyle = '#000000';
084     ctx.textAlign = "center";
085     // Draw all the buttons
086     for (var ib = 0; ib < buttons.length; ib++) { //
087         drawButton(ctx, buttons[ib]);
088     }
089 }
090 // Window Onload event handler
091 if (window.attachEvent) {
092     window.attachEvent('onload', main_init);
093 else {
094     if(window.onload) {
095         var curronload = window.onload;
096         var newonload = function() {
097             curronload();
098             main_init();
099         };
100         window.onload = newonload;
101     else {
102         window.onload = main_init;
103     }
104 }
105 // Main initialization
106 function main_init() {
107     // Create canvas and context objects
108     canvas = document.getElementById('panel');
109     ctx = canvas.getContext('2d');
110     // Prepare data for our fire object and prepare palette
111     img_data = ctx.createImageData(canvas.width, canvas.height);
112     data_width = img_data.width,
113     data_height = img_data.height,
114     prepare_palette();
115     // Fill new array with 0
116     out_data = fill_new_array(data_width * data_height, 0)
117     // Prepare image to buttons
118     var buttonImage = new Image();
119     buttonImage.src = 'images/button.png';
120     buttonImage.onload = function() {};
121     // Prepare 3 different buttons
122     buttons.push(new Button(0, 10, 245, 62, 'normal', buttonImage, 'button #1'));
123     buttons.push(new Button(250, 10, 245, 62, 'normal', buttonImage, 'button #2'));
124     buttons.push(new Button(500, 10, 245, 62, 'normal', buttonImage, 'button #3'));
125     buttons.push(new Button(750, 10, 245, 62, 'normal', buttonImage, 'button #4'));
126     // Loop main scene
127     setInterval(drawScene, 40);
128     // Onmousemove event handler
129     canvas.onmousemove = function(e) {
130         var mouse = getMousePosition(e).sub(new vector2d(canvas.offsetLeft, canvas.offsetTop));
131         for (var i = 0; i < buttons.length; i++) { // Apply 'hover' state for buttons
132             if (buttons[i].state != 'pressed') {
133                 buttons[i].state = 'normal';
134                 buttons[i].imageShift = 0;
135                 if (mouse.x > buttons[i].x && mouse.x < buttons[i].x+buttons[i].w && mouse.y > buttons[i].y && mouse.y < buttons[i].y+buttons[i].h) {
136                     buttons[i].state = 'hover';
137                     buttons[i].imageShift = 136;
138                 }
139             }
140         }
141     }
142     // Onmousedown event handler
143     canvas.onmousedown = function(e) {
144         var mouse = getMousePosition(e).sub(new vector2d(canvas.offsetLeft, canvas.offsetTop));
145         for (var i = 0; i < buttons.length; i++) { // Apply 'pressed' state for buttons
146             if (mouse.x > buttons[i].x && mouse.x < buttons[i].x+buttons[i].w && mouse.y > buttons[i].y && mouse.y < buttons[i].y+buttons[i].h) {
147                 buttons[i].state = 'pressed';
148                 buttons[i].imageShift = 68;
149             }
150         }
151     }
152     // Onmouseup event handler
153     canvas.onmouseup = function(e) {
154         var mouse = getMousePosition(e).sub(new vector2d(canvas.offsetLeft, canvas.offsetTop));
155         for (var i = 0; i < buttons.length; i++) { // Reset states for buttons
156             if (mouse.x > buttons[i].x && mouse.x < buttons[i].x+buttons[i].w && mouse.y > buttons[i].y && mouse.y < buttons[i].y+buttons[i].h) {
157                 alert(buttons[i].text + ' is pushed');
158             }
159             buttons[i].state = 'normal';
160             buttons[i].imageShift = 0;
161         }
162     }
163 }

The main idea of our menu – put several buttons at canvas element, and then – add event handlers. And, as extra feature – animated fire at the background. I sure that our demonstration can bring you new ideas for your projects.

Step 3. Extra files

It’s possible that you have noticed that I use the addition files in our project: main.css, Ds-digib.ttf, bg.jpg, button.png and vector2d.js. All these files are available in our download package.


Live Demo

Conclusion

I hope that you like our html5 experiments. Welcome back.