Iniciando com Canvas
O que é Canvas
É uma especificação que define o contexto 2D no browser, implementando um elemento HTML que provê métodos e propriedades e métodos para desenhar e manipular gráficos. - W3C 2D Context
Canvas é um elemento da HTML5 destinado a delimitar uma área para renderização dinâmica de gráficos. Todo o trabalho de criação e animação é realizado atráves de linguagens de programação dinâmica (usualmente Javascript).O elemento foi originalmente introduzido pela Apple Inc. para o navegador Safari. As aplicações da Mozilla ganharam suporte ao canvas começando pelo Gecko 1.8 (Firefox 1.5). O Internet Explorer possui suporte ao elemento a partir da versão 10. Para adicionar suporte as versões anteriores desse navegador, basta incluir um script feito pela Google chamado Explorer Canvas. Google Chrome e Opera também suportam o canvas. - Wikipedia
Funções Básicas
O Canvas é um elemento HTML uma tag Html como também é conhecido, é nesse elemento em que os contextos 2D e 3D serão instanciados. É importante salientar que o elemento canvas não refere-se ao contexto 2D mas a área do documento será dedicada a apresentar conteúdo seja ele 2D ou 3D.
O contexto pode ser 2d
ou webgl
(3D), cada elemento canvas pode ter somente um contexto. Para criar um contexto, chamamos o método do getContext()
.
Primeiro passo
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
</script>
</body>
</html>
Segundo passo
Iremos começar desenhando uma linha, nós iremos usar os seguintes métodos para essa tarefa: beginPath()
moveTo()
lineTo()
stroke()
.
O métodobeginTo()
serve para declarar que vamos desenhar um novo traço. O moveTo()
tem a função de mudar a posição do cursor do contexto, por fim stroke()
usamos esse método para torna o que desenhamos visível, como se disséssemos ao browser desenhe no elemento canvas. Ver código.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="canvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.stroke();
</script>
</body>
</html>
Resultado
Terceiro passo
Iremos agora criar linhas com estilos diferentes. Para isso vamos alterar a propriedade lineCap
com um dos seguintes parâmetros: butt
, round
e square
. Ver código.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="canvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// butt line cap (top line)
context.beginPath();
context.moveTo(200, canvas.height / 2 - 50);
context.lineTo(canvas.width - 200, canvas.height / 2 - 50);
context.lineWidth = 20;
context.strokeStyle = '#FF9900';
context.lineCap = 'butt';
context.stroke();
// round line cap (middle line)
context.beginPath();
context.moveTo(200, canvas.height / 2);
context.lineTo(canvas.width - 200, canvas.height / 2);
context.lineWidth = 20;
context.strokeStyle = '#52FF00';
context.lineCap = 'round';
context.stroke();
// square line cap (bottom line)
context.beginPath();
context.moveTo(200, canvas.height / 2 + 50);
context.lineTo(canvas.width - 200, canvas.height / 2 + 50);
context.lineWidth = 20;
context.strokeStyle = '#FF0047';
context.lineCap = 'square';
context.stroke();
</script>
</body>
</html>
Resultado
Quarto passo
Agora vamos criar desenhar mais complexo utilizando curva quadrática e curva de bezier.
Será necessário utilizar os seguintes métodos: lineTo()
, arcTo()
, quadraticCurveTo()
e bezierCurveTo()
, para desenhar cada parte. Ver código.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="canvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(100, 20);
// line 1
context.lineTo(200, 160);
// quadratic curve
context.quadraticCurveTo(230, 200, 250, 120);
// bezier curve
context.bezierCurveTo(290, -40, 300, 200, 400, 150);
// line 2
context.lineTo(500, 90);
context.lineWidth = 2;
context.strokeStyle = 'orange';
context.stroke();
</script>
</body>
</html>
Resultado
Quinto Passo
Nesse passo vamos criar um retângulo, para isso iremos utilizar o método rect()
, definir a posição do quadrado no modelo cartesiano alterando as propriedades x
e y
e por fim definindo a largura (width
) e altura (height
). Ver código.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="canvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.beginPath();
context.rect(10, 10, 50, 40);
context.fillStyle = 'orange';
context.fill();
context.lineWidth = 2;
context.strokeStyle = 'grey';
context.stroke();
</script>
</body>
</html>
Resultado
Sexto passo
Vamos desenhar um círculo, para isso iremos usar a função arc()
. iniciando no ângulo de 0º até 2*PI. Ver código.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="canvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 70;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'magenta';
context.fill();
context.lineWidth = 2;
context.strokeStyle = '#003300';
context.stroke();
</script>
</body>
</html>
Resultado
Funções Avançadas
Com base do que vimos, vamos manipular o elemento canvas com exemplos mais difíceis.
Primeiro Passo
Escrevendo com canvas, para realizar essa tarefa iremos alterar a propriedade strokeStyle
e chamar o métodostrokeText()
Ver código.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="canvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var x = 80;
var y = 110;
context.font = '60pt Verdana';
context.lineWidth = 3;
// stroke color
context.strokeStyle = 'green';
context.strokeText('Olá Mundo!', x, y);
</script>
</body>
</html>
Resultado
Segundo Passo
Rotacionando um retângulo, iremos utilizar o método rotate()
Ver código.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="canvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var rectWidth = 150;
var rectHeight = 75;
// translate context to center of canvas
context.translate(canvas.width / 2, canvas.height / 2);
// rotate 45 degrees clockwise
context.rotate(Math.PI * 1.5);
context.fillStyle = 'orange';
context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight);
</script>
</body>
</html>
Resultado
Terceiro passo
Vamos animar um retângulo, oscilando ele de um lado para o outro, para definir cada posição iremos utilizar a seguinte equação: x(t) = amplitude * sin(t * 2PI / period) + x0 . Ver código.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="canvas" width="578" height="200"></canvas>
<script>
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
function drawRectangle(myRectangle, context) {
context.beginPath();
context.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height);
context.fillStyle = '#8ED6FF';
context.fill();
context.lineWidth = myRectangle.borderWidth;
context.strokeStyle = 'black';
context.stroke();
}
function animate(myRectangle, canvas, context, startTime) {
// update
var time = (new Date()).getTime() - startTime;
var amplitude = 150;
// in ms
var period = 2000;
var centerX = canvas.width / 2 - myRectangle.width / 2;
var nextX = amplitude * Math.sin(time * 2 * Math.PI / period) + centerX;
myRectangle.x = nextX;
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
// draw
drawRectangle(myRectangle, context);
// request new frame
requestAnimFrame(function() {
animate(myRectangle, canvas, context, startTime);
});
}
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var myRectangle = {
x: 250,
y: 70,
width: 100,
height: 50,
borderWidth: 5
};
drawRectangle(myRectangle, context);
// wait one second before starting animation
setTimeout(function() {
var startTime = (new Date()).getTime();
animate(myRectangle, canvas, context, startTime);
}, 1000);
</script>
</body>
</html>
Resultado
Quarto passo
Criando uma foto a partir da câmera usando o elemento canvas
, video
o método getUserMedia()
presente no DOM e objeto chamado Blob
para armazenar dados de stream, que no nosso caso será a foto gerada, os dados presente no objeto do tipo Blob permitem que nós enviemos para um servidor que irá armazena-la. Ver código.
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/3.2.1/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/2.3.2/css/bootstrap.min.css">
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js"></script>
</head>
<body>
<style type="text/css">
.hide { display:none; }
</style>
<div class="container">
<div class="hide cam-disable">
<p><i class="icon-camera-retro icon-4x"></i> Enable access to camera</p>
</div>
<div id="cam-preview-container">
<h2>Smile for us!</h2>
<div class="row-fluid">
<div id="cam-preview" class="span12">
<video id="video" width="640" height="480" autoplay></video>
<canvas id="canvas" class="hide" width="640" height="480"></canvas>
</div>
<div class="span12">
<a id="snap" class="btn btn-large btn-success "><i class="icon-camera-retro"></i> Take Photo</a>
<span class="pull-left offset1">
<span>1 s</span> <input id="delay" type="range" min='1' max="15" value="3"> <span>15 s</span>
</span>
</div>
</div>
</div>
<div id="photo-preview-container" class="hide">
<h1>Smile for us!</h1>
<div class="row-fluid">
<div class="span12">
<img id="latest-photo" src="" width="640" height="480">
</div>
<div class="span12">
<span class="pull-left offset2"><a id="other-photo-btn" class="btn btn-large btn-primary"><i class="icon-camera-retro"></i> Take an Other Photo</a></span>
</div>
</div>
</div>
<a id='toggle-cam-status-btn' class="cam-stop-btn" onclick="App.controller.dashboard.toogleCam(this);"><i class="icon-eye-close"></i> <span>Close Camera</span></a>
</div>
<script type="text/javascript">
if(!App) var App = new function(){ return(this); }
if(!App.controller) App.controller = new function(){ return(this); }
App.controller.dashboard = new (function(){
var self = this;
this.localMediaStream = null;
this.toogleCam = function(e){
if($(e).attr('class') === 'cam-stop-btn'){
self.stop(); $(e).text('').attr('class', 'cam-start-btn');
$('<i class="icon-eye-open"></i> <span>Open Camera</span>').appendTo($(e));
return;
}
if($(e).attr('class') === 'cam-start-btn'){
self.start(); $(e).text('').attr('class', 'cam-stop-btn');
$('<i class="icon-eye-close"></i> <span>Close Camera</span>').appendTo($(e));
return;
}
}
this.stop = function(){
if(self.video) self.video.pause();
if(self.localMediaStream){
self.localMediaStream.stop();
self.localMediaStream = null;
}
}
this.start = function(response){
// Grab elements, create settings, etc.
var canvas = self.canvas = document.getElementById("canvas"),
context = self.context = canvas.getContext("2d"),
video = self.video = document.getElementById("video"),
videoObj = { "video": true };
function errBack(error) {
console.log("Video capture error: ", error.code);
$("#cam-preview-container").hide();
$(".cam-disable").show();
};
if(self.localMediaStream === null){
// Put video listeners into place
if(navigator.getUserMedia) { // Standard
navigator.getUserMedia(videoObj, function(stream) {
video.src = self.localMediaStream = stream;
video.play();
}, errBack);
} else if(navigator.webkitGetUserMedia) { // WebKit-prefixed
navigator.webkitGetUserMedia(videoObj, function(stream){
self.localMediaStream = stream;
video.src = window.webkitURL.createObjectURL(stream);
video.play();
}, errBack);
} else if(navigator.mozGetUserMedia){ //Moz-prefixed
navigator.mozGetUserMedia(videoObj, function(stream){
video.mozSrcObject = self.localMediaStream = stream;
video.play();
}, errBack);
}
} else {
if(navigator.getUserMedia) video.src = self.localMediaStream;
if(navigator.mozGetUserMedia) video.src = self.localMediaStream;
if(navigator.webkitGetUserMedia) video.src = window.webkitURL.createObjectURL(self.localMediaStream);
video.play();
}
function snap() {
context.drawImage(video, 0, 0, 640, 480);
document.getElementById('latest-photo').src = canvas.toDataURL('image/jpeg', 0.5);
self.video.pause();
$("#cam-preview-container").hide();
$("#photo-preview-container").show();
}
// Trigger photo take
$("#snap").click(function(){
var delay = parseInt($("#delay").val());
setTimeout(snap, delay*1000);
});
$("#other-photo-btn").click(function(){
self.video.play();
$("#cam-preview-container").show();
$("#photo-preview-container").hide();
});
}
/*
* Implementation of toBlob(dataURI, mimetype)
*
* - Convert DataURI in Blob object.
* - Return Blob
*/
function toBlob(dataURI, mimetype) {
if (!dataURI) {
return new Uint8Array(0);
}
var BASE64_MARKER = ';base64,';
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var base64 = dataURI.substring(base64Index);
var raw = window.atob(base64);
var uInt8Array = new Uint8Array(raw.length);
for (var i = 0; i < uInt8Array.length; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: mimetype});
}
//Append function in Window tree
window.toBlob = toBlob;
return(this);
});
</script>
<!-- See My Party -->
<script type="text/javascript">
//Start Camera
App.controller.dashboard.start();
//For share photo
$("#share-photo-btn").click(App.controller.dashboard.publishPhoto);
//For Preferennces
$("#config-btn").click(function(){
$('#preferences').modal({show: true});
});
</script>
</body>
</html>
Resultado
Quinto passo
Utilizando o exemplo acima, iremos deixar a foto gerada em preto e branco. Ver código.
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/3.2.1/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/2.3.2/css/bootstrap.min.css">
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js"></script>
</head>
<body>
<style type="text/css">
.hide { display:none; }
</style>
<div class="container">
<div class="hide cam-disable">
<p><i class="icon-camera-retro icon-4x"></i> Habilitar camera</p>
</div>
<div id="cam-preview-container">
<h2>Sorria!</h2>
<div class="row-fluid">
<div id="cam-preview" class="span12">
<video id="video" width="640" height="480" autoplay></video>
<canvas id="canvas" class="hide" width="640" height="480"></canvas>
</div>
<div class="span12">
<a id="snap" class="btn btn-large btn-success "><i class="icon-camera-retro"></i> Tirar foto</a>
<span class="pull-left offset1">
<span>1 s</span> <input id="delay" type="range" min='1' max="15" value="3"> <span>15 s</span>
</span>
</div>
</div>
</div>
<div id="photo-preview-container" class="hide">
<h1>Sorria!</h1>
<div class="row-fluid">
<div class="span12">
<img id="latest-photo" src="" width="640" height="480">
</div>
<div class="span12">
<span class="pull-left offset2"><a id="other-photo-btn" class="btn btn-large btn-primary"><i class="icon-camera-retro"></i> Tirar outra foto</a></span>
</div>
</div>
</div>
<a id='toggle-cam-status-btn' class="cam-stop-btn" onclick="App.controller.dashboard.toogleCam(this);"><i class="icon-eye-close"></i> <span>Desligar camera</span></a>
</div>
<script type="text/javascript">
if(!App) var App = new function(){ return(this); }
if(!App.controller) App.controller = new function(){ return(this); }
App.controller.dashboard = new (function(){
var self = this;
this.localMediaStream = null;
this.toogleCam = function(e){
if($(e).attr('class') === 'cam-stop-btn'){
self.stop(); $(e).text('').attr('class', 'cam-start-btn');
$('<i class="icon-eye-open"></i> <span>Open Camera</span>').appendTo($(e));
return;
}
if($(e).attr('class') === 'cam-start-btn'){
self.start(); $(e).text('').attr('class', 'cam-stop-btn');
$('<i class="icon-eye-close"></i> <span>Close Camera</span>').appendTo($(e));
return;
}
}
this.stop = function(){
if(self.video) self.video.pause();
if(self.localMediaStream){
self.localMediaStream.stop();
self.localMediaStream = null;
}
}
this.start = function(response){
// Grab elements, create settings, etc.
var canvas = self.canvas = document.getElementById("canvas"),
context = self.context = canvas.getContext("2d"),
video = self.video = document.getElementById("video"),
videoObj = { "video": true };
function errBack(error) {
console.log("Video capture error: ", error.code);
$("#cam-preview-container").hide();
$(".cam-disable").show();
};
if(self.localMediaStream === null){
// Put video listeners into place
if(navigator.getUserMedia) { // Standard
navigator.getUserMedia(videoObj, function(stream) {
video.src = self.localMediaStream = stream;
video.play();
}, errBack);
} else if(navigator.webkitGetUserMedia) { // WebKit-prefixed
navigator.webkitGetUserMedia(videoObj, function(stream){
self.localMediaStream = stream;
video.src = window.webkitURL.createObjectURL(stream);
video.play();
}, errBack);
} else if(navigator.mozGetUserMedia){ //Moz-prefixed
navigator.mozGetUserMedia(videoObj, function(stream){
video.mozSrcObject = self.localMediaStream = stream;
video.play();
}, errBack);
}
} else {
if(navigator.getUserMedia) video.src = self.localMediaStream;
if(navigator.mozGetUserMedia) video.src = self.localMediaStream;
if(navigator.webkitGetUserMedia) video.src = window.webkitURL.createObjectURL(self.localMediaStream);
video.play();
}
function snap() {
context.drawImage(video, 0, 0, 640, 480);
//document.getElementById('latest-photo').src = canvas.toDataURL('image/jpeg', 0.5);
//Transform image color to grayscale
var imageObj = new Image();
imageObj.src = canvas.toDataURL('image/jpeg', 0.5);
imageObj.onload = function() {
(function(imageObj){
var canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 480;
var context = canvas.getContext('2d');
var x,y; x=y=0;
context.drawImage(imageObj, x, y);
var imageData = context.getImageData(x, y, imageObj.width, imageObj.height);
var data = imageData.data;
for(var i = 0; i < data.length; i += 4) {
var brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
// red
data[i] = brightness;
// green
data[i + 1] = brightness;
// blue
data[i + 2] = brightness;
}
// overwrite original image
context.putImageData(imageData, x, y);
document.getElementById('latest-photo').src = canvas.toDataURL('image/jpeg', 0.5);
})(this);
};
self.video.pause();
$("#cam-preview-container").hide();
$("#photo-preview-container").show();
}
// Trigger photo take
$("#snap").click(function(){
var delay = parseInt($("#delay").val());
setTimeout(snap, delay*1000);
});
$("#other-photo-btn").click(function(){
self.video.play();
$("#cam-preview-container").show();
$("#photo-preview-container").hide();
});
}
/*
* Implementation of toBlob(dataURI, mimetype)
*
* - Convert DataURI in Blob object.
* - Return Blob
*/
function toBlob(dataURI, mimetype) {
if (!dataURI) {
return new Uint8Array(0);
}
var BASE64_MARKER = ';base64,';
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var base64 = dataURI.substring(base64Index);
var raw = window.atob(base64);
var uInt8Array = new Uint8Array(raw.length);
for (var i = 0; i < uInt8Array.length; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: mimetype});
}
//Append function in Window tree
window.toBlob = toBlob;
return(this);
});
</script>
<!-- See My Party -->
<script type="text/javascript">
//Start Camera
App.controller.dashboard.start();
//For share photo
$("#share-photo-btn").click(App.controller.dashboard.publishPhoto);
//For Preferennces
$("#config-btn").click(function(){
$('#preferences').modal({show: true});
});
</script>
</body>
</html>
Resultado
Bibliotecas
As bibliotecas para desenvolvimento de aplicações em Canvas tornam a manipulação de objetos primitivos(quadrado, círculo, triângulo, retângulo) mais intuitiva e prática além de permitir o uso de recursos físicos(simulação de gravidade e monitoramento de colisão). É importante salientar que para aplicações com um nível de complexidade pequeno não é recomendado o uso de bibliotecas pois será um gasto de recursos computacionais desnecessário. Existem dois tipos de bibliotecas para desenvolvimento de aplicações e jogos, especificamente para jogos podemos dividi-las em "game makers libraries" e "game engine libraries".
Game makers Libraries
Estas bibliotecas permitem o desenvolvimento rápido de aplicações, disponibilizando elementos gráficos prontos para o desenvolvedor, porém deixa o desenvolvimento limitado aos recursos que a biblioteca oferece. Um ótimo "game maker" é o Construct 2.
Game engine libraries
São bibliotecas que oferecem em sua maioria suporte para desenvolvimento de aplicações em 2D e 3D, com suporte para carregamento de conteúdo, sprites de imagem e áudio, recursos físicos(colisão) e objetos primitivos prontos(círculo, quadrado e etc).
Bibliotecas recomendadas (Game engines)
Iniciando em canvas usando bibliotecas
Collie- Criando um quadrado
<!DOCTYPE html> <html> <head> <title>Rectangle Demo | Collie library</title> <script type="text/javascript" src="/experiences/libs/collie/collie.min.js"></script> </head> <body> <div id="container" class="container"></div> <script type="text/javascript"> var layer = new collie.Layer({ width : 300, height : 300 }); var box = new collie.DisplayObject({ width : 50, height : 50, x : 10, y : 10, backgroundColor : 'red' }).addTo(layer); collie.Renderer.addLayer(layer); collie.Renderer.load(document.getElementById("container")); collie.Renderer.start(); </script> </body> </html>
- Repetindo plano de fundo
<!DOCTYPE html> <html> <head> <title>Background Repeat Demo | Collie library</title> <script type="text/javascript" src="/experiences/libs/collie/collie.min.js"></script> </head> <body> <div id="container" class="container"></div> <button id="start-btn">Iniciar</button> <script type="text/javascript"> var layer = new collie.Layer({ width : 300, height : 300 }); collie.ImageManager.add({ "ground" : "/experiences/canvas_tutorial/img/background-repeat/ground.png", "sky" : "/experiences/canvas_tutorial/img/background-repeat/sky.png" }); var oBackground = new collie.DisplayObject({ x : 0, y : 0, width : 300, height : 300, backgroundRepeat : "repeat", // Repeat X-Axis backgroundImage : "sky" }).addTo(layer); var oGround = new collie.DisplayObject({ x : 0, width : 320 * 2, // Using Double width for continuously horizontal move. height : 88, velocityX : -80, backgroundRepeat : "repeat-x", // Repeat X-Axis rangeX : [-320, 0], // This object can move from first position to second position. positionRepeat : true, // This object move the other side when It's on one end of the edge. backgroundImage : "ground" }).bottom(0).addTo(layer); collie.Renderer.addLayer(layer); collie.Renderer.load(document.getElementById("container")); //collie.Renderer.start(); </script> <!-- Start/pause animation --> <script type="text/javascript"> document.getElementById("start-btn").addEventListener("click", function(e){ var elem = e.target; if(elem.getAttribute("started") === null || elem.getAttribute("started") === "false"){ collie.Renderer.start(); elem.textContent = "Parar"; elem.setAttribute("started", "true"); } else { collie.Renderer.stop(); elem.textContent = "Iniciar"; elem.setAttribute("started", "false"); } }, false); </script> </body> </html>
- Pong
<!DOCTYPE html> <html> <head> <title>Pong Demo | Crafty</title> <script type="text/javascript" src="/experiences/libs/crafty/crafty-min.js"></script> <style> body, html { margin:0; padding: 0; overflow:hidden; font-family:Arial; font-size:20px } #cr-stage { color:white } </style> </head> <body> <script type="text/javascript"> Crafty.init(600, 300); Crafty.background('rgb(127,127,127)'); Crafty.scene("game", function() { //Paddles Crafty.e("Paddle, 2D, DOM, Color, Multiway") .color('rgb(255,0,0)') .attr({ x: 20, y: 100, w: 10, h: 100 }) .multiway(4, { W: -90, S: 90 }); Crafty.e("Paddle, 2D, DOM, Color, Multiway") .color('rgb(0,255,0)') .attr({ x: 580, y: 100, w: 10, h: 100 }) .multiway(4, { UP_ARROW: -90, DOWN_ARROW: 90 }); //Ball Crafty.e("2D, DOM, Color, Collision") .color('rgb(0,0,255)') .attr({ x: 300, y: 150, w: 10, h: 10, dX: Crafty.math.randomInt(2, 5), dY: Crafty.math.randomInt(2, 5) }) .bind('EnterFrame', function () { //hit floor or roof if (this.y <= 0 || this.y >= 290) this.dY *= -1; if (this.x > 600) { this.x = 300; Crafty("LeftPoints").each(function () { this.text(++this.points + " Points") }); } if (this.x < 10) { this.x = 300; Crafty("RightPoints").each(function () { this.text(++this.points + " Points") }); } this.x += this.dX; this.y += this.dY; }) .onHit('Paddle', function () { this.dX *= -1; }) //Score boards Crafty.e("LeftPoints, DOM, 2D, Text") .attr({ x: 20, y: 20, w: 100, h: 20, points: 0 }) .text("0 Points"); Crafty.e("RightPoints, DOM, 2D, Text") .attr({ x: 515, y: 20, w: 100, h: 20, points: 0 }) .text("0 Points"); }) Crafty.e("2D, DOM, Text").attr({x:250, y:130, w: 300 }).text("Click to play..."); Crafty.e("2D, DOM, Mouse").attr({x:0, y:0, h:300, w:600 }).bind("Click", function() { Crafty.scene("game");}) </script> </body> </html>
- Village Game
Leia o tutorial passo a passo de como criar o jogo.
- Planetas
Veja o exemplo
var canvas = oCanvas.create({ canvas: "#canvas", background: "#222" }); // Center planet var center = canvas.display.ellipse({ x: canvas.width / 2, y: canvas.height / 2, radius: canvas.width / 20, fill: "#fff" }).add(); // Prototype objects that will be used to instantiate the others var satelliteProto = canvas.display.ellipse({ fill: "#eee" }); var pathProto = canvas.display.ellipse({ stroke: "1px #999" }); // Set up data var satellites = [], depth = 3; var satelliteColors = ["#107B99", "#5F92C0", "#c7509f"]; var pathColors = ["#666", "#107B99", "#5F92C0"]; // Create seven satellites and paths. Definition is further down. for (var i = 0, l = 7; i < l; i++) { createSatellite({ parent: center, depth: 1, distance: (i + 1) * canvas.width / 6, radius: canvas.width / 100, speed: 1 }); } // Set up a tick function that will move all satellites each frame canvas.setLoop(function () { for (var i = 0, l = satellites.length; i < l; i++) { satellites[i].rotation += satellites[i].speed; } }); // Definition for a satellite and its corresponding path function createSatellite (options) { // Create the path that the satellite will follow var path = pathProto.clone({ radius: options.distance, x: options.x || 0, y: options.y || 0, strokeColor: pathColors[options.depth - 1] }); options.parent.addChild(path); // Create a new satellite var satellite = satelliteProto.clone({ origin: { x: 0, y: options.distance * (Math.round(Math.random()) ? 1 : -1) }, speed: Math.random() * (2 * Math.random() - 0.5) + 0.5, radius: options.radius, x: options.x || 0, y: options.y || 0, fill: satelliteColors[options.depth - 1], rotation: Math.random() * 360 }); options.parent.addChild(satellite); satellites.push(satellite); // Create another satellite that will circle around this satellite if (options.depth < depth) { createSatellite({ parent: satellite, depth: options.depth + 1, distance: options.radius * 7, radius: options.radius / 1.5, x: satellite.origin.x * -1, y: satellite.origin.y * -1, speed: 10 }); } }
Som
Em aplicações onde utiliza-se o recurso de animação, geralmente faz-se necessário o uso de sons que são reproduzidos de acordo com as ações do usuário por exemplo. Abaixo está uma lista de biblioteca que fazem "sprite" de um arquivo de áudio e bibliotecas que sintetizam o áudio.
Conclusões
A formalização do elemento canvas pela W3C, está refolucionando a Web como conhecemos, sendo um meio onde uma aplicação pode funcionar em diversas plataformas (Sitemas operacionais e arquiteturas), sem a necessidade do desenvolvedor se preocupar com os recursos necessários para executar sua aplicação. Este elemento tornou-se possível criar softwares que só eram possíveis no desktop, atualmente ferramentas CAD online são criadas a partir de canvas, jogos 2D e 3D e animações, recursos disponíveis antes somente através de plugins como Silverlight e Flash e com um diferencial da soluções em nuvem.
Com o canvas
o desenvolvedor web tem a possibilidade de criar algoritmos para processamento de um arquivo que não há suporte nativo do browser e usar o contexto 2D ou 3D para exibir um determinado arquivo, os exemplos de aplicações que utilizam isso, são os leitores de PDF nativos dos browsers(Chrome e Firefox) e a Flash VM que tem como objetivo processar arquivos flash sem a necessidade do plugin com praticamente a mesma performance, como nesse jogo de corrida.
Além do canvas
muitas aplicações vem utilizando CSS3(Cascading Style Sheets) para realizar animações não havendo a necessidade da utilização da Web API de canvas. Essa abordagem trás um performance um pouco maior devido a inexistência do processamento do código em javascript pela js engine, uma aplicação muito interessante dessa técnica é uma outra ferramenta CAD online chamada Tridiv.