I was trying to work on a rainbow-pen drawing canvas, but whenever I draw, the lines appear dotted.
Only when I move really slowly, does it appear properly.
The 'mousemove' event listener, isn't really being able to detect fast changes or is there some other issue in my code?
Also, here is the codepen link, if anyone wants a working program.
Here is the codepen!
const canvas = document.querySelector('#draw');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
ctx.lineWidth = 50;
ctx.lineCap = "round";
ctx.lineJoin = "line";
ctx.strokeStyle = 0;
let hue = 0;
var [x, y] = [0, 0];
let paint = false;
canvas.addEventListener('mousedown', beginDraw);
function beginDraw(e) {
paint = true;
[x, y] = [e.x, e.y];
// ctx.moveTo(x, y);
}
canvas.addEventListener('mousemove', draw);
function draw(e) {
if (paint == false)
return;
ctx.strokeStyle = `hsla(${hue}, 100%, 50%, 0.5)`;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(e.x, e.y);
ctx.stroke();
[x, y] = [e.x, e.y];
hue++;
}
canvas.addEventListener('mouseup', endDraw);
function endDraw() {
paint = false;
}
<canvas id="draw"></canvas>
I think the problem is opacity in your hsla function for color. Because it is set to 0.5 you have some transparency and because you are drawing a line for each mousemove you have for each mouse move event start and end point for your drawing.
Sometimes these points overlaps each other.
You can remove transparency and keep it to 1. In such case you are no longer see places where 2 dots are drawn one above another making color more visible to others.
ctx.strokeStyle = `hsla(${hue}, 100%, 50%, 1)`;
In case you dont like to see dots when color changes you will need to make more color values also playing with other parameters not only hue parameter, because color step may be to large and you may see edges when color is changed.
The mousemove event handler fires only so many times per second. On each execution, your code draws a line from the previous mouse location to the current mouse location. Each subsequent line partially overlaps the previous line.
For slow strokes, the overlap is almost 100% so you won't see the effect, but for faster strokes, the overlap is shown as a circle. Since you're using partial transparency, the spots where the lines overlap become darker and this causes the "dotted" effect you are seeing.
If you set your opacity to 1, the effect will go away.
Related
I'm trying to make a pixel editor with 2 canvas. The first canvas displays a second canvas which contains the pixels. The first canvas uses drawImage to position and scale the second canvas.
When the second canvas is scaled smaller than it's original size, it starts to glitch.
Here is the canvas displayed at it's original size. When I zoom in, the second canvas get bigger and everything works perfectly.
However when I zoom out, the grid and the background (transparency) act very strangely.
To draw the second canvas on the first canvas, I use the function
ctx.drawImage(drawCanvas, offset.x, offset.y, width * pixelSize, height * pixelSize);
I have read that scaling in multiple iterations might give a better quality with images but I am not sure about a canvas.
I could fully redraw the second canvas in a lower resolution when the user zooms out, but it is a bit heavy on the cpu.
Is there any better solution that I don't know of?
Your problem comes from anti-aliasing.
Pixels aren't sub-divisible, and when you ask the computer to draw something outside of the pixel boundaries, it will try its best to render something that usually looks good to eyes, by mixing the colors so that what should have been a black 0.1 pixel line will become a light-gray pixel for instance.
This generally works good, particularly with pictures of the real word, or complex shapes like circles. However with grids... That's not so great as you experienced it.
Your case is dealing with two different cases, and you will have to deal with hem separately.
In the canvas 2D API (and a lot of 2D APIs) stroke do bleed from both sides of the coordinates you did set it. So when drawing lines of 1px wide, you need to account for a 0.5px offset to be sure it won't get rendered as two gray pixels. For more info about this, see this answer. You are probably using such a stroke for the grid.
fill on the other hand only covers the inside of the shape, so if you fill a rectangle, you need to not offset its coords from the px boundaries. This is required for the checkerboard.
Now, for boh these drawings, the best is probably to use patterns. You only need to draw a small version of it, and then the pattern will repeat it automatically, saving a lot of computation.
Scaling of a pattern can be done by calling the transform methods of the 2D context. We can even take advantage of the closest-neighbor algorithm to avoid antialising when drawing this pattern by setting the imageSmoothingEnabled property to false.
However for our grid, we may want to keep the lineWidth constant. For this we will need to generate a new pattern at every draw call.
// An helper function to create CanvasPatterns
// returns a 2DContext on which a simple `finalize` method is attached
// method which does return a CanvasPattern from the underlying canvas
function patternMaker(width, height) {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.finalize = (repetition = "repeat") => ctx.createPattern(canvas, repetition);
return ctx;
}
// The checkerboard can be generated only once
const checkerboard_patt_maker = patternMaker(2, 2);
checkerboard_patt_maker.fillStyle = "#CCC";
checkerboard_patt_maker.fillRect(0,0,1,1);
checkerboard_patt_maker.fillRect(1,1,1,1);
const checkerboard_patt = checkerboard_patt_maker.finalize();
// An helper function to create grid patterns
// Since we want a constant lineWidth, no matter the zoom level
function makeGridPattern(width, height) {
width = Math.round(width);
height = Math.round(height);
const grid_patt_maker = patternMaker(width, height);
grid_patt_maker.lineWidth = 1;
// apply the 0.5 offset only if we are on integer coords
// for instance a <3,3> pattern wouldn't need any offset, 1.5 is already perfect
const x = width/2 % 1 ? width/2 : width/2 + 0.5;
const y = height/2 % 1 ? height/2 : height/2 + 0.5;
grid_patt_maker.moveTo(x, 0);
grid_patt_maker.lineTo(x, height);
grid_patt_maker.moveTo(0, y);
grid_patt_maker.lineTo(width, y);
grid_patt_maker.stroke();
return grid_patt_maker.finalize();
}
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const checkerboard_input = document.getElementById('checkerboard_input');
const grid_input = document.getElementById('grid_input');
const connector = document.getElementById('connector');
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const checkerboard_zoom = checkerboard_input.value;
const grid_zoom = grid_input.value;
// we generate a new pattern for the grid, so the lineWidth is always 1
const grid_patt = makeGridPattern(grid_zoom, grid_zoom);
// draw once the rectangle covering the whole canvas
// with normal transforms
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
// the checkerboard
ctx.fillStyle = checkerboard_patt;
// our path is already drawn, we can control only the fill
ctx.scale(checkerboard_zoom, checkerboard_zoom);
// avoid antialiasing when painting our pattern (similar to rounding the zoom level)
ctx.imageSmoothingEnabled = false;
ctx.fill();
// done, reset to normal
ctx.imageSmoothingEnabled = true;
ctx.setTransform(1, 0, 0, 1, 0, 0);
// paint the grid
ctx.fillStyle = grid_patt;
// because our grid is drawn in the middle of the pattern
ctx.translate(Math.round(grid_zoom/2), Math.round(grid_zoom/2));
ctx.fill();
// reset
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
draw();
checkerboard_input.oninput = grid_input.oninput = function(e) {
if(connector.checked) {
checkerboard_input.value = grid_input.value = this.value;
}
draw();
};
connector.oninput = e => checkerboard_input.oninput();
<label>checkerboard-layer zoom<input id="checkerboard_input" type="range" min="2" max="50" step="0.1"></label><br>
<label>grid-layer zoom<input id="grid_input" type="range" min="2" max="50" step="1"></label><br>
<label>connect both zooms<input id="connector" type="checkbox"></label>
<canvas id="canvas"></canvas>
I have been looking around for this function and thus far I just can't find any I can make any sense of. I already have a rotating function to make it equal to the position but slowly is proving to be a bit harder with 0-360 and all.
I am using a html canvas 2d context to render the objects on a Cartesian coordinate system .
I would like object1 to face at positionX and positionY at a turn rate (R) , fairly straightforward.
there is no need for me to supply any code since your likely going to make your own anyways. But I will anyways here you go:
let faceAt = function (thisObject,positionX,positionY) {
let desiredLocationX = positionX - thisObject.transform.x;
let desiredLocationY = positionY -thisObject.transform.y;
thisObject.transform.rotation = Math.degrees(Math.atan2(desiredLocationY, desiredLocationX));
};
The (Math.degrees) function converts radians to degrees.
This thread says it all : https://www.google.ca/amp/s/jibransyed.wordpress.com/2013/09/05/game-maker-gradually-rotating-an-object-towards-a-target/amp/
This question is quite unclear. But, I'm assuming you essentially just want to rotate an element around an arbitrary point on a HTML5 canvas.
On a canvas, you can only draw one element at a time. You can't really manipulate singular elements - for example, you can't rotate an element by itself. Instead, you'd need to rotate the entire canvas. This will always rotate around the centre of the canvas, but if you move the canvas origin, then you will draw on a different part of the canvas; thus allowing you to rotate around a point.
Check out the following example. You can click anywhere on the canvas to make the square rotate around that point. Hopefully this is what you are after:
let cv = document.getElementById("cv");
let ctx = cv.getContext("2d");
let angle = 0;
//Variables you can change:
let speed = 1; //Degrees to rotate per frame
let pointX = 250; //The x-coord to rotate around
let pointY = 250; //The y-coord to rotate around
ctx.fillStyle = "#000";
setInterval(()=>{ //This code runs every 40ms; so that the animation looks smooth
angle = (angle + speed) % 360; //Increment the angle. Bigger changes here mean that the element will rotate faster. If we go over 360deg, reset back to 0.
ctx.clearRect(0, 0, 400, 400); //Clear away the previous frame.
//Draw the point we are rotating around
ctx.beginPath();
ctx.arc(pointX,pointY,5,0,2*Math.PI);
ctx.fill();
ctx.closePath();
ctx.save(); //Save the state before we transform and rotate the canvas; so we can go back to the unrotated canvas for the next frame
ctx.translate(pointX, pointY); //Move the origin (0, 0) point of the canvas to the point to rotate around. The canvas always rotates around the origin; so this will allow us to rotate around that point
ctx.rotate(angle*Math.PI/180); //Rotate the canvas by the current angle. You can use your Math.degrees function to convert between rads / degs here.
ctx.fillStyle = "#f00"; //Draw in red. This is also restored when ctx.restore() is called; hence the point will always be black; and the square will always be red.
ctx.fillRect(0, 0, 50, 50); //Draw the item we want rotated. You can draw anything here; I just draw a square.
ctx.restore(); //Restore the canvas state
}, 40);
//Boring event handler stuff
//Move the point to where the user clicked
//Not too robust; relys on the body padding not changing
//Really just for the demo
cv.addEventListener("click", (event)=>{
pointX = event.clientX - 10;
pointY = event.clientY - 10;
});
#cv {
border:solid 1px #000; /*Just so we can see the bounds of the canvas*/
padding:0;
margin:0;
}
body {
padding:10px;
margin:0;
}
<canvas id="cv" width="400" height="400"></canvas><br>
Click on the canvas above to make the rectangle rotate around the point that was clicked.
I am experimenting with drawing using javascript and the canvas element..my goal now is to draw a circle and gradually increase the opacity; I have this code:
http://codepen.io/anon/pen/zrVvOQ
Which seems to work, but the circle has rough edges; I found I need to clear the canvas each time the frame is redrawn, but the attempts I have made have not quite worked...any suggestions on how to?
window.onload = function draw(){
var frame1 = document.getElementById('frame1');
if (frame1.getContext){
var ctx = frame1.getContext('2d');
var centerX = frame1.width / 2;
var centerY = frame1.height / 2;
var radius = 50;
var alpha = 1.0;
/*call function over and over */
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
var rendergreen = function()
{
var opacityValue = 0;
opacityValue += 0.03;
ctx.fillStyle = 'rgba(68,107,62, ' + opacityValue + ')';
animate();
ctx.arc(50, centerY, radius, 0, 2 * Math.PI, false);
ctx.clip();
ctx.fill();
ctx.closePath;
function animate() {
if (opacityValue < 1) {
opacityValue += 0.3;
} else {
opacityValue = 1;
}
}
requestAnimationFrame(rendergreen);
}
rendergreen();
}
}
You say the circle has "rough edges". That's pixellation and is inherent in using canvas to draw, which is a bit-mapped style of graphics. That means that you essentially can't get higher resolution than a single pixel. Contrast that with svg which is vector-based. An svg image can be magnified a thousand times and still have a smooth edge. I've shown an svg circle next to the canvas circle so that you can see the difference. It becomes much more apparent if you zoom in with your browser. There are pro's and con's to using canvas vs svg, too much to go into here, but it's worth looking into if you're really concerned.
In terms of changing the opacity of the circle, you've got several problems with your approach. With the way you've written it, you're actually not changing the opacity. Instead, you're drawing the same very transparent circle many times over top of each other so that by the end it looks opaque, giving the impression that you are gradually increasing the transparency of a single circle. Notice that you're setting your opacity to zero in each drawing iteration, then incrementing it to 0.05 (note that there are differences in the code in your question versus in the codepen that you linked to...I'm referring to the codepen version), then drawing it (so it will always be drawn at opacity 0.05), then further changing the value of the variable opacityValue which is never used in the drawing. The example below shows a relatively simple example of what I think you were trying to achieve. Note that I've deliberately made the 'clearRect' too small so that you can see how not clearing the canvas each time allows semi-transparent drawings to "pile up". This also allows you to see that the blockiness gets worse if you overlay many semi-transparent images. e.g. Compare the left and right sides of the canvas circle. The part of the circle that is cleared every time ends up looking smoother because of anti-aliasing, but the overlaid images have the smoothing effects of anti-aliasing effectively destroyed.
window.onload = function draw() {
var frame1 = document.getElementById('frame1');
if (frame1.getContext) {
var ctx = frame1.getContext('2d');
var opacityValue = 0;
var render = function() {
ctx.clearRect(0, 0, 80, 80); // deliberately set too small
ctx.beginPath();
opacityValue += 0.01;
ctx.fillStyle = 'rgba(68,107,62, ' + opacityValue + ')';
ctx.arc(60, 60, 50, 0, 2 * Math.PI, false);
ctx.fill();
ctx.closePath;
requestAnimationFrame(render);
}
render();
}
}
<canvas id="frame1" width="120" height="120"></canvas>
<svg width="120" height="120">
<circle cx="60" cy="60" r="50" fill="#446B3E"></circle>
</svg>
Ok, here is something simple, I hope. I've a div container in which I can click and what I'm trying to do is to create two perpendicular lines which cross each other where I've clicked. So far I've written those:
#fr{
float: right;
margin-top: 5%;
height: 720px;
width: 1280px;
position: relative;
background-image: url(blueboard.jpg);
border: 1px solid black;
clear:none;
}
canvas{
border:1px solid red;
float: right;
height: 720px;
width: 1280px;
clear:none;
}//css part, I actually place the canvas on top of my div
//and on html...
<canvas id="line"></canvas>
//js
function saveOO(e){
var xo, yo;
xo=e.clientX;
yo=e.clientY;
...
document.getElementById("saved").innerHTML="The (0;0) = " +"("+xo+";"+yo+")";
document.getElementById("ball").style.left=xo+'px';
document.getElementById("ball").style.top=yo+'px';
xylines(xo, yo);
...;
}
function lines(xo, yo){
var xo, yo, xl, xline, yl, yline, Dx, Dy, a, b, c, d, e;
xo=xo;
yo=yo;
a=$("#fr").position();
b=$("#fr").width();
c=$("#fr").height();
Dy=a.top+100;
Dx=a.left;
d=Dx+b;
e=Dy+c;
xline = document.getElementById("line");
xl=xline.getContext("2d");
xl.beginPath();
xl.moveTo(Dx,yo);
xl.lineTo(d,yo);
xl.lineWidth = 15;
xl.stroke();
yline = document.getElementById("line");
yl=yline.getContext("2d");
yl.beginPath();
yl.moveTo(xo,Dy);
yl.lineTo(xo,e);
yl.lineWidth = 15;
yl.stroke();}
I have, as well, checked whether all variables assign a value and everything is good with that. The crossing point should be exactly where the blue ball is, it also positions on the place where it's clicked. As you can see on the image no lines show, even if I remove the blue background. Please, help me, maybe something is missing. I'm looking forward to your answers.
:)
P.S. Dy is Y of the top left corner, Dx respectively X of the top left corner
Update 2
A slightly amended fiddle where the lines span the entire width/height of the canvas:
https://jsfiddle.net/0y37qwvw/5/
This is by using 0 as the starting point for each and canvas.width/canvas.height as the ending point.
Update
Here is a fiddle demonstrating the use of a canvas overlay and responding to a click events.
https://jsfiddle.net/0y37qwvw/4/
The important thing is to get the relative x and y co-ordinates right, which I have done using event.offsetX and event.offsetY and a fallback for when they are not implemented:
document.getElementById("canvas-overlay").addEventListener("click", function( event ) {
var x, y, clientRect;
clientRect = event.target.getBoundingClientRect();
x = (typeof event.offsetX !== 'undefined' ? event.offsetX : (event.clientX - clientRect.left));
y = (typeof event.offsetY !== 'undefined' ? event.offsetY : (event.clientY - clientRect.top));
lines(x, y, 50, 50);
}, false);
Original answer
Your code is incomplete, so it is hard to be sure what the exact problems are.
One problem though is that you have not directly set the width and height of the canvas element, which will result in the canvas being scaled.
You can do this on the canvas element:
<canvas id="line" height="720" width="1280"></canvas>
Demonstrated at:
https://jsfiddle.net/0y37qwvw/1/
You could also set the width and height programmatically:
var canvas = document.getElementById("line");
canvas.width = 1280;
canvas.height = 720;
https://jsfiddle.net/0y37qwvw/3/
Compare to what happens if the width and height are only set by CSS:
https://jsfiddle.net/0aph7yno/
If you still can't get it to work, set up a plunker/fiddle with the broken code.
Here is some code I have written, I tried to use as much as OOP style so it's easier to expand it. But still simple and clear.
http://jsfiddle.net/eeqhc2tn/2/
var canvas = document.getElementById('can');
var ctx = canvas.getContext('2d');
var drawThese = [];
canvas.width = 500;
canvas.height = 500;
render();
//Line Code
function Line(sx, sy, ex, ey) {
this.sx = sx;
this.sy = sy;
this.ex = ex;
this.ey = ey;
}
Line.prototype.draw = function () {
ctx.beginPath();
ctx.moveTo(this.sx, this.sy);
ctx.lineTo(this.ex, this.ey);
ctx.stroke();
};
Line.drawCrossLines = function (x, y) {
var horizontal = new Line(0, y, canvas.width, y);
var vertical = new Line(x, 0, x, canvas.height);
drawThese.push(horizontal);
drawThese.push(vertical);
}
canvas.addEventListener('click', function (e) {
Line.drawCrossLines(e.offsetX, e.offsetY);
});
//Circle code
function Circle(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}
Circle.prototype.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.fill();
};
//Moving Circle Code that extends Circle
function MovingCircle() {
Circle.call(this,0,0,10);
}
MovingCircle.prototype = Object.create(Circle.prototype);
MovingCircle.prototype.constructor = MovingCircle;
MovingCircle.prototype.move = function (x, y) {
this.x = x;
this.y = y;
}
var pointer = new MovingCircle();
drawThese.push(pointer);
canvas.addEventListener('mousemove',function(e){
pointer.move(e.offsetX, e.offsetY);
});
//Rendering and animation code
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawThese.forEach(function (e) {
e.draw();
});
requestAnimationFrame(render);
}
I've found a working solution on my own, of course inspired by rpn. If you would like to make same or similar thing to what I this topic is about, a system including a container, canvas and a mouse event (click, in this case) you will have to follow this steps.
1. You cannot draw directly to a div, you've to create something like an invisible overlay for canvas and to place it perfectly over the div.
2. An important part is getting the proper coordinates, take into account that there are some tad but important differences between the various coords get methods. (`clientX/Y`, `offsetX/Y` and so on). It matters a lot.
3. Last but not at least, you should carefully consider how you get container's dimensions, as you'll need them later. Remember! We are in a container, a.k.a. working with an area which is just part of the whole window (your browser).
At first I was receiving my coordinates with clientX/Y but maybe due to some mismatches between the get method for coordinates and the one for div's dimensions I was hammered by bugs. To solve this, I've created a method from which I take mouse click coordinates with offsetX/Y. This way you have to think of you container as if it is a separate coordinate system, different from your window's(which by default is general). This means that now the top left corner of this div has x:y = (0;0);
function getcooords(){
xo = e.offsetX;
yo = e.offsetY;
DrawLines(x0, y0); //invoke func DrawLines
}
Now we go to the moment where we should draw our lines.
function DrawLines(x0,y0){
//your variables here...
w=$("#yourcontainer").width();
h=$("#yourcontainer").height();
Dy=0;
Dx=0;
}
That's the second step, Dx and Dy are the top and left properties of the div. Hence, from the what I've just said above, they will be equal to 0, both. Width and height of the container I take simply with jq but you can do it on another preferred way.
Generally speaking, that's the core of your drawing algorithm after we've taken needed dimensions and coordinates:
xl.beginPath();
xl.moveTo(b,yo);//start point of line
xl.lineTo(Dx,yo);//end point of line
xl.stroke(); //DRAW IT :)
Now, I've forgot to tell you that you have to define your lines at first, where should they be, what properties, styles should they have, etc..:
var xline = document.getElementById("line");
xl=xline.getContext("2d");
Remember! If you would like to delete, to erase the what you've drawn you'd have to define your lines FIRST and delete them after their definition. I'm saying definition, not drawing and you can draw the next 1, 2, 3... n lines and delete them over and over again if you follow this principle.
That's it guys, I hope I've explained it well, sorry for my English, probably I've made some tad mistakes but I hope you understand me. If you have any questions, please feel free to ask me, I'll try to do my best in order to help you.
:)
I'm trying to learn how to learn basic animation using ONLY canvas with the setInterval function.
I'm trying to draw a simple rectangle to the screen and move it to the right 1 pixel every 100 milliseconds. However when I do it paints over the previous rectangle. I called clearRect() but that doesn't seem to do anything.
How can I make this rectangle smoothly travel across the screen without leaving a trail?
Also if there is a better way to do this rather than using clearRect() and translate() please do share.
var ctx = document.getElementById('mycanvas').getContext('2d');
var a = setInterval(draw,100);
var x = 50;
function draw()
{
ctx.clearRect(0,0,300,300);
ctx.translate(1,0);
ctx.rect(x,50,50,50);
ctx.stroke();
}
You can do it two different ways:
You can continue to use rect() and stroke(), but you need to call beginPath() beforehand. When you call methods like rect(), a list, called the "path," is kept of all of the shapes, or "subpaths," that you've created. Then, when you call stroke(), the entire path is drawn. Thus, even if you clear the screen, all of the past rectangles are still remembered in the path, and drawn again. beginPath() clears that list.
var x = 50;
function draw() {
ctx.clearRect(0, 0, 300, 300);
ctx.beginPath();
ctx.rect(x, 50, 50, 50);
ctx.stroke();
x++;
}
Or, you can combine the rect() and stroke() into one line, and not need to call beginPath(). That's because the rectangle is both created and drawn at the same time, and isn't put in the list.
var x = 50;
function draw() {
ctx.clearRect(0, 0, 300, 300);
ctx.strokeRect(x, 50, 50, 50);
x++;
}
Either way, I advise incrementing x instead of using translate(), because translate() basically moves the imaginary "pen" that is drawing on the canvas. So if you translate(50, 50), and then you try to draw a rectangle at (0, 0) on the canvas, it will actually be at (50, 50).
As Microsoft puts it on MSDN, "The translate method effectively remaps the (0,0) origin on a canvas."
If you repeatedly do that, it will become difficult to keep track of where you're actually drawing.
Your x variable never changes, so your shape will not move. You need to increment x to get movement:
var x = 50;
function draw(){
ctx.clearRect(0,0,300,300);
ctx.translate(1,0);
ctx.rect(x,50,50,50);
ctx.stroke();
x++;
}
In order to get a smooth animation with shapes and other sprites moving across the screen (or even staying still) it would be better to make a clearScreen method that will basically draw over the entire canvas in whatever background color the canvas is. It is basically just a function that will draw a white (or whatever background color you are using) rectangle over the entire canvas. Then, you call the draw function that will make all the necessary drawings. That way, there won't be any trail or anything of the past movements and you won't have to call clearRect() on every single rectangle you make.
Basically, the function will erase the canvas and you can redraw whatever you need to in order to make the animation of the box moving across the screen.
Does that make sense?
EDIT:
Also, to be clear, you would make your own clearScreen method based on what size your canvas is and what color your background is. Its not hard, all it does is draw a rectangle over the screen.
Simply increment x on every call:
var canvas = document.getElementById('mycanvas')
var ctx = canvas.getContext('2d');
var a = setInterval(draw,100);
var x = 50;
function draw(){
canvas.width = canvas.width; // clears the canvas
ctx.rect(x++,50,50,50);
ctx.stroke();
if (x > 250) // resets the position
x = 50;
}
<canvas id="mycanvas"></canvas>
I also removed that translation since there's no need to do it just for the square animation.
Try beginPath() and closePath():
var maxX, x = 0,
s = 50,
maxY;
var repaint = function(ctx) {
if (x + s >= maxX) { //reached end of the canvas
return;
}
ctx.clearRect(0, 0, maxX, maxY); //clear previous
ctx.beginPath(); //start drawing
ctx.rect(x, s, s, s);
ctx.stroke();
ctx.closePath(); //stop drawing
x++;
setTimeout(function() {
repaint(ctx); //continue here
}, 100);
};
var cnvs = document.getElementById('canvas');
maxX = cnvs.width;
maxY = cnvs.height;
repaint(cnvs.getContext('2d'));
canvas {
border: 1px solid grey;
}
<canvas width="360" height="180" id='canvas'>HTML5 canvas not supported</canvas>