Im study html canvas and trying to make simple animation.
I want to make rectangle move right.
You can look at my code here.
http://codepen.io/inkluter/pen/GgeQqj
var x = 0, y = 0, w = 200, h = 100;
function draw() {
c.clearRect(0, 0, c.width, c.height);
x++;
c.beginPath();
c.strokeRect(x, y, w, h);
c.closePath();
requestAnimationFrame(draw);
};
Problem is that clearRect() function seems not working. Old rectangle still displaying on canvas.
What am i doing wrong?
'width' and 'height properties are defined in the canvas element, not the context. Write c.clearRect(0, 0, canvas.width, canvas.height) instead.
Related
I've been battling with <canvas> for a while. I want to create an animation/game with lots of different units on different layers.
Due to <canvas> limitation to just one context my approach is as follows:
have one <canvas> on the page,
create multiple "layers" using document.createElement("canvas"),
animate/rerender "layers" separately.
But this approach does not seem to work properly due to one quirk - in order to stack "layers" on my main canvas I'm doing realCanvas.drawImage(layerCanvas, 0, 0);. Otherwise the layers are not being rendered.
The issue here is ultimately it does not change a thing as everything is in being drawn on my main <canvas> and when I do clearRect on one of my layers it does nothing as the pixels are also drawn on the main canvas in addition to given layer. If I run clearRect on main canvas then the layers are useless as every layer is on main canvas so I'm back to starting point as I'm clearing the whole canvas and layers are not separated at all.
Is there a way to fix it easily? I feel like I'm missing something obvious.
Here's an example, how do I clear blue ball trail without touching background rectangles here? There should be only one blue ball under your cursor. Note it's a very simplified example, I'll have multiple blue balls and multiple other layers. I just want to know how the heck do I clear only one layer in canvas. Note I don't want to use multiple <canvas> elements and don't want to use any libs/engines as I'm trying to learn canvas by this. I know many apps use just one canvas html element, many layers and somehow animate them separately.
Source: https://jsfiddle.net/rpmf4tsb/
Try adding canvas2ctx.clearRect(0,0, canvas.width, canvas.height); under ctx.clearRect(0,0, canvas.width, canvas.height); and it works as supposed but all the layers are being cleared, not only the one with the ball...
If you look at things from a performance point-of-view, things are better if you use a single visible <canvas> element for your visual output.
Nothing is stopping you from doing things on seperate canvases you stack on top of each other though. Maybe there's just a basic misunderstanding here.
You say:
and when I do clearRect on one of my layers it does nothing as the
pixels are also drawn on the main canvas in addition to given layer
Well that's not true. If you draw the contents of a freshly cleared canvas onto another canvas it won't overwrite the target canvas with 'nothing'.
Take a look at this example:
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
ctx.fillStyle = "green";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.lineWidth = 10;
ctx.arc(canvas.width / 2, canvas.height / 2, 50, 0, 2 * Math.PI);
ctx.stroke();
let tempCanvas = document.createElement("canvas");
let tempContext = tempCanvas.getContext("2d");
tempContext.clearRect(0, 0, tempCanvas.width, tempCanvas.height);
ctx.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height);
<canvas id="canvas"></canvas>
Our main canvas contains a green background with a black circle and we're utilizing the drawImage() method to draw a dynamically created, freshly cleared canvas onto, which results in a green background with a black circle as the new canvas element did not contain any data to draw. It did not erase the main canvas.
If we change the example a bit, so the second canvas contains a rectangle things will work as expected:
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
ctx.fillStyle = "green";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.lineWidth = 10;
ctx.arc(canvas.width / 2, canvas.height / 2, 50, 0, 2 * Math.PI);
ctx.stroke();
let tempCanvas = document.createElement("canvas");
let tempContext = tempCanvas.getContext("2d");
tempContext.clearRect(0, 0, tempCanvas.width, tempCanvas.height);
tempContext.strokeRect(tempCanvas.width / 2 - 60, tempCanvas.height / 2 - 60, 120, 120);
ctx.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height);
<canvas id="canvas"></canvas>
Now if we assume the green background with the circle (tempCanvasA) and the rectangle (tempCanvasB) are two separate canvases we ultimately want to draw to a main canvas it will bring up an important point: the order of drawing.
So this will work:
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
let tempCanvasA = document.createElement("canvas");
let tempContextA = tempCanvasA.getContext("2d");
tempContextA.fillStyle = "green";
tempContextA.fillRect(0, 0, tempCanvasA.width, tempCanvasA.height);
tempContextA.beginPath();
tempContextA.lineWidth = 10;
tempContextA.arc(tempCanvasA.width / 2, tempCanvasA.height / 2, 50, 0, 2 * Math.PI);
tempContextA.stroke();
let tempCanvasB = document.createElement("canvas");
let tempContextB = tempCanvasB.getContext("2d");
tempContextB.strokeRect(tempCanvasB.width / 2 - 60, tempCanvasB.height / 2 - 60, 120, 120);
ctx.drawImage(tempCanvasA, 0, 0, canvas.width, canvas.height);
ctx.drawImage(tempCanvasB, 0, 0, canvas.width, canvas.height);
<canvas id="canvas"></canvas>
while this fails:
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
let tempCanvasA = document.createElement("canvas");
let tempContextA = tempCanvasA.getContext("2d");
tempContextA.fillStyle = "green";
tempContextA.fillRect(0, 0, tempCanvasA.width, tempCanvasA.height);
tempContextA.beginPath();
tempContextA.lineWidth = 10;
tempContextA.arc(tempCanvasA.width / 2, tempCanvasA.height / 2, 50, 0, 2 * Math.PI);
tempContextA.stroke();
let tempCanvasB = document.createElement("canvas");
let tempContextB = tempCanvasB.getContext("2d");
tempContextB.strokeRect(tempCanvasB.width / 2 - 60, tempCanvasB.height / 2 - 60, 120, 120);
ctx.drawImage(tempCanvasB, 0, 0, canvas.width, canvas.height);
ctx.drawImage(tempCanvasA, 0, 0, canvas.width, canvas.height);
<canvas id="canvas"></canvas>
The rectangle is missing! Why does it fail? Because we changed the order we draw the canvases onto the main canvas. In the latter example:
ctx.drawImage(tempCanvasB, 0, 0, canvas.width, canvas.height);
ctx.drawImage(tempCanvasA, 0, 0, canvas.width, canvas.height);
We first draw tempCanvasB which contains a transparent background & the rectangle and afterwards tempCanvasA with the solid green background - which covers the entire canvas - and the circle. As there are no transparent pixels it will overwrite the rectangle which we've drawn first.
To get to your example with the ball. The problem is that you're drawing the ball to the wrong canvas. Inside your draw function you're doing this:
ctx.clearRect(0, 0, canvas.width, canvas.height);
ball.draw();
ball.x = e.clientX;
ball.y = e.clientY;
ctx.drawImage(canvas2, 0, 0);
So first you clear ctx, afterwards call ball's draw method which draws onto canvas2ctx and finally drawImage onto ctx with the contents of canvas2ctx.
Instead draw the ball onto the main ctx after using drawImage()
e.g.
// helper functions
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
// canvas
let firstRender = true;
var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth - 50;
canvas.height = window.innerHeight - 50;
let ctx = canvas.getContext('2d');
// virtual canvas for rectangles layer
let canvas2 = document.createElement("canvas");
canvas2.width = window.innerWidth - 50;
canvas2.height = window.innerHeight - 5;
let canvas2ctx = canvas2.getContext("2d");
let ball = {
x: 100,
y: 100,
vx: 5,
vy: 2,
radius: 25,
color: 'blue',
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
};
function draw(e) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(canvas2, 0, 0);
ball.draw();
ball.x = e.clientX;
ball.y = e.clientY;
if (firstRender) {
drawRandomRectangles()
firstRender = false;
}
}
function drawRandomRectangles() {
for (i = 0; i < 50; i++) {
canvas2ctx.beginPath();
canvas2ctx.rect(randomInt(0, window.innerWidth - 50), randomInt(0, window.innerWidth - 50), randomInt(5, 20), randomInt(5, 20));
canvas2ctx.stroke();
}
}
canvas.addEventListener('mousemove', function(e) {
draw(e);
});
ball.draw();
<canvas id="canvas"></canvas>
Thinking about your approach of multiple canvas stacking above each other sounds like an interesting approach to get things done. I would not recommend doing this in that way and therefore handle multiple layers through JavaScript and then still render every time everything new. Especially if you will use animations, then I believe that multiple not synchronized canvases will give you another sort of headache.
Then you would do the following:
Clear your canvas with clearRect.
Draw in an iteration each layer above each other
I hope this theoretical explanation helps.
Now to your code: At the end of the day your ctx and canvas2ctx are in the very same context, because they are from the same canvas. That makes anyway not much sense.
Seems like there are other questions like this and I'd like to avoid a buffer and/or requestAnimationFrame().
In a recent project the player is flickering but I cannot find out the reason. You can find the project on JSFiddle: https://jsfiddle.net/90wjetLa/
function gameEngine() {
timer += 1;
timer = Math.round(timer);
// NEWSHOOT?
player.canShoot -= 1;
// MOVE:
movePlayer();
shootEngine(); // Schussbewegung & Treffer-Abfrage
// DRAW:
ctx.beginPath();
canvas.width = canvas.width;
ctx.beginPath();
ctx.fillStyle = 'black';
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fill();
drawField();
drawPlayer();
drawShoots();
setTimeout(gameEngine, 1000 / 30);
}
Each time you write to a visible canvas the browser want's to update the display. Your drawing routines might be out of sync with the browsers display update. The requestAnimationFrame function allows you to run all your drawing routines before the display refreshes. Your other friend is using an invisible buffer canvas. Draw everything to the buffer canvas and then draw the buffer to the visible canvas. The gameEngine function should only run once per frame and if it runs multiple times you could see flicker. Try the following to clear multiple runs in the same frame.
(edit): You might also want to clear the canvas instead of setting width.
(edit2): You can combine the clearRect, rect, and fill to one command ctx.fillRect(0, 0, canvas.width, canvas.height);.
var gameEngineTimeout = null;
function gameEngine() {
// clear pending gameEngine timeout if it exists.
clearTimeout(gameEngineTimeout);
timer += 1;
timer = Math.round(timer);
// NEWSHOOT?
player.canShoot -= 1;
// MOVE:
movePlayer();
shootEngine(); // Schussbewegung & Treffer-Abfrage
// DRAW:
ctx.beginPath();
//canvas.width = canvas.width;
//ctx.clearRect(0, 0, canvas.width, canvas.height);
//ctx.beginPath();
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
//ctx.fill();
drawField();
drawPlayer();
drawShoots();
gameEngineTimeout = setTimeout(gameEngine, 1000 / 30);
}
I found this code to tint an canvas image file. I'm wondering what ist ctx.save and ctx.restore is used for in this tinting context? Why is it needed here?
JS FIDDLE
function recolor(color) {
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(pic, 0, 0);
ctx.globalCompositeOperation = "source-in";
ctx.fillStyle = color;
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fill();
ctx.restore();
var img = new Image();
img.src = canvas.toDataURL();
return (img);
}
save and restore are used to save and restore all context state, such as fillStyle, lineWidth, globalCompositeOperation, the clipping region, the current context transformation matrix, and so on.
The only necessary purpose of the save and restore in your fiddle is to reset the globalCompositeOperation.
You can do this manually instead:
function recolor(color) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(pic, 0, 0);
ctx.globalCompositeOperation = "source-in";
ctx.fillStyle = color;
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fill();
//instead of save and restore:
ctx.globalCompositeOperation = "source-over";
var img = new Image();
img.src = canvas.toDataURL();
return (img);
}
In general you should avoid using save and restore unless you absolutely must, as it can get computationally expensive.
ctx save and restore are used to save the current context state. This is commonly used in functions so that you can call them anywhere and not worry that it will change the draw state outside of the function.
So for instance, in this code you change the fillStyle. Once ctx.restore is called, the fill style will return to what it was when the ctx.save was called.
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.
Hi I'm doing a drawing app using canvas but i dunno how to clear canvas.
I've tried clearRect and other functions but they don't work.
The last two function of the script should clear canvas but they don't work...
(sorry for my bad english)
Here the code:
function clear_canvas_width ()
{
var s = document.getElementById ("scribbler");
var w = s.width;
s.width = 10;
s.width = w;
}
function clear_canvas_rectangle ()
{
var canvas = $('#canvas')[0]; // or document.getElementById('canvas');
canvas.width = canvas.width;
}
Need a bit more code to really see what the problem is. Here is something really simple that you can go off of to maybe narrow it down. Also for performance reasons its better to use clearRect over resetting the width of the canvas. How you clear your canvas matters
Live Demo
var clearBut = document.getElementById("clearCan"),
canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = canvas.height = 300;
ctx.fillRect(10,10,280,280);
function clearCanvas(){
ctx.clearRect(0,0,canvas.width, canvas.height);
}
clearBut.addEventListener("click", clearCanvas);
Have you checked whether you are clearing the right canvas? Maybe if you provide us with more code we can help you further.
Also make sure you don't draw over it after you have cleared it.
However, when it comes to clearing canvases this is the easiest way I know.
function clear_canvas( canvas ){
canvas.width = canvas.width;
}
But you can also use
function clear_canvas (ctx, canvas){
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
from;
How to clear the canvas for redrawing
// Store the current transformation matrix
ctx.save();
// Use the identity matrix while clearing the canvas
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Restore the transform
ctx.restore();