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?
Related
I am working on an app which uses canvas. I draw some shapes, one over another which can be filled with colours or images and overlap each other. I use clip() to clip images to fit shape, but when I change globalCompositeOperation to multiply it makes clip() stop working. I've created a simple example to present what my problem is.
Please try to open it in Google Chrome and in Mozilla Firefox. While in Chrome image can be clipped then set as a multiply of lower layer, in Mozilla after applying multiply, clipping stops working. Any ideas to solve this issue?
// Grab the Canvas and Drawing Context
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
// Create an image element
var img = document.createElement('IMG');
// When the image is loaded, draw it
img.onload = function () {
ctx.fillStyle="red"
ctx.fillRect(0,0,100,100)
// Save the state, so we can undo the clipping
ctx.save();
// Create a shape, of some sort
ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(100, 30);
ctx.lineTo(180, 10);
ctx.lineTo(200, 60);
ctx.arcTo(180, 70, 120, 0, 10);
ctx.lineTo(200, 180);
ctx.lineTo(100, 150);
ctx.lineTo(70, 180);
ctx.lineTo(20, 130);
ctx.lineTo(50, 70);
ctx.closePath();
// Clip to the current path
ctx.clip();
ctx.globalCompositeOperation="multiply";
ctx.drawImage(img, 0, 0);
// Undo the clipping
ctx.restore();
}
// Specify the src to load the image
img.src = "http://i.imgur.com/gwlPu.jpg";
body {
background: #CEF;
}
<canvas id="c" width="200" height="158"></canvas>
This sounds like a known issue on Windows version of FireFox...
Only FF devs could have provided a real fix, but since the bug has been reported at least two years ago, I wouldn't expect it to be fixed anytime soon.
Now, to workaround the issue, you could draw in two steps:
on a first offscreen canvas do the clipping,
then do the blending on the main canvas using the now clipped one.
This may incur a little memory overhead, but having a second canvas ready might also come handy if you do a lot of compositing/blending.
// Grab the Canvas and Drawing Context
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
// create an off-screen copy of the context
var ctx2 = canvas.cloneNode().getContext('2d');
// Create an image element
var img = document.createElement('IMG');
// When the image is loaded, draw it
img.onload = function () {
// Do the clipping on the offscreen canvas
ctx2.save();
// Create a shape, of some sort
ctx2.beginPath();
ctx2.moveTo(10, 10);
ctx2.lineTo(100, 30);
ctx2.lineTo(180, 10);
ctx2.lineTo(200, 60);
ctx2.arcTo(180, 70, 120, 0, 10);
ctx2.lineTo(200, 180);
ctx2.lineTo(100, 150);
ctx2.lineTo(70, 180);
ctx2.lineTo(20, 130);
ctx2.lineTo(50, 70);
ctx2.closePath();
ctx2.clip();
// draw the image
ctx2.drawImage(img, 0, 0);
ctx2.restore();
// Now go back on the main canvas
ctx.fillStyle = "red";
ctx.fillRect(0,0,100,100);
// do the blending with our now clipped image
ctx.globalCompositeOperation="multiply";
ctx.drawImage(ctx2.canvas,0,0);
}
// Specify the src to load the image
img.src = "http://i.imgur.com/gwlPu.jpg";
body {
background: #CEF;
}
<canvas id="c" width="200" height="158"></canvas>
But for this exact case, i.e multiply blending, you can actually simply get rid of the clipping altogether on a single canvas.
Indeed, if I'm not mistaken, multiply doesn't really cares of which layer is top or bottom, the result will just be the same.
So you could simply do your compositing first, and as a final step do the blending over the composited image.
// Grab the Canvas and Drawing Context
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
// Create an image element
var img = document.createElement('IMG');
// When the image is loaded, draw it
img.onload = function () {
ctx.fillStyle="red";
// Create a shape, of some sort
ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(100, 30);
ctx.lineTo(180, 10);
ctx.lineTo(200, 60);
ctx.arcTo(180, 70, 120, 0, 10);
ctx.lineTo(200, 180);
ctx.lineTo(100, 150);
ctx.lineTo(70, 180);
ctx.lineTo(20, 130);
ctx.lineTo(50, 70);
ctx.closePath();
// fill the current path
ctx.fill();
// draw only where our previous shape was drawn
ctx.globalCompositeOperation="source-atop";
ctx.drawImage(img, 0, 0);
// multiply blending should work the same in two directions
ctx.globalCompositeOperation="multiply";
ctx.fillRect(0,0,100,100);
}
// Specify the src to load the image
img.src = "http://i.imgur.com/gwlPu.jpg";
body {
background: #CEF;
}
<canvas id="c" width="200" height="158"></canvas>
And of course, you could very well get rid of clipping thanks to compositing and use a second offscreen canvas. (Probably my personal favorite).
I am just getting started with Canvas programming and trying to build a small game. Below is a sample code that I am trying out. My intention is to:
Create a canvas.
Fill it with some background color.
Draw a circle.
Clear the canvas.
Draw another circle in different location.
Here's the code:
var canvas = document.createElement('canvas');
canvas.width= 400;
canvas.height = 400;
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
// 2. Fill background
ctx.fillStyle = 'rgb(30,0,0)';
ctx.fillRect(0,0,400,400);
// 3. Draw circle
ctx.save();
ctx.fillStyle = 'rgba(256,30,30,.8)';
ctx.arc(50,50, 20, 0, Math.PI*2, true);
ctx.fill();
ctx.restore();
// 4. Clear Canvas
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
// 5. Draw another circle
ctx.save();
ctx.fillStyle = 'rgba(256,30,30,.8)';
ctx.arc(150,150, 20, 0, Math.PI*2, true);
ctx.fill();
ctx.restore();
But as you can see, only the background color gets cleared and the first circle remains as it is.
Why is the above code fails to clear the canvas completely before drawing second circle?
If you don't use beginPath before starting a new path, all draw command keeps stacking in the current path.
What's happening here is that when you fill() the second time, the first circle is still in the current path, so even if the screen was in deed cleared, there are two circles drawn with this single fill() command.
==>> use beginPath() before starting a new path.
I want to set an image instead of painting the ball
My code is the following
ballPainter = {
BALL_FILL_STYLE: 'rgb(255,0,50)',
BALL_STROKE_STYLE: 'rgb()',
paint: function (ball, context) {
var imageObj = new Image();
context.save();
context.shadowColor = undefined;
context.lineWidth = 2;
context.fillStyle = this.BALL_FILL_STYLE;
context.strokeStyle = this.BALL_STROKE_STYLE;
context.beginPath();
context.arc(ball.left, ball.top,
ball.radius, -1, Math.PI*2, false);
context.clip();
context.fill();
context.stroke();
context.restore();
}
},
You might want to check
http://www.w3schools.com/tags/canvas_drawimage.asp
I'm not sure if you can crop an image to a cricle though (maybe if the image has a transparant background)
*edit apperently you can HTML5 Canvas - Fill circle with image
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 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);
});