HTML5 is really hot all over the web right now so I figured I would drop some HTML5 knowledge on y'all. I have worked with Flash and Flex consistently for the last few years so I can easily drop and manipulate graphics in it, but I haven't done much with HTML5. This lead me to try and challenge myself to recreate something I built in Flash in HTML5. That is what we are going to look at and learn to build in this tutorial.
A few things should be mentioned. First IE simply doesn't implement the canvas tag which means it won't work in IE, Google has released Explorer
Canvas which fixes this to
some extent. I, however, didn't worry about adding that to this tutorial
because this post is about creating the content. Also, there are some
small things that don't work in other browsers. Finally, Mobile Safari
(iPhone, iPod Touch, and iPad) doesn't implement text rendering
correctly.
To get things rolling we'll first put some simple html down. This
includes the canvas tag which is what we're using for this tutorial. The only other element we are going to use is an input button to spin our wheel. I also added a little bit of inline style to place the button.
<input type="button" value="spin" style="float:left;" />
<canvas id="canvas" width="500" height="500"></canvas>
The next thing we are going to do is begin drawing some stuff on to our
canvas. This is going to be done in JavaScript in a function I named drawRouletteWheel.
The basics of drawing on the canvasin 2D at least involve grabbing a drawing context and then drawing onto
it. Sounds complicated doesn't it. Let's look at a little bit of code.
var colors = ["#B8D430", "#3AB745", "#029990", "#3501CB",
"#2E2C75", "#673A7E", "#CC0071", "#F80120",
"#F35B20", "#FB9A00", "#FFCC00", "#FEF200"];
var startAngle = 0;
var arc = Math.PI / 6;
var ctx;
function drawRouletteWheel() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var outsideRadius = 200;
var insideRadius = 125;
ctx = canvas.getContext("2d");
ctx.clearRect(0,0,500,500);
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
for(var i = 0; i < 12; i++) {
var angle = startAngle + i * arc;
ctx.fillStyle = colors[i];
ctx.beginPath();
ctx.arc(250, 250, outsideRadius, angle, angle + arc, false);
ctx.arc(250, 250, insideRadius, angle + arc, angle, true);
ctx.stroke();
ctx.fill();
}
}
}
Okay, looking at the start of the function we grab a reference to our canvas object by id - nothing new here. Then we check to make sure the browser supports grabbing a drawing context, this is important to make sure we don't throw errors in browsers that don't support the features. Then we set a couple of variables for the inner and outer radius of our wheel. Now, we get into the meat. The first thing we do is grab a 2D drawing context by calling getContext. From here we clear the canvas, so we have a blank slate to draw onto. Then we set the stroke color and width which in this case is "black" and 2.
The next part takes a little bit of explaining, we are going to loop through 12 sections (the number of sides we are going to draw). For each section we determine the angle of where each section is going to start. The startAngle is a global variable in which we are going to use to animate the wheel, for now, it is set to 0. The following line sets the fill color by pulling a value from the global colors array. Drawing begins after that with starting a path drawing two arcs, using the 'arc(x, y, radius, startAngle, endAngle, anticlockwise)' function. We then tell the context to stroke the path and fill the path, these will use the previously set parameters.
Now, we are going to add the restaurant text to our drawing code. Also,
to finish off the drawing function we'll add the nice little arrow at
the top. The text drawing is done using the fillText(text, x, y [, maxWidth ]) function.
Let's take a look at the updated code.
var restaraunts = ["Wendy's", "McDonalds", "Chick-fil-a", "Five Guys",
"Gold Star", "La Mexicana", "Chipotle", "Tazza Mia",
"Panera", "Just Crepes", "Arby's", "Indian"];
function drawRouletteWheel() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var outsideRadius = 200;
var textRadius = 160;
var insideRadius = 125;
ctx = canvas.getContext("2d");
ctx.clearRect(0,0,500,500);
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.font = 'bold 12px Helvetica, Arial';
for(var i = 0; i < 12; i++) {
var angle = startAngle + i * arc;
ctx.fillStyle = colors[i];
ctx.beginPath();
ctx.arc(250, 250, outsideRadius, angle, angle + arc, false);
ctx.arc(250, 250, insideRadius, angle + arc, angle, true);
ctx.stroke();
ctx.fill();
ctx.save();
ctx.shadowOffsetX = -1;
ctx.shadowOffsetY = -1;
ctx.shadowBlur = 0;
ctx.shadowColor = "rgb(220,220,220)";
ctx.fillStyle = "black";
ctx.translate(250 + Math.cos(angle + arc / 2) * textRadius,
250 + Math.sin(angle + arc / 2) * textRadius);
ctx.rotate(angle + arc / 2 + Math.PI / 2);
var text = restaraunts[i];
ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
ctx.restore();
}
//Arrow
ctx.fillStyle = "black";
ctx.beginPath();
ctx.moveTo(250 - 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 0, 250 - (outsideRadius - 13));
ctx.lineTo(250 - 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius + 5));
ctx.fill();
}
}
Looking at the new text drawing we start by saving the current context state - this is going to allow us to rotate and translate the text without affecting everything else. We then set some shadow stuff, which will put a drop shadow on the text. Translating and rotating the text is tackled next, we first translate the text to the correct placement on the wheel and then rotate it. Drawing the text follows this but with the added effect of centering the text by measuring it and dividing that by 2 to offset it. You'll notice we grab the restaurant name from a global array. Lastly we restore to our initial state we saved to, this makes sure the transformations do not affect later drawing. The arrow is even easier to explain, we just move to one corner and draw lines to create the shape and fill it with black.
Below I have the complete code for this demo. I'll explain the spinning and animating code right after.
var colors = ["#B8D430", "#3AB745", "#029990", "#3501CB",
"#2E2C75", "#673A7E", "#CC0071", "#F80120",
"#F35B20", "#FB9A00", "#FFCC00", "#FEF200"];
var restaraunts = ["Wendy's", "McDonalds", "Chick-fil-a", "Five Guys",
"Gold Star", "La Mexicana", "Chipotle", "Tazza Mia",
"Panera", "Just Crepes", "Arby's", "Indian"];
var startAngle = 0;
var arc = Math.PI / 6;
var spinTimeout = null;
var spinArcStart = 10;
var spinTime = 0;
var spinTimeTotal = 0;
var ctx;
function drawRouletteWheel() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var outsideRadius = 200;
var textRadius = 160;
var insideRadius = 125;
ctx = canvas.getContext("2d");
ctx.clearRect(0,0,500,500);
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.font = 'bold 12px Helvetica, Arial';
for(var i = 0; i < 12; i++) {
var angle = startAngle + i * arc;
ctx.fillStyle = colors[i];
ctx.beginPath();
ctx.arc(250, 250, outsideRadius, angle, angle + arc, false);
ctx.arc(250, 250, insideRadius, angle + arc, angle, true);
ctx.stroke();
ctx.fill();
ctx.save();
ctx.shadowOffsetX = -1;
ctx.shadowOffsetY = -1;
ctx.shadowBlur = 0;
ctx.shadowColor = "rgb(220,220,220)";
ctx.fillStyle = "black";
ctx.translate(250 + Math.cos(angle + arc / 2) * textRadius,
250 + Math.sin(angle + arc / 2) * textRadius);
ctx.rotate(angle + arc / 2 + Math.PI / 2);
var text = restaraunts[i];
ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
ctx.restore();
}
//Arrow
ctx.fillStyle = "black";
ctx.beginPath();
ctx.moveTo(250 - 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 0, 250 - (outsideRadius - 13));
ctx.lineTo(250 - 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius + 5));
ctx.fill();
}
}
function spin() {
spinAngleStart = Math.random() * 10 + 10;
spinTime = 0;
spinTimeTotal = Math.random() * 3 + 4 * 1000;
rotateWheel();
}
function rotateWheel() {
spinTime += 30;
if(spinTime >= spinTimeTotal) {
stopRotateWheel();
return;
}
var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal);
startAngle += (spinAngle * Math.PI / 180);
drawRouletteWheel();
spinTimeout = setTimeout('rotateWheel()', 30);
}
function stopRotateWheel() {
clearTimeout(spinTimeout);
var degrees = startAngle * 180 / Math.PI + 90;
var arcd = arc * 180 / Math.PI;
var index = Math.floor((360 - degrees % 360) / arcd);
ctx.save();
ctx.font = 'bold 30px Helvetica, Arial';
var text = restaraunts[index]
ctx.fillText(text, 250 - ctx.measureText(text).width / 2, 250 + 10);
ctx.restore();
}
function easeOut(t, b, c, d) {
var ts = (t/=d)*t;
var tc = ts*t;
return b+c*(tc + -3*ts + 3*t);
}
drawRouletteWheel();
It's a lot to take in but we'll take it slow. At the bottom of the code
you'll see we call drawRouletteWheel, this is too draw the initial wheel when the page loads. We have to
update the input button we put on the screen to now call our spin
function, which handles spinning the wheel, of course. The updated input
follows.
<input type="button" value="spin" onclick="spin();" style="float:left;" />
Taking a look at the spin function we set a couple global variables that decide how fast we are
going to spin and how long we are going to spin. These have a little bit
of randomness in them to make things more interesting. Lastly, we call rotateWheel.
The rotateWheel function updates the amount of time we have been spinning, checks to see
if we should stop, updates spinning speed, draws the wheel, and then
calls itself in 30 milliseconds. After checking the time we change the
spinning angle, startAngle, this is done using an easing function to slow down the spinning. Then
we call our drawing function and use
setTimeout
and keeps a reference to call our rotate again.
The last function we need look at is stopping the wheel, stopRotateWheel. This starts with clearing the timeout which will stop the code from
rotating the wheel. Then we use some math to figure out what restaurant
is at the top. Finally, we draw the large name of the of the selected
restaurant in the middle of the wheel.
Well, that's pretty much a wrap on this post. I really like how HTML5 and the canvas tag are coming along. Although it's not quite ready for primetime production work yet. I look forward to the next couple years of web development.
Source Files:
Great tutorial - thanks :)
This wheel is what I have been looking for. Is this available to download. If so, can the prizes be changed? Jim
Yeah at the bottom of the post there is a list of source files. This has everything you'll need for implementing it. Including an array of the the "prizes".
Hi there :)
This might be a bit late for a reply, but I would really appreciate some help regarding the roulette tutorial.
How can I change the individual opacity/transparency for the colors?
I was able to use ctx.globalAlpha = ; but this also affects the text and arrow...
Help!
Before you set the ctx.globalAlpha you need to call ctx.save() and then after the fill you need to call ctx.restore().
is there any way to spin this wheel automatically ? prosenjit1234567890@gmail.com
Can I stop this wheel in a particular room ??
you can fire up the spin(); function on document.load event, or more elegantly using setTimeout(handler, milliseconds) function.
Have you checked out Raphael? It works all the way back to ie v5.5 as well as all of the other browsers (Opera, Chrome, Safari, etc....). A very nice implementation of the Canvas model.
The biggest problem with the canvas model is that most of the browsers (Chrome is the exception) gobble up tons of memory they really just don't need. I have seen as much as 500MB of memory used to move ten rectangles around randomly on the screen.
In the way-back machine: When Netscape v2.0 came out I wrote to the folks at Netscape and asked them why they didn't allow Javascript to write directly to the screen via graphical commands (like dot, line, rect, oval, etc....). Their reply to me? "No one will every want to do that." Silly me! :-)
It's sad: There are tons of 3D, super fast games out there now. Yet browsers can't handle 3D graphics and can barely handle 2D. Browsers have been around for almost twenty years - yet they still can't handle simple graphics. Maybe they should get some game development people to re-write the underlying code so they start from a basis of drawing to the screen and then make an HTML interpretor that just uses that code? Then they'd be able to do all of the graphics (which is what HTML ultimately is - just another way to lay out the graphics. [Remember that all text is now drawn to the screen so it also is now graphics unlike when there were actual CRT terminals with characters hard coded into the motherboard.])
It is my guess that this is what Google did with Chrome. Instead of an HTML engine that draws to the screen; they probably made a graphical 2D/3D drawing engine and then made an HTML interpreter that uses the underlying graphical engine to draw the web page. Just like a 2D/3D game can do.
Wouldn't surprise me if Google is using OGRE to do the graphics either. Anyway, just some old timer thoughts.
Can you explain wich parameters we must change at the stopRotateWheel function if we move the arrow to another angle?
any advice on changing the number of segments?? more prizes or less??
Thanks!
I used this as my tutorial to learn drawing on canvas, and it was incredible. It took me a few experiments to get how stroking an arc clockwise and then the inner arc counter clockwise would give a fillable segment, but got it, this was very helpful. Thanks.
Thank you very much for the tutorial. I used this tutorial to help create this site: http://www.nhlwheelofjustice.com/
Hi Pinder, Would you be kind enough to share your source code?
I wonder if would be possible add to wheel some images rather then text and set the resulting image as a link to a different web page. Thanks for the help.
Or better:
Thank for the help
Need help for this. How to add image?
Hi, i need to do this, but in WPF... any ideas?
I was looking for a tutorial to do this!!
Thank you for taking the time to do this, I really appreciate it!
Is there a way to put the resulting Roulette wheel result in a database?
Thanks so much for this, it served as an excellent basis for a random student name selector web part in a school's SharePoint site.
Just a word of warning: if you intend (or are forced) to use the html5 canvas in IE9, the following meta tag wll be needed to ensure IE operates in standards mode.
[language] [/language]
Without the meta tag you probably wont see the canvas.
how would you make this stop at set points ? So to come up with a pre chosen choice, to fit in with a role play ?
You can insert non-random integers for variables spinAngleStart and spinTimeTotal inside the spin function. This will guarantee the spin will land on a specific spot.
You could then use some outside function to randomly generate that set point accordingly, if needed.
There may be a more elegant solution, but that is a quick and dirty option.
Thank you, Johnstonian
Please Help, I am unable to stop this in a particular spot !!
Hi,
This is exactly what I need. How can I put it on my website using html?
Thank you
Does anyone know how to add more segments onto it?
thanks for helps.
Excellent post. I have a question though...
Could someone explain the maths behind the text positioning. I understand that this line:
ctx.translate(250 + Math.cos(angle + arc / 2) * textRadius, 250 + Math.sin(angle + arc / 2) * textRadius);
is setting the origin for the text fill but I don't understand why "250 + Math.cos(angle + arc / 2) * textRadius" gives the correct X co-ordinate and same goes for the Y.
That will position the end of the text around a circle of text radius. The cosine and sine will the correct x and y position because of right triangle math. Basically the position of the text is a point on a circle where radius "textRadius" and using some trigonometry you can determine the x and y coords, http://library.thinkquest.org/20991/geo/stri.html#trig.
Let me know if there's something else you don't get.
Thank you! I see now that with Trig i can find the Adjacent and Opposite values for X & Y.
I have another question, again it's my maths :(
It's in the stopRotateWheel function and it's the maths behind working out the segment it stopped in.
var degrees = startAngle * 180 / Math.PI + 90; var arcd = arc * 180 / Math.PI; var index = Math.floor((360 - degrees % 360) / arcd);
So first we work out in degrees the final startAngle + 90. Why do we add the 90?
Then we work out the arc in degrees.
Finally to work out the segment index we minus from 360 the remainder of "degrees" divided by 360, then divide that result by the size of the arc.
I don't really understand how that works. Can somebody step through it?
Thanks for your help.
Can I rotate the wheel in steps of 30 degrees by mouse clicking or dragging in the same wheel? what code or parameter need to change?
This is so cool, I have been taking it apart for weeks now, and I cant figure out how to draw more than 45 segments/
PLEEZE if you get a minute?
You need to update pieces of the application.
You need to change the above variable for the number of segments you want to draw.
You also need to update the i \< 12 to be the number of segments you're going to draw.
Maybe its a memory draw that onlr app.mobi javascript can help
OK figured it out now. Now I need to know how to grow back the hair i pulled out. Anyone?
Is there a way to limit the number of spins? Like a one spin kind of deal?
I had read ur tutorial and it was much more Usefull to me , here Iam facing the Problem that You are partitioning the circle in to 12 parts and ur adding the Text in the each cell,
In the same Place I need to Place Images in that 12 parts of the circle.
Can u solve this query as early as possible Thankq
I changed the prizes to stuff like "/\$100", "\$500"
but how can I add the values to a database so that the players really get a prize?
What a great tutorial, Thank you
supposingly if i have to use an image and i dont wish to draw it...So what lines of code i should do to make image spin instead of drawroulette() function ?
You need to draw the image and rotate it in the canvas element, check out the following tutorial.
http://www.switchonthecode.com/tutorials/html-5-canvas-tutorial-rotation
Thanks!
This is a great tutorial, but where can I download the excanvas.js file so this works in IE. It works perfect in FireFox. Thanks again!! :-)
http://excanvas.sourceforge.net/
how to stop the wheel at the randomly selected item ? is this can be possible ? if so how we can make the wheel to stop at randomly selected item?
The code above stops at a random selection. Are you looking to predefine the selected item so it always spins to that one?
If that is what you're looking for you would need to calculate the rotation amount based on the position of the predefined item and rotate that amount over a period of time.
thanks for ur reply. let me clarify little more on what i need exactly.
?1. I will select the item on which the spin should stop initially by means of some logic and algorithm.
?2. Then once the user click on spin button, the wheel has to rotate for sometime and it should halt at the item which we have selected in the step 1.
Is there anyway to stop the spin wheel like this?
Wonderful Work!
What I need to do if I want to link each text to web page?
any help really appreciated.
Thank you,
That would take a little bit of work to do, what you'd have to do is listen for the click event on the canvas and then check the coordinates to see if the click was on one of the pieces of text then using javascript redirect the browser.
is there any way that this wheel will work on EI6?
You'll have to play around with http://code.google.com/p/explorercanvas/ which backports canvas calls to compliant IE calls.
The roulette wheel is not showing, its only the spin button
Dowloaded the excanvass and examples from the zip files our perfectly working however when i opened the roulette file, i can only see the SPIN button. I hate company computers. Everything is restricted.
How do I stop the wheel according to the index of the array, according to the prize that I want?
Hi
How can we increase or decrease the sections of spinning wheel? And is there anyway to put image in those sections instead of text.
Hey, how do I reset the wheel at the point what the wheel was started, before I start for the second spin?
Thanks, The Fattest! Cool 1st experience with HTML5.
2 Questions. I've changed the restaurant names to numbers, but want to stack the numbers on the segments. Ex.
1 2 3
Is that possible?
Is it also possible to add sound? Like that standard wheel sound you hear when these things spin around.
Awesome tutorial!
hey.
it is possible to change the names of the restaraunts into clickable links a href?
awesome roulette you have made BTW :)
Hello,
Good how to, thanks,
it's possible to change text by image ? or add image in table colors[] ?
Thanks,
jerau56
Find an alternative aproach here http://ruleta.ibarra.me
How to spin the wheel anti clock wise ??
You just need edited 2 function like below:
function rotateWheel() { spinTime += 30; if(spinTime >= spinTimeTotal) { stopRotateWheel(); return; } var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal); startAngle += (-spinAngle * Math.PI / 180); drawRouletteWheel(); spinTimeout = setTimeout('rotateWheel()', 30); }
function stopRotateWheel() { clearTimeout(spinTimeout); var degrees = startAngle * 180 / Math.PI + 90; var arcd = arc * 180 / Math.PI; var index = Math.floor((360 - degrees % 360) / arcd); ctx.save(); ctx.font = 'bold 30px sans-serif'; index-=restaraunts.length; var text = restaraunts[index] ctx.fillText(text, 250 - ctx.measureText(text).width / 2, 250 + 10); ctx.restore(); }
Please Any one told me, I want to create two button : one that stop rotating wheel slowly as now works fine .. and second that rotate wheel continuously...I do with small change in code but not work...
function stopWheel() { spinAngleStart = Math.random() * 10 + 10; spinTime = 0; spinTimeTotal = Math.random() * 3 + 4 * 1000; rotateSlowWheel(); } function rotateSlowWheel(){//alert(spinTimeTotal); clearTimeout(spinTimeTotal); clearTimeout(spinTimeout); //spinTimeTotal -= ; spinTime = 10; if(spinTime >= spinTimeTotal) {alert("Spin"); stopRotateWheel(); return; } var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal); startAngle += (spinAngle * Math.PI / 180); drawRouletteWheel(); spinTimeout = setTimeout('rotateSlowWheel()', 20); }
Well I do this.....using adding one more function in code
How we can send the result of "var text" in php or other language for including in mysql.
I want that player spin wheel and after he win the result... so
How do i place odds on this game. I want to have a 10 prize with 1 out of 100 win and a 50 prize where 1 out of 500 win and so on and so forth
I just spent an hour trying to get this working properly for i.e. 9.
You have to stick that on your page or it won't work.
Hi Charlie, great tutorial - thank you very much. but i have some question - 1) i want to play audio(mp3) in the canvas background. when i click spin button audio will be play and when wheel going to stop then audio will be stop ...
2) i want to use images - place of colors.. can it's possible?
Thanks!!! Shachish Sneh
I don't see this game in Internet explorer 8 & lower. How can i get method to see the game in Internet explorer 8 & lower?