I'm currently learning about the html5 canvas, and I was making a program that rotates a square around the center of the page using the .rotate() function, but it was leaving behind 1px lines from where the square used to be even though I was filling the canvas after every turn and nothing in the code was 1px. so can someone please explain why this is happening and how to stop it from happening
thanks!
const canvas = document.getElementById("canvas")
const ctx = canvas.getContext("2d")
canvas.width = 300;
canvas.height = 300;
//debugger;
/*
canvas.addEventListener("mousedown", mouseDown, false)
canvas.addEventListener("mouseup", mouseUp, false)
canvas.addEventListener("mousemove", mouseMove, false)
*/
ctx.fillStyle = "#303030";
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.translate(150, 150)
let turnSquare = setInterval(() =>{
ctx.fillStyle = "#303030"
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.rotate(degree(10))
ctx.beginPath()
ctx.fillStyle = "lime"
ctx.fillRect(0, 0, 100, 100)
}, 1000 / 20)
function degree(input){
return 2 * Math.PI / 360 * input
}
<canvas id="canvas">
Anti aliasing edge.
The reason you are getting the ~1px line is because the ctx.fillRect
you use to clear the canvas is transformed by the same transform used to draw the box.
When a shape is rendered its edges are anti-aliased, some of the edge pixels are drawn semi-transparent meaning that drawing over that same box will leave some of the original pixels behind.
To fix
- Reset the transform to the default before you clear the canvas.
ctx.setTransform(1,0,0,1,0,0);
- Use a variable to hold the total rotation of the box (see example)
Use
setTransform
to replace the current transform rather than build the transform in stages usingctx.translate
andctx.rotate
.Transform basics while help understand how
setTransform
can be used to position, scale, and rotate rendered objects in the 2D canvas API.
Other points
- Use
requestAnimationFrame
rather thansetInterval
to call the animation loop to ensure the smoothest possible animation. - Use a single object to store related data. Eg the object
box
in example holds all relevant data needed to animate the box. - Try to express animations in meaningfully ways. In the example the box rotation rate
box.rotSpeed
is set as rotations per second
Example
requestAnimationFrame(mainLoop);
const ctx = canvas.getContext("2d")
canvas.height = canvas.width = 300;
canvas.style.backgroundColor = "#303030"
const box = {
color: "lime",
cx: 0.25, // x rotation center as fraction of box size
cy: 0.25, // y rotation center as fraction of box size
x: 150,
y: 150,
size: (150 * 150 / 2) ** 0.5, // makes diagonal size of box 150
rot: 0,
rotSpeed: 0.5, // in rotations per second
};
function drawBox(box) {
box.rot += box.rotSpeed * Math.PI / 30; // 2*PI/60 is one rotation per second at 60FPS
ctx.fillStyle = box.color;
const xa = Math.cos(box.rot);
const ya = Math.sin(box.rot);
ctx.setTransform(xa, ya, -ya, xa, box.x, box.y);
ctx.fillRect(-box.cx * box.size, -box.cy * box.size, box.size, box.size);
}
function mainLoop() {
ctx.setTransform(1, 0, 0, 1, 0, 0); // set default transform
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBox(box);
requestAnimationFrame(mainLoop);
}
<canvas id="canvas">