Altering HTML5 Canvas element on keydown (JS, Jquery) - javascript

I'm trying to move something on the canvas upon pressing the left key.
$(document).ready(function () {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.onload = function(){
ctx.drawImage(img,0,0); // draw the image at the right coords
ctx.drawImage(img,110,110); // draw the image at the right coords
ctx.save();
};
img.src = 'tiles/processed/1_grass.png'; // Set source path
function draw() {
ctx.translate(20,0);
};
draw();
draw();
draw();
$(document).keydown(function(e) {
if (e.keyCode == 37) {
draw();
};
});
});
Now, it appears the three draw();'s work, but the one inside the function doesn't.
Am I totally missing the concept of canvas (in that it is static by nature, and has to be entirely re-drawn all the time) or is there something I'm doing wrong?
(ps.: I'm using Jquery as well)
Cheers!

You're never actually redrawing the canvas. You draw once (img.onload) and otherwise only translate the canvas.
Your draw function should clear the canvas and redraw the image.
here is a simple example, building on your code:
$(function () {
var ctx = document.getElementById('canvas').getContext('2d');
function draw() {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.restore();
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 20, 20);
};
draw();
$(document).keydown(function(evt) {
switch(evt.keyCode) {
case 37:
ctx.translate(-5, 0);
break;
case 38:
ctx.translate(0, -5);
break;
case 39:
ctx.translate(5, 0);
break;
case 40:
ctx.translate(0, 5);
break;
}
draw();
});
});​
demo: http://jsfiddle.net/Vx2kQ/
Personally, though, I would not use translate to handle that movement. I would use some x/y coords, stored in a private variable. On keydown I would then manipulate those coords and redraw.

Related

Why is clearRect() not removing anything from my canvas?

I want to clear a full canvas. But using clearRect() isn't doing anything. So how can I remove the drawn picture from my canvas?
<canvas id="myCanvas" width="600" height="580"></canvas>
<script>
window.onload = function() {
canv = document.getElementById("myCanvas");
ctx = canv.getContext("2d");
falcon = document.getElementById("milenium_falcon");
ctx.drawImage(falcon, 315, 500, 75, 75);
document.addEventListener("keydown",keyPush);
setInterval(game,1000/15);
}
x = 0;
function game() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(falcon, 315 - x, 500, 75, 75);
}
function keyPush(evt) {
switch(evt.keyCode) {
case 37:
x -= 5;
break;
case 39:
x += 5;
break;
}
}
</script>
In your call to .clearRect(), you are using the variable canvas instead of canv. It should work if you use this line instead:
ctx.clearRect(0, 0, canv.width, canv.height);

Javascript Canvas clear/ redraw

I've tried googling the answer to this but i'm just going around in circles....
If I clear the rect (using clearRect) then the image doesn't redraw after.
However, if I don't clear the images just stack.
What I want it to clear the current image and then draw with new one.
What am I missing?
Does it have something to do with the image Load ?
Sorry if this is a question repeat, I couldn't find the exact answer to this- and I tried what others suggested but results were poor.
http://jsfiddle.net/bxeuhh4h/
function clear() {
var canvasTemp = document.getElementById(imgSection);
var ctxTemp = canvasTemp.getContext("2d");
ctxTemp.clearRect(0, 0, 500, 500);
}
function fillColorOrPattern(imgSection,currentcolor){
if ((oldcolor !== currentcolor) || (oldxImgToBeFilled !== xImgToBeFilled)){
clear();
}
imgFill.onload = function () {
imgToBeFilled.onload = function () {
if ((oldcolor !== currentcolor) || (oldxImgToBeFilled !== xImgToBeFilled)){
fill(imgSection,currentcolor)
}
};
imgToBeFilled.src = xImgToBeFilled;
}
imgFill.src = xImgFill;
}
function fill(imgSection,currentcolor){
canvas = document.getElementById(imgSection);
ctx = canvas.getContext("2d");
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.globalCompositeOperation = "source-atop";
console.log(isItColorOrPattern);
if (isItColorOrPattern == "color"){
ctx.rect(0, 0, canvas.width, canvas.height);
console.log("currentcolor: " + currentcolor);
ctx.fillStyle = getColor(currentcolor);
console.log(getColor(currentcolor));
ctx.fill();
}else{
var pattern = ctx.createPattern(imgFill, 'repeat');
console.log("canvas.width: " + canvas.width);
console.log("xImgFill: " + xImgFill);
console.log(canvas.getContext);
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = pattern;
ctx.fill();
}
ctx.globalAlpha = .10;
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.drawImage(imgToBeFilled, 0, 0);
oldcolor = currentcolor;
oldxImgToBeFilled = xImgToBeFilled;
}
$(window).load(function(){
imgToBeFilled = new Image();
imgFill = new Image();
fillColorOrPattern(imgSection,currentcolor);
}
You need to add a beginPath() in there. rect() will accumulate rectangles to the path, clearRect() won't clear those. Also reset comp. mode and alpha as they are sticky.
You could avoid beginPath() if you use fillRect() instead of rect() + fill() (added example below) as fillRect() does not add to the path.
function fill(imgSection,currentcolor){
// these should really be initialized outside the loop
canvas = document.getElementById(imgSection);
ctx = canvas.getContext("2d");
// clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// clear path
ctx.beginPath();
// use default comp. mode
ctx.globalCompositeOperation = "source-over";
// reset alpha
ctx.globalAlpha = 1;
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.globalCompositeOperation = "source-atop";
if (isItColorOrPattern === "color"){
// rect() accumulates on path
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = getColor(currentcolor);
ctx.fill();
// instead of rect() + fill() you could have used:
// fillRect() does not accumulate on path
// fillRect(0, 0, canvas.width, canvas.height);
}
else {
var pattern = ctx.createPattern(imgFill, 'repeat');
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = pattern;
ctx.fill();
}
ctx.globalAlpha = .1;
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.drawImage(imgToBeFilled, 0, 0);
ctx.drawImage(imgToBeFilled, 0, 0);
oldcolor = currentcolor;
oldxImgToBeFilled = xImgToBeFilled;
}
Canvas workflow goes like this:
Draw some things on the canvas.
Calculate changes to the position of those things.
Clear the canvas.
Redraw all the things in their new positions.
Canvas does not "remember" where it drew your things so you cannot directly order your things to move.
But you can save the definition of your things in javascript object:
var myCircle={
centerX:50,
centerY:50,
radius:25,
fill:'blue'
}
Then you can "move" your things using the javascript objects:
myCircle.centerX += 5;
And then redraw the things at their new positions. Putting the redraw code in a function makes redrawing easier:
function redraw(){
// clear the canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// redraw one or more things based on their javascript objects
ctx.beginPath();
ctx.arc( myCircle.centerX, myCircle.centerY, myCircle.radius, 0, Math.PI*2 );
ctx.closePath();
ctx.fillStyle=myCircle.fill;
ctx.fill();
}
Putting it all together:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var myCircle={
centerX:50,
centerY:50,
radius:25,
fill:'blue'
}
redraw();
document.getElementById('move').addEventListener('click',function(){
myCircle.centerX+=5;
redraw();
});
function redraw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.arc( myCircle.centerX, myCircle.centerY, myCircle.radius, 0, Math.PI*2 );
ctx.closePath();
ctx.fillStyle=myCircle.fill;
ctx.fill();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<button id=move>Move</button>
<br>
<canvas id="canvas" width=300 height=300></canvas>

How can I capture multiple images out of one in the <canvas>?

I wrote some code that draws a semi-transparent rectangle over an image that you can draw with touch:
function drawRect() {
var canvas = document.getElementById('receipt');
var ctx = canvas.getContext('2d');
var drag = false;
var imageObj;
var rect = { };
var touch;
canvas.width = WIDTH;
canvas.height = HEIGHT;
function init() {
imageObj = new Image();
imageObj.src = 'img.jpg';
imageObj.onload = function() {
ctx.drawImage(imageObj, 0, 0);
};
canvas.addEventListener('touchstart', handleTouch, false);
canvas.addEventListener('touchmove', handleTouch, false);
canvas.addEventListener('touchleave', handleEnd, false);
canvas.addEventListener('touchend', handleEnd, false);
}
function handleTouch(event) {
if (event.targetTouches.length === 1) {
touch = event.targetTouches[0];
if (event.type == 'touchmove') {
if (drag) {
rect.w = touch.pageX - rect.startX;
rect.h = touch.pageY - rect.startY ;
draw();
}
} else {
rect.startX = touch.pageX;
rect.startY = touch.pageY;
drag = true;
}
}
}
function handleEnd(event) {
drag = false;
}
function draw() {
drawImageOnCanvas();
ctx.fillStyle = 'rgba(0, 100, 255, 0.2)';
ctx.fillRect(rect.startX, rect.startY, rect.w, rect.h);
}
function drawImageOnCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imageObj, 0, 0);
}
init();
}
This works. But, now I'd like to have it so that I can capture each of the parts of the image that are in rectangles as separate images.
How can I pull this off? Because I have that redraw stuff, it deletes the previous rectangle, which makes this difficult.
A canvas can only save out the whole canvas as an image, so the trick is to create a temporary canvas the size of the region you want to save out.
One way could be to create a function which takes the image and a rectangle object as argument and returns a data-uri (or Blob) of the region:
function saveRegion(img, rect) {
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
canvas.width = rect.w;
canvas.height = rect.h;
ctx.drawImage(img, rect.startX, rect.startY, rect.w, rect.h, 0, 0, rect.w, rect.h);
return canvas.toDataURL():
}
You can pass in the original image if don't want any graphics on top, or if you draw elements on top of it just pass in the original canvas element as image source. And of course, CORS-restrictions apply.

Javascript canvas: animate box on touch, view only borders

I am trying to do a simple animation with html5. Please take a look at the link below, through a touch screen device.
https://dl.dropbox.com/u/41627/wipe.html
The problem is as follows : Every time the user touches the screen , a box gets drawn around his finger which animates from small to big. I want just the outer most boundary to be visible and not the rest. I do not want to clear the canvas as I want the state of the rest of the canvas to be preserved.
Images to illustrate the issue:
My code is as follows :
function init() {
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var img = document.createElement('IMG');
img.onload = function () {
ctx.beginPath();
ctx.drawImage(img, 0, 0);
ctx.closePath();
ctx.globalCompositeOperation = 'destination-out';
}
img.src = "https://dl.dropbox.com/u/41627/6.jpg";
function drawPoint(pointX,pointY){
var grd = ctx.createRadialGradient(pointX, pointY, 0, pointX, pointY, 30);
grd.addColorStop(0, "rgba(255,255,255,.6)");
grd.addColorStop(1, "transparent");
ctx.fillStyle = grd;
ctx.beginPath();
ctx.arc(pointX,pointY,50,0,Math.PI*2,true);
ctx.fill();
ctx.closePath();
}
var a = 0;
var b = 0;
function boxAround(pointX,pointY, a, b) {
ctx.globalCompositeOperation = 'source-over';
ctx.strokeStyle = "black";
ctx.strokeRect(pointX-a, pointY-b, (2*a), (2*b));
ctx.globalCompositeOperation = 'destination-out';
if(a < 100) {
setTimeout(function() {
boxAround(pointX,pointY, a+5, b+5);
}, 20);
}
}
canvas.addEventListener('touchstart',function(e){
drawPoint(e.touches[0].screenX,e.touches[0].screenY);
boxAround(e.touches[0].screenX,e.touches[0].screenY,0 , 0);
},false);
canvas.addEventListener('touchmove',function(e){
e.preventDefault();
drawPoint(e.touches[0].screenX,e.touches[0].screenY);
},false);
You can achieve this effect by either using a second canvas, or even just having the box be a plain <div> element that is positioned over the canvas. Otherwise, there is no way around redrawing your canvas.

How to erase on canvas without erasing background

I have a canvas to which I drawimage() to be a background.
I have an eraser tool with which I want to be able to erase what I draw but not the image I have in the canvas. I know can place the image as a separate element behind the canvas but that is not what I am after since I desire to save what is in the canvas as a image.
My drawing function is here:
function draw (event) {
if (event == 'mousedown') {
context.beginPath();
context.moveTo(xStart, yStart);
} else if (event == 'mousemove') {
context.lineTo(xEnd, yEnd);
} else if (event == 'touchstart') {
context.beginPath();
context.moveTo(xStart, yStart);
} else if (event == 'touchmove') {
context.lineTo(xEnd, yEnd);
}
context.lineJoin = "round";
context.lineWidth = gadget_canvas.radius;
context.stroke();
}
If I need to explain further I will.
Thank you in advance.
There are a few ways you can go about this.
I'd recommend putting the image as the canvas's CSS "background-image". Then draw on the canvas as normal.
When you want to save the Canvas as an image, you will need to do a sequence of events:
Create an in-memory canvas just as big as your normal canvas. Call it can2
ctx2.drawImage(can1, 0, 0) // paint first canvas onto new canvas
ctx.clearRect(0, 0, width, height) // clear first canvas
ctx.drawImage(background, 0, 0) // draw image on first canvas
ctx.drawImage(can2, 0, 0) // draw the (saved) first canvas back to itself
This will let you have the best of both worlds.
I saved the image path in the an array, when I cleared the canvas I call the init function again:
$( ".clear_canvas" ).click(function() {
console.log("clear");
var canvas = document.getElementById("canvasMain"),
ctx=canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
//load the image again
init(path,width,height);
});

Categories