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);
});
Related
Currently I have a circle in my canvas, and the circle follows the mouse around. The canvas updates periodically, so that the old circles are erased so it gives the impression that there is one circle that follows the mouse around. I did this using the setMousePosition function. The function is called in the update function that looks like this
function update() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(mouseX, mouseY, 50, 0, 2 * Math.PI, true);
context.clip();
var img = new Image()
img.addEventListener("load", function(e) {
context.fillStyle = context.createPattern(this, "no-repeat")
context.fill()
}, true);
img.src="pics/iris.jpg"
requestAnimationFrame(update);
}
update();
As you can see, img is sourced at pics/iris.jpg
Why is the picture not filling the circle?
Trying to wrap my head around the HTML5 canvas, I thought I'd create an image carousel, where images would be changed by an opacity gradient sweep, i.e. the same thing as in my fiddle here, only with canvas. I managed to come up with this fiddle, but I can't understand at all what's happening, or rather, why nothing is.
Here's the code:
var outputCanvas = document.getElementById('output'),
ctx = outputCanvas.getContext('2d'),
eWidth = 50,
speed = 5,
cWidth = 480,
img = document.getElementById('newimg'),
x = 0, y = 0,
reqAnimFrame = window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
ctx.drawImage(img, 0, 0, img.width, img.height);
ctx.globalCompositeOperation = "destination-out";
function draw() {
console.log(x);
gradient = ctx.createLinearGradient(x, 0, x+eWidth, 0);
gradient.addColorStop(0, "rgba(255, 255, 255, 0)");
gradient.addColorStop(1, "rgba(255, 255, 255, 1)");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, img.width, img.height);
}
function animate() {
if (x < 480) {
x += Math.floor((cWidth / 1000) * speed);
console.log(x);
draw();
reqAnimFrame(animate);
}
}
reqAnimFrame(animate);
Calling the draw function by itself it seems to work, but once I start firing it with RequestAnimationFrame it just stops working. The gradient gets drawn once, but even though x is updated in the animation loop, the gradient stays put.
I guess there's something I'm just not getting about how canvas and RequestAnimationFrame work.
Note that I'm not looking for a script or library that does the same thing, but rather I'm hoping to actually understand how canvas works, and in particular why my script doesn't.
Here's one way to do a wipe transition between 2 images using Canvas Compositing:
Original Images (before & after):
Canvas during gradient wipe-transition between the images:
create a transparent-to-opaque gradient that is eWidth pixels wide.
clear the canvas
draw the gradient
fill all pixels to the right of the gradient with opaque
draw the first image with source-in compositing. This will display the first image only where the gradient has non-transparent pixels.
draw the second image with 'destination-over' compositing. This will display the second image "under" the existing first image.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw,ch;
var x=0;
var eWidth=100;
var img1=new Image();
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/sailboat.png";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height;
img1.onload=function(){
requestAnimationFrame(animate);
};
img1.src="https://dl.dropboxusercontent.com/u/139992952/multple/sailboat1.png";
}
function draw() {
// create gradient
gradient = ctx.createLinearGradient(x-eWidth,0, x,0);
gradient.addColorStop(0, "rgba(0,0,0, 0)");
gradient.addColorStop(1, "rgba(0,0,0, 1)");
// save the unaltered canvas context
ctx.save();
// clear the canvas
ctx.clearRect(0,0,cw,ch);
// gradient zone
ctx.fillStyle = gradient;
ctx.fillRect(x-eWidth,0,eWidth,ch);
// fully original right of x
ctx.fillStyle='black';
ctx.fillRect(x,0,cw,ch);
// original image with gradient "dissolve" on left
// set compositing to source-in
ctx.globalCompositeOperation='source-in';
ctx.drawImage(img,0,0);
// revealed image
ctx.globalCompositeOperation='destination-over';
ctx.drawImage(img1,0,0);
// restore the context to its unaltered state
ctx.restore();
}
function animate() {
if (x<cw+eWidth){ requestAnimationFrame(animate); }
x+=5;
draw();
}
$('#again').click(function(){
x=0;
requestAnimationFrame(animate);
});
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Wipe transition between images using canvas</h4>
<button id=again>Again</button><br><br>
<canvas id="canvas" width=300 height=300></canvas>
I have a canvas layered over a div and I am trying to paint a rectangle at position 0, 0 on load and move it to another position x, y when needed. The x, y positions I need are returning perfectly and I am using the clearRect(0, 0, canvas.width, canvas.height) method to clear the canvas when I need to move and use the fillRect(x, y, width, height) again to redraw at those specific positions. However although the x, y positions are good and fillRect(..) is being called (I debugged in chrome) the rectangle is only being removed and painted when I repaint it at position 0, 0. Otherwise it just removes. At first I thought that it is being painted but maybe the layering of the div and canvas is being lost but I positioned it somewhere else and no this was not the problem.
This is the code I have maybe someone could kindly see something wrong in my code! Thanks
function removeCursor(connectionId) {
var canvas = document.getElementById(connectionId);
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function paintCursor(x, y, connectionId, color) {
var canvas = document.getElementById(connectionId);
var context = canvas.getContext('2d');
context.fillStyle = color;
context.fillRect(x, y, 0.75, 5);
}
// the canvas is created on someone connecting to a specific page
if (someoneConnected) {
var canvas = document.createElement("canvas");
canvas.id = connectionId;
canvas.className = 'canvases';
canvas.style.zIndex = zindex;
zindex++;
var parentDiv = document.getElementById("editor-section");
parentDiv.appendChild(canvas);
paintCursor(0, 0, connectionId, color);
} else { // someone disconnected
var canvas = document.getElementById(connectionId);
canvas.parentNode.removeChild(canvas);
}
I call the methods removeCursor(connectionId) and paintCursor(x, y, connectionId, color) on a user event such as keypress and click. X, Y are the coordinates of the current selection.
Any ideas what's wrong here?
Why don't you re-factor to
function rePaintCursor(x, y, connectionId, color) {
var canvas = document.getElementById(connectionId);
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = color;
context.fillRect(x, y, 0.75, 5);
}
my guess is that, if x and y are correct, the execution order might be in your way.
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.
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.