after trying a few ways to preserve a background under a canvas drawing a moving rectangle (animation code not reproduced), here is my best try :
canvas {background-image:url('background.png');}
var x,y, pixels;
function draw() {
if(pixels) {
context.putImageData(pixels,x,y);
}
x = //calculate new X
y = //calculate new Y
pixels = context.getImageData(x, y, 10, 10);
context.fillStyle = 'red';
context.fillRect(x, y, 10, 10);
}
My first question is : why won't replacing the first two lines of draw() with :
context.fillStyle = 'rgba(10,10,10,0)';
context.fillRect(x,y,10,10);
work to clear the previously drawn pixels?
And my second question is : is there really no better way than get and putImageData(), which are very labor-intensive?
EDIT: in particular is there a div containing the background image trick that would maybe work without the get and putImageData calls?
Thanks!
By default, whatever you draw gets drawn on top of each other, instead of being replaced.
Use context.clearRect(x,y,10,10); instead.
There is also the context.globalCompositeOperation property, which could be set to "destination-out" before using fillRect .. but you're better off with clearRect.
Related
Say I have drawn a circle on a canvas that has something else drawn on it that stops me from clearing the canvas - due to the other element being randomly generated
var circleX = 50;
var circleY = 10;
var moveCircX = 2;
var moveCircY = 3;
function createCirc(){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(circleX, circleY, 10, 0, Math.PI*2, true);
ctx.fill();
}
function circMove(){
circleY = (circleY + circMoveY)
//then validation to stop it from being drawn of the canvas
So what I'm trying to do is move the circle but clear the previous drawn circle from the canvas. So is there a solution to clearing the circle or would it be easier to create a sprite that replicates the circle?
Since your background isn't changing, the simplest strategy is to copy the background before you first draw your circle, then draw your circle. When you're moving, redraw that part of the background from the copy you kept, then draw your circle in the new place.
An efficient way to do that is to use getImageData and putImageData.
So, (my javascript is rusty, so this may not be perfect. Feel free to correct any mistakes), before the first time you createCirc, simply do:
imageData = ctx.getImageData(0,0, ctx.canvas.width, ctx.canvas.height)
And, in your circMove function, before you move and redraw the circle, you want:
ctx.putImageData(imageData, circleX, circleY, circleX, circleY, 2*circle_radius, 2*circle_radius)
(You don't define circle_radius, but I'm sure you must have a similar value. I'm using 2x the radius to presumably be the size of the image that is drawn.)
I'm trying to draw an object based on it's rotate property, but it seems to get confused when the object is moving.
ctx.save();
ctx.translate(this.width * 0.5, this.height*0.5);
ctx.rotate(DegToRad(45));
ctx.translate(-this.width*0.5,-this.height*0.5);
ctx.drawImage(this.img, this.spriteOffset, 0, this.width, this.height, this.x, this.y,this.width,this.height);
ctx.restore();
The image is drawn rotated 45 degrees, however it now moves in a down-left direction, when the object should only be moving downwards. The movement code is simply handled by incrementing the this.y position. Is there a simpler way to accomplish this?
This is because the whole canvas is being rotated.
What you could try is looking into a framework like Kinetic JS, which creates a sort of SVG API for the canvas.
This site has tons of information on how to use it as well, including rotation, transition, transformation, and pretty much anything else that you might need in working with it.
This should suit your needs rather well.
I think I should provide a low-level, framework-free option as well. Basically, using raw javascript and HTML to pull this off.
Now, as I understood your question, you are trying to make an object (let's assume a black square) move downwards AND have it spin. The only way I can think of to spin it in the canvas without going into path hell is to rotate the entire rendering context. BUT you can also import an image into canvas, for instance, a transparent black diamond (i.e. that same square rotated). So you'd use a separate canvas to render each step of the rotation for the square.
Essentially something like this:
var canvas2 = document.createElement('canvas'), ctx2 = canvas2.getContext('2d');
//do something with the second canvas
//let's assume the second canvas is the same size as the square
ctx.drawImage(canvas2, squareX, squareY);
See my attempt
As you can see, it is a bit wonky, but it does do essentially what the question asks; it moves the square down, and rotates it. It also plots the result of that rotation below the actual canvas so you can see what's happening under the hood, but the square cuts out due to the "center" being on the top left of the square, and not in the middle.
In the end, it really comes down to how you want to do it.
I was playing around with the API and found it was easier to just keep track of where the object should be. here's an example of a square moving diagonally across the screen and rotating.
<canvas id="canvas" style="width: 100%; height: 100%;">
</canvas>
<script>
var DELAY = 15; // ms
var RECT_WIDTH = 100; // px
var RECT_HEIGHT = 100; // px
var canvas = document.getElementById('canvas');
// set intrinsic dimensions
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext('2d')
ctx.fillStyle = 'teal';
var step = 0
var vx = 2
var animate = setInterval(function () {
ctx.resetTransform()
ctx.clearRect(0, 0, 1000, 1000);
ctx.translate(vx * step, vx * step);
// rotation in place, translate to center of square and back
ctx.translate(RECT_WIDTH / 2, RECT_HEIGHT / 2);
ctx.rotate((Math.PI / 180) * step);
ctx.translate(-(RECT_WIDTH / 2), -(RECT_HEIGHT / 2));
// Draw the rectangle
ctx.fillRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
step = step + 1
}, DELAY)
setTimeout(function () {
clearInterval(animate);
}, 5000);
</script>
Here, I use vx and keep track of the steps to translate, and calculate what the rotation will be in radians based on the steps a new not caring what the previous state was. Make sure you rotate across the center of where you're square will be as well.
Use ctx.translate to set the object's position before applying the rotation. This should fix the problem.
ctx.save();
ctx.translate(this.x, this.y);
ctx.translate(this.width * 0.5, this.height*0.5);
ctx.rotate(DegToRad(45));
ctx.translate(-this.width*0.5,-this.height*0.5);
ctx.drawImage(this.img, this.spriteOffset, 0, this.width, this.height, 0, 0,this.width,this.height);
ctx.restore();
I've got a canvas that includes images, I'm re-drawing 1 pixel lower each time to give the effect of falling. I've got the images in an array and I just place them 1 pixel lower without recreating the image.
Is it possible to add a border dynamically to images that reach a certain point and if so, how?
Yes, all you have to do is draw a path outside the image and call ctx.stroke() to make the border.
So say the image has the coordinates x and y, with a width and height of w and h, you just do:
ctx.rect(x, y, w, h);
ctx.stroke();
Want a different colored border?
ctx.strokeStyle = 'blue';
Thicker?
ctx.lineWidth = 5;
If you know your images' size and location and as you draw them you probably do, You can use the .rect canvas method to draw a rectangle around the image.
I have this bound to the mousemove event of my canvas:
function(e){
var contDiv = $('#current_system_map');
var offset = contDiv.offset();
x = e.clientX-offset.left;
y = e.clientY-offset.top;
context.beginPath();
context.moveTo(0,y);
context.lineTo(595,y);
context.moveTo(x,0);
context.lineTo(x,595);
context.strokeStyle = "rgb(255,255,255)";
context.stroke();
}
and it works fine, to a point. The drawn cross is persistent, so when the mouse moves a new cross is drawn but the old one remains. I've tried essentially re-drawing the canvas but that cause the cross to be laggy and remain quite away behind the mouse.
So i need to know how to draw the cross and make it dis-appear without having to re-draw everything on the canvas
http://jsfiddle.net/PgKEt/2/
This is the best that I can do.
If you try to use setInterval and such to animate it, it will keep redrawing even when it does not need to. So by doing this, you essentially only redraw when the mouse moves, and only draw 2 lines, instead of whatever content you want it on top.
In addition, if you have any detection such as mousedown and such, it has to be on whatever canvas is on the top, otherwise it will not detect them anymore.
Usually if you draw something on the canvas you will have to redrawn the canvas contents to erase it. I suggest you use an image element as a cursor and position it absolutely above the
Or you could try the old trick and draw the cursor in the canvas with globalCompositeOperation='xor', then draw it again in the same place to erase it. Afterwards you will need to restore globalCompositeOperation to source-over.
This approach works fast enough for me in Firefox 3.6.8 to do in a mousemove event. Save the image before you draw the crosshair and then restore it to erase:
To save:
savedImage = new Image()
savedImage.src = canvas.toDataURL("image/png")
The to restore:
ctx = canvas.getContext('2d')
ctx.drawImage(savedImage,0,0)
If you do not want to store it persistently, you can also take a look at SVG.
Try
ctx.clearRect(0,0,YourCanvasHeight,YourCanvasWidth);
In my case I implemented a circle and every time the user clicks inside it, this instruction returns and deletes the previous points.
This is the complete code:
function getMousePosition(canvas, event) {
let rect = canvas.getBoundingClientRect();
let x = event.offsetX; //event.clientX - rect.left;
let y = event.offsetY; //event.clientY - rect.top;
drawPoint(canvas,x,y);
};
function drawPoint(canvas,x,y) {
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.clearRect(0,0,200,200);
ctx.beginPath();
ctx.arc(x, y, 5, 0, 2 * Math.PI);
ctx.fillStyle = "white";
ctx.fill();
}
};
$(document).ready(function(){
let canvasElem = document.getElementById("circle");
canvasElem.addEventListener("click", function(e)
{
getMousePosition(canvasElem, e);
});
});
I am using an HTML canvas and javascript and I need to clear all of the pixels underneath a shape created by closing a path (for example, I am using flot, and I want to make rounded corners, and to do this, I first need to remove the square corners by drawing a curve on top of the corner to remove the desired pixels).
Right now, I am doing this by just filling the shape with the same color as the background, which can imitate what I want to do, but, it is not ideal as it makes it impossible to place the chart on top of non-solid backgrounds without seeing the square corners. I know that there is a clearRect method that would do what I want to do, but with only rectangles, I need to do it with any closed shape. Is it possible, and if so, how would I do it?
brainjam's code was heading in the right direction, but didn't fully solve the problem. Here's the solution:
context.save();
context.globalCompositeOperation = 'copy';
context.fillStyle = 'rgba(0,0,0,0)';
//draw shape to cover up stuff underneath
context.fill();
context.restore();
Here's an example of a function that will clear a circle from a canvas:
var clearCircle = function(x, y, radius)
{
context.save();
context.globalCompositeOperation = 'destination-out';
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
context.fill();
context.restore();
};
I think what you want is a clipping region, defined by the clip() function. The latter takes a bunch of paths. Here's an example.
This is a little different from what you are specifically asking (which is to remove pixels after drawing them), but actually not drawing the pixels in the first place is probably better, if I understand your requirements correctly.
Edit: I now think I understand that what you want to do is clear pixels to transparent black. To do that, after having defined your paths, do something like this:
context.fillStyle = 'rgba(0,0,0,0)';
context.fill();
The first statement sets the fill color to transparent black.
Use globalCompositeOperation = 'destination-out' instead of 'copy', it will erase all pixels of the shape in the canvas.
See all kinds of composition here
very usefull !