How to add elements to a canvas that change over time? - javascript

Everytime someone clicks on parts of my website, I'd like to show a circle that becomes larger over time. I thought of doing it via a canvas. So far I managed to add circle to the position where the visitor clicked:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Canvas</title>
</head>
<body>
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #cccccc;">
</canvas>
<script src="bower_components/jquery/dist/jquery.min.js"></script>
<script>
/* Canvas test. */
$(function() {
var c = $("#myCanvas");
// add element that changes over time,
$(c).click(function(e) {
var ctx = this.getContext("2d");
ctx.beginPath();
ctx.arc(e.pageX,e.pageY,40,0,2*Math.PI);
ctx.stroke();
});
});
</script>
</body>
</html>
Is it also possible to have those circles change their radius 1px per 100ms and disappear when their radius in larger than the canvas?
Is it also possible to do this without a canvas?

Solution :
You have to use requestAnimationFrame, to add element and change over time, push in array the point, and draw circle.
/* Canvas test. */
var circles = [];
var canvas = null;
var ctx = null;
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
circles.forEach(function(arg) {
var size = 100 - (new Date() - arg.time) / 10;
if (size <= 0)
return;
ctx.beginPath();
ctx.arc(arg.x, arg.y, size, 0, 2 * Math.PI);
ctx.stroke();
});
requestAnimFrame(loop);
}
$(function() {
canvas = $("#myCanvas")[0];
ctx = canvas.getContext("2d");
window.requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
requestAnimFrame(loop);
});
$(function() {
var c = $("#myCanvas");
// add element that changes over time,
$(c).click(function(e) {
circles.push({
time: +new Date(),
x: e.pageX,
y: e.pageY
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #cccccc;"></canvas>

Is it also possible to have those circles change their radius 1px per 100ms and disappear when their radius in larger than the canvas? YES
You can start an animation loop(use raF) and change/stop the growth
Is it also possible to do this without a canvas? YES
Javasccript + SVG or javascript + css3 but these might not be as easy as canvas
edit: found some other usefull question: Expanding circles with CSS3 animations

Related

Draw on Image loaded inside HTML canvas

I have a HTML canvas in Ionic app.
<canvas id="canvas" color="{{ color }}" width="800" height="600" style="position:relative;"></canvas>
In this canvas, I am loading an image. Below is the code from controller
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var img = new Image();
img.src = $stateParams.imageId;
img.onload = function() {
context.drawImage(img, 0, 0, canvas.width, canvas.height);
}
After the image is loaded, users need the ability to write on the image and circle/highlight certain areas of the image.
Doesn't HTML canvas provide this feature by default? Right now I am not able to annotate anything on the image. How can I achieve this?
You need to implement this yourself.
You can do it by hooking into the mouse click / move events. using your 2d context, draw small rectangle at the mouse's current position if the most moves and the left mouse button is down.
The effect is similar to a Windows paint pencil tool. Here's a simple example.
<html>
<head>
<style>
canvas{
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var isMouseDown = false;
canvas.onmousedown = function(e){
isMouseDown = true;
}
canvas.onmouseup = function(e){
isMouseDown = false;
}
canvas.onmousemove = function(e){
if(isMouseDown === false){
return;
}
var canvasPosition = canvas.getBoundingClientRect();
var x = e.clientX - canvasPosition.left;
var y = e.clientY - canvasPosition.top;
ctx.fillRect(x, y, 2, 2);
};
</script>
</body>
</html>

Moving image in a circular path purely using JavaScript

How can I make the imageObject to move in a circular path? Or more specific, what are the mathematical formulas needed to make it do so?
I am required to use setInterval with a function that caluclates the new coordinates of the picture. setInterval is supposed to call the function at least 20 times a second.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Graphics and animation using HTML5 Canvas</title>
<style>
#the-canvas { border: 1px dashed gray }
</style>
<script>
addEventListener('load', function() {
var x = document.getElementById("the-canvas");
var y = x.getContext("2d");
var imageObject = new Image();
imageObject.onload = function() {
y.drawImage(imageObject, 100, 200);
};
imageObject.src = "image.jpg";
});
</script>
</head>
<body>
<canvas id="the-canvas" width="500" height="400">
Your browser does not support the <canvas> element.
</canvas>
</body>
</html>
Mathematical formulas needed would be cosine in one dimension and sine in the other.
Something like this:
addEventListener('load', function() {
var x = document.getElementById("the-canvas");
var y = x.getContext("2d");
var imageObject = new Image();
var step = 0, radius = 50, speed = 0.05;
function spin() {
y.clearRect(0, 0, x.width, x.height);
y.drawImage(imageObject, 100 + radius * Math.cos(speed * step), 200 + radius * Math.sin(speed * step));
++step;
}
imageObject.onload = function() {
y.drawImage(imageObject, 100, 200);
setInterval(spin, 50); // 20 fps
};
imageObject.src = "image.jpg";
});

Html5 canvas animation. How to reset rectangle when it hits the end

Hey Im experimenting with some html5 animation and so far I have a square that "falls" whenever I press the button. I was wondering how i could have it go back to the top when it hits the bottom and fall again.
My current code is:
<html>
<head>
</head>
<body>
<canvas id="myCanvas" width="200" height="400" style="border:1px solid black;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
function draw (x,y){
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.save();
var side = 10
var up = 10
ctx.clearRect(0,0,200,400);
ctx.fillStyle = "#FF0000";
ctx.fillRect(x,y,up,side);
ctx.restore();
y += 5;
var loopTimer = setTimeout('draw('+x+','+y+')',30);
}
</script>
<button onclick="draw(0,0)">draw</button>
</body>
</html>
Using your variables y, you can simply check if it is below the height of the canvas height.
if( y > c.height ){ // use the canvas height, not the context height
y = 0;
}
Also, the way you're currently calling the timer is a bit inefficient. Instead of :
var loopTimer = setTimeout('draw('+x+','+y+')',30);
I would recommend
var loopTimer = setTimeout(function(){ draw(x,y); },30);
Here's a working example: http://jsfiddle.net/vwcdpLvv/

How do I hand draw on canvas with JavaScript?

Question
How do I draw free (using my mouse / fingers) on a canvas element like you can do it in paint with a pencil?
About this question
There are a lot of questions that want to achieve free hand drawing on canvas:
draw by mouse with HTML5 Canvas
KineticJS - Draw free with mouse
Free drawing on canvas using fabric.js
Sketching with JS
Paint canvas not working properly
Mouse position on canvas painting
Implementing smooth sketching and drawing on the element
So I thought it would be a good idea to make a reference question, where every answer is community wiki and contains a explanation for exactly one JavaScript library / pure JavaScript how to do paint on canvas.
Structure of answers
The answers should be community wiki and use the following template:
## [Name of library](Link to project page)
### Simple example
A basic, complete example. That means it has to contain HTML
and JavaScript. You can start with this:
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<script type='text/javascript' src='http://cdnjs.com/[your library]'></script>
<style type='text/css'>
#sheet {
border:1px solid black;
}
</style>
<script type='text/javascript'>
window.onload=function(){
// TODO: Adjust
}
</script>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
If possible, this example should work with both, mouse and touch events.
[JSFiddle](Link to code on jsfiddle.net)
This solution works with:
<!-- Please test it the following way: Write "Hello World"
Problems that you test this way are:
* Does it work at all?
* Are lines separated?
* Does it get slow when you write too much?
-->
* Desktop computers:
* [Browser + Version list]
* Touch devices:
* [Browser + Version list] on [Device name]
### Import / Export
Some explanations how to import / export user drawn images.
### Line smoothing
Explanations about how to manipulate the line the user draws.
This can include:
* Bézier curves
* Controlling thickness of lines
Fabric.js
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js'></script>
<style type='text/css'>
#sheet {
border:1px solid black;
}
</style>
<script type='text/javascript'>
window.onload=function(){
var canvas = new fabric.Canvas('sheet');
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.width = 5;
canvas.freeDrawingBrush.color = "#ff0000";
}
</script>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
JSFiddle - Demo
The width of the lines can be controlled with canvas.freeDrawingBrush.width.
The color of the lines can be controlled with canvas.freeDrawingBrush.color.
This solution works with:
Desktop computers:
Chrome 33
Firefox 28
Touch devices:
Chrome 34 on Nexus 4
Opera 20 on Nexus 4
Firefox 28 on Nexus 4
Import / Export
Is only possible by serializing the complete canvas, see Tutorial
Line smoothing
Is done automatically and it seems not to be possible to deactivate it.
Plain JavaScript
Simple example
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<style type='text/css'>
#sheet {
border:1px solid black;
}
</style>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
<script type='text/javascript'>
/*jslint browser:true */
"use strict";
var context = document.getElementById('sheet').getContext("2d");
var canvas = document.getElementById('sheet');
context = canvas.getContext("2d");
context.strokeStyle = "#ff0000";
context.lineJoin = "round";
context.lineWidth = 5;
var clickX = [];
var clickY = [];
var clickDrag = [];
var paint;
/**
* Add information where the user clicked at.
* #param {number} x
* #param {number} y
* #return {boolean} dragging
*/
function addClick(x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
/**
* Redraw the complete canvas.
*/
function redraw() {
// Clears the canvas
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
for (var i = 0; i < clickX.length; i += 1) {
if (!clickDrag[i] && i == 0) {
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
} else if (!clickDrag[i] && i > 0) {
context.closePath();
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
} else {
context.lineTo(clickX[i], clickY[i]);
context.stroke();
}
}
}
/**
* Draw the newly added point.
* #return {void}
*/
function drawNew() {
var i = clickX.length - 1
if (!clickDrag[i]) {
if (clickX.length == 0) {
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
} else {
context.closePath();
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
}
} else {
context.lineTo(clickX[i], clickY[i]);
context.stroke();
}
}
function mouseDownEventHandler(e) {
paint = true;
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
if (paint) {
addClick(x, y, false);
drawNew();
}
}
function touchstartEventHandler(e) {
paint = true;
if (paint) {
addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, false);
drawNew();
}
}
function mouseUpEventHandler(e) {
context.closePath();
paint = false;
}
function mouseMoveEventHandler(e) {
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
if (paint) {
addClick(x, y, true);
drawNew();
}
}
function touchMoveEventHandler(e) {
if (paint) {
addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, true);
drawNew();
}
}
function setUpHandler(isMouseandNotTouch, detectEvent) {
removeRaceHandlers();
if (isMouseandNotTouch) {
canvas.addEventListener('mouseup', mouseUpEventHandler);
canvas.addEventListener('mousemove', mouseMoveEventHandler);
canvas.addEventListener('mousedown', mouseDownEventHandler);
mouseDownEventHandler(detectEvent);
} else {
canvas.addEventListener('touchstart', touchstartEventHandler);
canvas.addEventListener('touchmove', touchMoveEventHandler);
canvas.addEventListener('touchend', mouseUpEventHandler);
touchstartEventHandler(detectEvent);
}
}
function mouseWins(e) {
setUpHandler(true, e);
}
function touchWins(e) {
setUpHandler(false, e);
}
function removeRaceHandlers() {
canvas.removeEventListener('mousedown', mouseWins);
canvas.removeEventListener('touchstart', touchWins);
}
canvas.addEventListener('mousedown', mouseWins);
canvas.addEventListener('touchstart', touchWins);
</script>
</body>
</html>
JSFiddle
The width of the lines can be controlled with context.lineWidth.
The color of the lines can be controlled with strokeStyle.
This solution works with:
Desktop computers:
Chrome 33
Firefox 28
Touch devices:
Firefox 28 on Nexus 4
It does not work with
Touch devices:
Chrome 34 / Opera 20 on Nexus 4 (see issue)
Import / Export
Importing and exporting the image can be done by importing / exporting clickX, clickY and clickDrag.
Line smoothing
Can eventually be done by replacing lineTo() with bezierCurveTo()
Plain JS - ES6
Simple example
Plain Javascript example above has some serious issues: it does not reflect the comments objections, the paint state is redundant, events are not unhooked properly, the redraw() function is not used, it can be simplified a lot and it doesn't work with modern syntax. The fix is here:
var canvas = document.getElementById('sheet'), g = canvas.getContext("2d");
g.strokeStyle = "hsl(208, 100%, 43%)";
g.lineJoin = "round";
g.lineWidth = 1;
g.filter = "blur(1px)";
const
relPos = pt => [pt.pageX - canvas.offsetLeft, pt.pageY - canvas.offsetTop],
drawStart = pt => { with(g) { beginPath(); moveTo.apply(g, pt); stroke(); }},
drawMove = pt => { with(g) { lineTo.apply(g, pt); stroke(); }},
pointerDown = e => drawStart(relPos(e.touches ? e.touches[0] : e)),
pointerMove = e => drawMove(relPos(e.touches ? e.touches[0] : e)),
draw = (method, move, stop) => e => {
if(method=="add") pointerDown(e);
canvas[method+"EventListener"](move, pointerMove);
canvas[method+"EventListener"](stop, g.closePath);
};
canvas.addEventListener("mousedown", draw("add","mousemove","mouseup"));
canvas.addEventListener("touchstart", draw("add","touchmove","touchend"));
canvas.addEventListener("mouseup", draw("remove","mousemove","mouseup"));
canvas.addEventListener("touchend", draw("remove","touchmove","touchend"));
<canvas id="sheet" width="400" height="400" style="border: 1px solid black"></canvas>
Support
It should work everywhere today. I could be further simplified by pointer events, but Safari lacks support for it as of 2021.
Import / Export
For import, use g.drawImage()
g.drawImage(img, 0, 0);
For export, see canvas.toBlob()
function save(blob) {
var fd = new FormData();
fd.append("myFile", blob);
// handle formData to your desire here
}
canvas.toBlob(save,'image/jpeg');
Line smoothing
For antialiasing, See blur() from SVG filters; if you import, don't forget to apply it AFTER the image is imported
context.filter = "blur(1px)";
Paper.js
Simple example
<!DOCTYPE html>
<html>
<head>
<title>Paper.js example</title>
<script type='text/javascript' src='http://paperjs.org/assets/js/paper.js'></script>
<style type='text/css'>
#sheet {
border:1px solid black;
}
</style>
</head>
<body>
<script type="text/paperscript" canvas="sheet">
var path;
function onMouseDown(event) {
// If we produced a path before, deselect it:
if (path) {
path.selected = false;
}
// Create a new path and set its stroke color to black:
path = new Path({
segments: [event.point],
strokeColor: 'black',
strokeWidth: 3
});
}
// While the user drags the mouse, points are added to the path
// at the position of the mouse:
function onMouseDrag(event) {
path.add(event.point);
}
// When the mouse is released, we simplify the path:
function onMouseUp(event) {
path.simplify();
}
</script>
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
JSFiddle
The width of the lines can be controlled with strokeWidth.
The color of the lines can be controlled with strokeColor.
This solution works with:
Desktop computers:
Chrome 33
Import / Export
?
Line smoothing
Line smoothing can be done by adjusting path.simplify();.
EaselJs
Simple example
A basic, complete example. That means it has to contain HTML
and JavaScript. You can start with this:
<!DOCTYPE html>
<html>
<head>
<title>EaselJS example</title>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/EaselJS/0.7.1/easeljs.min.js"></script>
<script>
var canvas, stage;
var drawingCanvas;
var oldPt;
var oldMidPt;
var color;
var stroke;
var index;
function init() {
if (window.top != window) {
document.getElementById("header").style.display = "none";
}
canvas = document.getElementById("sheet");
index = 0;
//check to see if we are running in a browser with touch support
stage = new createjs.Stage(canvas);
stage.autoClear = false;
stage.enableDOMEvents(true);
createjs.Touch.enable(stage);
createjs.Ticker.setFPS(24);
drawingCanvas = new createjs.Shape();
stage.addEventListener("stagemousedown", handleMouseDown);
stage.addEventListener("stagemouseup", handleMouseUp);
stage.addChild(drawingCanvas);
stage.update();
}
function stop() {}
function handleMouseDown(event) {
color = "#ff0000";
stroke = 5;
oldPt = new createjs.Point(stage.mouseX, stage.mouseY);
oldMidPt = oldPt;
stage.addEventListener("stagemousemove" , handleMouseMove);
}
function handleMouseMove(event) {
var midPt = new createjs.Point(oldPt.x + stage.mouseX>>1, oldPt.y+stage.mouseY>>1);
drawingCanvas.graphics.clear().setStrokeStyle(stroke, 'round', 'round').beginStroke(color).moveTo(midPt.x, midPt.y).curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);
oldPt.x = stage.mouseX;
oldPt.y = stage.mouseY;
oldMidPt.x = midPt.x;
oldMidPt.y = midPt.y;
stage.update();
}
function handleMouseUp(event) {
stage.removeEventListener("stagemousemove" , handleMouseMove);
}
</script>
</head>
<body onload="init();">
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
Demo
The interesting parts in the documentation are:
EaselJS: A starting point for getting into EaselJS.
Stage Class:
This solution works with:
Desktop computers:
Chrome 33
Firefox 28
Touch devices:
Chrome 34 / Firefox 28 / Opera 20 on Nexus 4
Import / Export
?
Line smoothing
?
Here, try my canvas free drawing and erase.
https://jsfiddle.net/richardcwc/d2gxjdva/
//Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//Variables
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;
var tooltype = 'draw';
//Mousedown
$(canvas).on('mousedown', function(e) {
last_mousex = mousex = parseInt(e.clientX-canvasx);
last_mousey = mousey = parseInt(e.clientY-canvasy);
mousedown = true;
});
//Mouseup
$(canvas).on('mouseup', function(e) {
mousedown = false;
});
//Mousemove
$(canvas).on('mousemove', function(e) {
mousex = parseInt(e.clientX-canvasx);
mousey = parseInt(e.clientY-canvasy);
if(mousedown) {
ctx.beginPath();
if(tooltype=='draw') {
ctx.globalCompositeOperation = 'source-over';
ctx.strokeStyle = 'black';
ctx.lineWidth = 3;
} else {
ctx.globalCompositeOperation = 'destination-out';
ctx.lineWidth = 10;
}
ctx.moveTo(last_mousex,last_mousey);
ctx.lineTo(mousex,mousey);
ctx.lineJoin = ctx.lineCap = 'round';
ctx.stroke();
}
last_mousex = mousex;
last_mousey = mousey;
//Output
$('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+last_mousex+', '+last_mousey+'<br/>mousedown: '+mousedown);
});
//Use draw|erase
use_tool = function(tool) {
tooltype = tool; //update
}
canvas {
cursor: crosshair;
border: 1px solid #000000;
}
<canvas id="canvas" width="800" height="500"></canvas>
<input type="button" value="draw" onclick="use_tool('draw');" />
<input type="button" value="erase" onclick="use_tool('erase');" />
<div id="output"></div>
(Disclaimer: I wrote this library)
Scrawl.js
Simple example
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<style type='text/css'>
#sheet {border:1px solid black;}
</style>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
<script src="http://scrawl.rikweb.org.uk/js/scrawlCore-min.js"></script>
<script>
var mycode = function(){
//define variables
var myPad = scrawl.pad.sheet,
myCanvas = scrawl.canvas.sheet,
sX, sY, here,
drawing = false,
currentSprite = false,
startDrawing,
endDrawing;
//event listeners
startDrawing = function(e){
drawing = true;
currentSprite = scrawl.newShape({
start: here,
lineCap: 'round',
lineJoin: 'round',
method: 'draw',
lineWidth: 4,
strokeStyle: 'red',
data: 'l0,0 ',
});
sX = here.x;
sY = here.y;
if(e){
e.stopPropagation();
e.preventDefault();
}
};
myCanvas.addEventListener('mousedown', startDrawing, false);
endDrawing = function(e){
if(currentSprite){
currentSprite = false;
}
drawing = false;
if(e){
e.stopPropagation();
e.preventDefault();
}
};
myCanvas.addEventListener('mouseup', endDrawing, false);
//animation object
scrawl.newAnimation({
fn: function(){
//get current mouse position
here = myPad.getMouse();
if(here.active){
if(drawing){
if(here.x !== sX || here.y !== sY){
//extend the line
currentSprite.set({
data: currentSprite.data+' '+(here.x - sX)+','+(here.y - sY),
});
sX = here.x;
sY = here.y;
}
}
}
else{
//stop drawing if mouse leaves canvas area
if(currentSprite){
endDrawing();
}
}
//update display
scrawl.render();
},
});
};
//Scrawl is modular - load additional modules
scrawl.loadModules({
path: 'js/',
modules: ['animation', 'shape'],
callback: function(){
window.addEventListener('load', function(){
scrawl.init(); //start Scrawl
mycode(); //run code
}, false);
},
});
</script>
</body>
</html>
JSFiddle
This solution works with:
recent versions of IE, Chrome, Firefox, Opera (desktop)
(not tested on mobile/touch devices)
Adding touch support
(try adding a dedicated touch library like Hammer.js?)
Import / Export
Scrawl has experimental support for saving and loading JSON strings
tutorial page: load and save
Line smoothing and other sprite manipulations
line data is saved internally as an SVGTiny Path.d value - any algorithm that can take line data in that format and smooth it should work
line attributes - thickness, color, positioning, rotation, etc - can be set, and animated.

Canvas: animating a simple star field

I'm relatively new to Canvas. In finding my feet I'm creating a simple arcade game.
My question is regarding CPU performance / efficiency.
I'm creating 100 randomly positioned white dots as stars on a black background. On each requestAnimationFrame, the stars move one pixel to the left and as they get to the extreme left that column of pixels is placed on the extreme right of the screen.
Using requestAnimationFrame I'm calling the following function:
bgAnimateId = requestAnimationFrame( scrollBg );
function scrollBg() {
var imgData = ctx.getImageData( 0, 0, 1, canvas.height );
var areaToMoveLeft = ctx.getImageData( 1, 0, canvas.width-1, canvas.height );
ctx.putImageData( areaToMoveLeft, 0, 0 );
ctx.putImageData( imgData, canvas.width-1, 0 );
bgAnimateId = requestAnimationFrame( scrollBg );
}
My concern is - would it be better to create 100 small canvas elements (or 100 divs) and animate those, or is it better to utilise the pixel methods that I've used above.
Many thanks for your help / guidance in advance :-)
It turns out that context.getImageData and context.putImageData are very expensive to perform and having 100 canvases is too many.
So here’s a plan for creating an efficient panning starfield:
Using context.drawImage is very efficient and not very expensive to perform.
Here’s how to draw a random starfield on a canvas and then save that canvas as an image:
// draw a random starfield on the canvas
bkCtx.beginPath();
bkCtx.fillStyle="darkblue";
bkCtx.rect(0,0,background.width,background.height);
bkCtx.fill();
bkCtx.beginPath();
for(var n=0;n<100;n++){
var x=parseInt(Math.random()*canvas.width);
var y=parseInt(Math.random()*canvas.height);
var radius=Math.random()*3;
bkCtx.arc(x,y,radius,0,Math.PI*2,false);
bkCtx.closePath();
}
bkCtx.fillStyle="white";
bkCtx.fill();
// create an new image using the starfield canvas
var img=document.createElement("img");
img.src=background.toDataURL();
You will have 2 kinds of drawing going on:
A panning background of stars
A foreground where your game objects will be drawn.
So create 2 canvases aligned on top of each other. The back canvas is for the stars and the front canvas for you game objects.
This is the background canvas that pans the moving image of the starfield:
This is the foreground canvas where your game objects go -- see my cheesy “rocket”
These are the 2 canvases stacked to create a background/foreground combination:
Here is the Html+CSS to stack the 2 canvases:
<div id="container">
<canvas id="background" class="subcanvs" width=300; height=300;></canvas>
<canvas id="canvas" class="subcanvs" width=300; height=300;></canvas>
</div>
#container{
position:relative;
border:1px solid blue;
width:300px;
height:300px;
}
.subcanvs{
position:absolute;
}
Here’s how to use the starfield image to create a panning starfield on the background canvas:
var fps = 60;
var offsetLeft=0;
panStars();
function panStars() {
// increase the left offset
offsetLeft+=1;
if(offsetLeft>backImage.width){ offsetLeft=0; }
// draw the starfield image and
// draw it again to fill the empty space on the right of the first image
bkCtx.clearRect(0,0,background.width,background.height);
bkCtx.drawImage(backImage,-offsetLeft,0);
bkCtx.drawImage(backImage,backImage.width-offsetLeft,0);
setTimeout(function() {
requestAnimationFrame(panStars);
}, 1000 / fps);
}
Now the front canvas is used for all your game objects.
Your game is efficient and performant with 2 canvases dedicated to their own purposes.
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/5vfVb/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{padding:20px;}
#container{
position:relative;
border:1px solid blue;
width:300px;
height:300px;
}
.subcanvs{
position:absolute;
}
</style>
<script>
$(function(){
// Paul Irish's great RAF shim
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var background=document.getElementById("background");
var bkCtx=background.getContext("2d");
// create an image of random stars
var backImage=RandomStarsImage();
// draw on the front canvas
ctx.beginPath();
ctx.fillStyle="red";
ctx.rect(75,100,100,50);
ctx.arc(175,125,25,0,Math.PI*2,false);
ctx.closePath();
ctx.fill();
// start panning the random stars image across the background canvas
var fps = 60;
var offsetLeft=0;
panStars();
function panStars() {
// increase the left offset
offsetLeft+=1;
if(offsetLeft>backImage.width){ offsetLeft=0; }
// draw the starfield image and draw it again
// to fill the empty space on the right of the first image
bkCtx.clearRect(0,0,background.width,background.height);
bkCtx.drawImage(backImage,-offsetLeft,0);
bkCtx.drawImage(backImage,backImage.width-offsetLeft,0);
setTimeout(function() {
requestAnimFrame(panStars);
}, 1000 / fps);
}
function RandomStarsImage(){
// draw a random starfield on the canvas
bkCtx.beginPath();
bkCtx.fillStyle="darkblue";
bkCtx.rect(0,0,background.width,background.height);
bkCtx.fill();
bkCtx.beginPath();
for(var n=0;n<100;n++){
var x=parseInt(Math.random()*canvas.width);
var y=parseInt(Math.random()*canvas.height);
var radius=Math.random()*3;
bkCtx.arc(x,y,radius,0,Math.PI*2,false);
bkCtx.closePath();
}
bkCtx.fillStyle="white";
bkCtx.fill();
// create an new image using the starfield canvas
var img=document.createElement("img");
img.src=background.toDataURL();
return(img);
}
}); // end $(function(){});
</script>
</head>
<body>
<div id="container">
<canvas id="background" class="subcanvs" width=300; height=300;></canvas>
<canvas id="canvas" class="subcanvs" width=300; height=300;></canvas>
</div>
</body>
</html>

Categories

Resources