HTML5 : Canvas Image Manipulation Tutorial

Today, I’m sharing with you a little on Image manipulation using Canvas. Since it’s already 2012, I figured it’s time to write more on my HTML5 stuff. As you already know by now, one of the most powerful and interactive element in html5 is none other than the Canvas. Rather, than blogging about theory of what html5 is and what’s the new tag blah blah…I’m just gonna make a simple project out of it – a straight forward implementation. So today’s tutorial, I will be covering the followings :-

  • Loading an image into the canvas
  • Image blurring effect – using an External Library (StackBlur.js)
  • Image rotation – pure html5 canvas programming
  • Little on CSS3  - a little on the border radius stuff, gradient stuff.
  • Slider element of HTML5. – pure html5 usage of input type=”range” (yes, No more JQueryUI and loading lots of js files – just for one slider)

First of you might wanna have a look at the demo I’ve prepared in advance here. You may also download the entire tutorial below.

Download Here


Ok, first let’s look html code itself. Like all standard html5 page, the standard doctype must be declared prior to anything. Click on the button to load an image into the canvas which is the white box with rounded borders in the center of the html page. Try playing around with the sliders. Apply blur effect and the rotation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<!DOCTYPE HTML>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>Sample App HTML5 One - Using Canvas and Filters</title>
	<meta name="generator" content="TextMate http://macromates.com/">
	<meta name="author" content="Alex Tam Ming Yen">
	<LINK href="default.css" rel="stylesheet" type="text/css">
	<!-- Date: 2011-12-31 -->
	<script type="text/javascript" src="lib/StackBlur.js"></script>
	<script>
		function triggerReady(){
			var btnLoadImage 	= document.getElementById('btnLoadImage');
			var blur_range 		= document.getElementById('blurRange');
			var rotation_range 	= document.getElementById('rotationRange');
			var canvas 		 = document.getElementById('canvas');
			var imageRefence = document.getElementById('imgSrc');
                        // The Canvas context governs the rendering of the canvas content itself. 
			var contxt = canvas.getContext('2d');
                        // Declaring an image Object.
			var img = new Image();
			img.width = 480;
			img.height = 320;
 
			btnLoadImage.onclick = function(){
				img.onload = function(){
				    clear();
				    contxt.drawImage(img, 0, 0, img.width, img.height);
				}
				img.src = 'images/ProfilePic.png';
				imageRefence.src = img.src;
			}
 
			blur_range.onchange = function(){
				processImageBlur( blur_range.value );
			}
 
			rotation_range.onchange = function(){
				processRotateImage( rotation_range.value );
			}
 
			function processImageBlur( value ){
                                // I use stackBlur.js for this refer to this site to know more on how to use and apply this.
                                // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
				stackBlurImage( "imgSrc", "canvas", value, false );
				rotation_range.value = 0;
			}
 
			function processRotateImage( value ){
				clear();
				contxt.save();
                                // Like all rotation, it needs a center pivot to refer to.
                                // For that we use translate and move the coordinate to the middle of the image
				contxt.translate(480/2, 320/2);
                                contxt.rotate(value * Math.PI / 180);
				// You need to reverse translate back the coordinate because, you still need to render the image from 0,0.
                                contxt.translate(-(480/2), -(320/2));
				contxt.drawImage(img, 0, 0, 480, 320);
				contxt.restore();
			}
 
			function clear(){
                                // Erase content of the canvas and refresh.
				contxt.clearRect(0, 0, 480, 320);
			}
		}
 
	</script>
</head>
<body onload="triggerReady()">
	<div class="titleBar">
		<h1 class="label15Arial">Sample App With Canvas<h1>
	</div>
 
	<div class="borderContainer"><canvas id="canvas" width="480" height="320"></canvas></div>
	<div class="controlsContainer">
		<label class="range1label">Blur Range</label>
		<input type="range" min="0" max="100" value="0" step="1" class="range1" id="blurRange"/>
 
		<label class="range2label">Rotation</label>
		<input type="range" min="0" max="359" value="0" step="1" class="range2" id="rotationRange"/>
		<center>
		<input type="submit" value="Load Image..." class="buttons" id="btnLoadImage" />
		</center>
	</div>
 
	<div id="referenceData" hidden="true"><img id="imgSrc"></div>
</body>
</html>

Take a look how the canvas is implemented, just a simple and clean tag isn’t it sweet ? Notice, in canvas programming, you must always remember, to “represent” an object e.g. image in our case, everything must be done in data abstraction in the memory – then render to display. Like the case of rotation (see line 48).

  • The canvas content is cleared,
  • New angle is calculated,
  • The coordinates is translated
  • Image object is rotated in memory
  • Image object is redrawn into the canvas

Every time a rotation slider value is changed. Well, the rest you can read up and experiment more by tweaking my codes in the source to find out more. We now take a look at the CSS file. By the way, I generate my gradient here

 

/**************************** HTML 5 RESET **************************/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
	display: block;
}
body {
	line-height: 1;
}
ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}
 
/**************************** HTML 5 RESET **************************/
.label15Arial {
	font-family:Arial;
	color:#fff;
	font-size:16pt;
	font-weight:bolder;
	text-shadow: #333 0 0 0;
	padding-top:10px;
	text-align:center;
}
 
.titleBar {
        // Here is the cool gradient stuff - use generator http://gradients.glrzad.com/
	background-image: linear-gradient(bottom, rgb(0,0,0) 58%, rgb(184,184,184) 80%, rgb(39,46,46) 94%);
	background-image: -o-linear-gradient(bottom, rgb(0,0,0) 58%, rgb(184,184,184) 80%, rgb(39,46,46) 94%);
	background-image: -moz-linear-gradient(bottom, rgb(0,0,0) 58%, rgb(184,184,184) 80%, rgb(39,46,46) 94%);
	background-image: -webkit-linear-gradient(bottom, rgb(0,0,0) 58%, rgb(184,184,184) 80%, rgb(39,46,46) 94%);
	background-image: -ms-linear-gradient(bottom, rgb(0,0,0) 58%, rgb(184,184,184) 80%, rgb(39,46,46) 94%);
	width:100%;
	height:45px;
}
 
.borderContainer {
	border:1px solid #c0c0c0;
	margin-left:auto;
	margin-right:auto;
	margin-top:15px;
	width:480px;
	height:320px;
	-moz-border-radius: 8px;
	border-radius: 8px;
}
 
.controlsContainer {
	border:1px solid #c0c0c0;
	margin-left:auto;
	margin-right:auto;
	margin-top:15px;
	width:480px;
	height:120px;
	-moz-border-radius: 8px;
	border-radius: 8px;
}
 
.range1 {
	margin-top:15px;
	width:280px;
	float:left;
}
 
.range1label {
	font-family:Arial;
	color:#000;
	font-size:14px;
	margin-top:15px;
	margin-left:15px;
	width:105px;
	font-weight:bolder;
	text-shadow: #333 0 0 0;
	float:left;
}
 
.range2 {
	margin-top:15px;
	width:280px;
	float:left;
}
 
.range2label {
	font-family:Arial;
	color:#000;
	font-size:14px;
	margin-top:15px;
	margin-left:15px;
	width:105px;
	font-weight:bolder;
	text-shadow: #333 0 0 0;
	float:left;
}
 
.buttons {
	-moz-border-radius: 6px;
	border-radius: 6px;
	width:300px;
	background:#fff;
	height:30px;
	margin-top:15px;
	color:#000;
}
 
#canvas {
        // Just two lines for cool border radius, no more IE hack or using Image, then abusing of divs 
	-moz-border-radius: 6px;
	border-radius: 6px;
}

Note of warning :Things you should know about canvas. For some apparent reason, try not to style / css width and height of the canvas element. Instead using the attribute of width and height on to the tag itself. Reason being, some browsers will render the content scale weirdly. This information is accurate to date of the post. This issue might be fix in near future.

That’s it for today sharing. Oh yeah and Happy New Year 2012.