How to draw a rectangle on canvas in JavaScript

How to draw a rectangle on canvas in JavaScript

Setting up your environment is crucial before diving into any programming task, especially when it comes to graphics. The core concept revolves around the idea of a canvas. In most web applications, this is typically achieved using the HTML5 <canvas> element, which allows for drawing graphics via JavaScript.

To begin, you need to create a canvas in your HTML. Here’s a simple setup:

<canvas id="myCanvas" width="500" height="500"></canvas>

Once you have your canvas in place, you can access it within your JavaScript code. This is where the real fun begins. To get a 2D drawing context, you can use the following code:

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

The ctx variable now gives you access to all the drawing methods provided by the 2D context. It’s like having a set of brushes and colors at your disposal. You can draw shapes, lines, and even manipulate images with relative ease.

Now that you have your canvas and context set up, you might be wondering what to paint first. Getting familiar with basic shapes is a natural starting point. Drawing a rectangle is one of the simplest tasks you can accomplish, and it paves the way for understanding how to fill and stroke shapes with color.

Here’s how you can draw a filled rectangle:

ctx.fillStyle = 'blue';
ctx.fillRect(50, 50, 150, 100);

The above code will create a blue rectangle starting at the coordinates (50, 50) with a width of 150 pixels and a height of 100 pixels. You can change the fillStyle to any CSS color value you prefer. This is the beauty of the canvas API: it’s straightforward and, at the same time, incredibly flexible.

But as much as we want to keep things simple, you should also consider how your drawings can evolve beyond basic shapes. Once you get comfortable with rectangles, transitioning to more complex shapes and designs will be a breeze. Enhancements like gradients, shadows, and even animations will start to enrich your drawings significantly.

As you think about beautifying your canvas creations, remember that integrating user interactions can also bring your graphics to life. Whether it’s responding to mouse clicks or keyboard events, these interactions can lead to a richer user experience. For instance, imagine allowing users to draw on the canvas themselves.

To enable drawing, you can start by listening for mouse events. Here’s a basic example:

let isDrawing = false;

canvas.addEventListener('mousedown', () => {
  isDrawing = true;
});

canvas.addEventListener('mousemove', (event) => {
  if (!isDrawing) return;
  ctx.lineTo(event.clientX - canvas.offsetLeft, event.clientY - canvas.offsetTop);
  ctx.stroke();
});

canvas.addEventListener('mouseup', () => {
  isDrawing = false;
  ctx.beginPath();
});

This setup allows for a basic drawing capability on your canvas. It captures the mouse down event to start drawing, tracks the mouse movement, and stops when the mouse button is released. With just these few lines, you can create an interactive drawing application that makes the canvas an engaging space.

The simple beauty of drawing a filled rectangle

Now that we have the basics down, let’s take a moment to refine our drawing capabilities. While a simple filled rectangle is a great start, we can enhance our visuals by introducing some style. One way to elevate your canvas output is to incorporate gradients. Gradients allow for a smooth transition between colors, which can add depth and interest to your shapes.

To create a linear gradient, you can use the createLinearGradient method available on the canvas context. Here’s how to set it up:

const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'yellow');
ctx.fillStyle = gradient;
ctx.fillRect(50, 50, 150, 100);

This code snippet will draw a rectangle with a gradient that transitions from red to yellow. The createLinearGradient method takes four parameters that define the starting and ending points of the gradient. You can add multiple color stops to create more complex gradients.

After mastering gradients, the next logical step is to implement shadows. Shadows can give your shapes a three-dimensional appearance. To enable shadows, you need to set a few properties on the context:

ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.fillStyle = 'blue';
ctx.fillRect(50, 50, 150, 100);

This will create a blue rectangle with a shadow that is offset and blurred, adding a layer of realism to your drawing. Adjusting the shadowBlur and shadowOffset values can help you achieve various effects, so experiment with different settings to find what looks best.

Now, if we want to move beyond static shapes and shadows, animations can really bring your canvas to life. JavaScript provides a straightforward way to animate your drawings by using the requestAnimationFrame method, which creates a smooth animation loop. Here’s an example of how to animate a rectangle moving across the canvas:

let x = 0;
function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
  ctx.fillStyle = 'blue';
  ctx.fillRect(x, 50, 150, 100); // Draw the rectangle
  x += 2; // Move the rectangle to the right
  if (x > canvas.width) x = 0; // Reset position
  requestAnimationFrame(animate); // Call animate again for the next frame
}
animate(); // Start the animation

This code initializes a position for the rectangle and then creates an animation loop that moves the rectangle to the right. The clearRect method is used to clear the canvas before each frame is drawn, allowing you to see the movement. The rectangle resets its position once it moves off the canvas, creating a seamless animation effect.

As you explore further, consider how you can combine all these techniques-gradients, shadows, and animations-into a single cohesive design. The possibilities are vast, and each enhancement adds layers of complexity and beauty to your canvas projects. You might also find that incorporating event listeners for user interactions can lead to unique, dynamic creations.

For example, you could let users control the movement of shapes with their mouse, or even create a game where the canvas responds to keyboard events. Such interactivity turns your static drawings into an engaging experience, opening up a new realm of possibilities. Here’s a brief illustration of how you could implement keyboard controls:

document.addEventListener('keydown', (event) => {
  if (event.key === 'ArrowRight') {
    x += 10; // Move right
  } else if (event.key === 'ArrowLeft') {
    x -= 10; // Move left
  } else if (event.key === 'ArrowUp') {
    // Additional controls can be added for up/down movement
  }
});

This snippet allows the rectangle to move left or right based on the arrow keys pressed. Each key press adjusts the position variable, which is then used in the animation loop. This makes the canvas not only a canvas but a playground for creativity and interactivity.

As you proceed with your canvas journey, remember that combining these elements thoughtfully will lead to creations that are visually appealing and interactive. The canvas is a powerful tool, and with a bit of imagination and experimentation, you can produce stunning graphics that captivate your audience.

Now let’s make it look less like 1995

Moving shapes around is one thing, but to make things truly dynamic, you need to be able to rotate and scale them. The canvas 2D context provides a set of transformation methods that operate on a transformation matrix behind the scenes. You don’t usually have to touch the matrix directly; methods like rotate(), scale(), and translate() are your friends. But there’s a catch you’ll run into almost immediately.

If you just call ctx.rotate() and then ctx.fillRect(), you’ll see your rectangle swing around the top-left corner of the canvas (the 0,0 point), which is almost never what you want. You want to rotate it around its own center. The trick is to first move the canvas origin to the center of your shape, then rotate, and then draw the shape relative to that new origin. Don’t forget to save and restore the canvas state so your transformations don’t affect everything else you draw later.

// Let's rotate a rectangle around its center
const rectX = 150;
const rectY = 100;
const rectWidth = 100;
const rectHeight = 75;
const angleInRadians = Math.PI / 4; // 45 degrees

ctx.save(); // Save the current state
// Translate to the center of the rectangle
ctx.translate(rectX + rectWidth / 2, rectY + rectHeight / 2);
// Rotate the canvas
ctx.rotate(angleInRadians);
// Draw the rectangle, but centered on the new origin
ctx.fillStyle = 'green';
ctx.fillRect(-rectWidth / 2, -rectHeight / 2, rectWidth, rectHeight);
ctx.restore(); // Restore the state to what it was before we started

See what happened there? We used ctx.translate() to move the entire coordinate system. Then, ctx.rotate() rotated that new system. Finally, we drew the rectangle at (-width/2, -height/2) so that its center would be at the new (0,0) origin. Using ctx.save() and ctx.restore() is non-negotiable here; it’s the only sane way to apply transformations to a single object without creating a complete mess.

Rectangles are fine, but boring. Real applications need custom shapes. This is where paths come in. You tell the canvas you’re starting a new path with beginPath(), then you use methods like moveTo() (to lift the pen and move it to a starting point) and lineTo() (to draw a line from the current point to a new one). When you’re done, you can either fill() the shape or stroke() its outline.

ctx.beginPath();
ctx.moveTo(250, 100); // Start at the top point
ctx.lineTo(300, 200); // Line to the bottom-right point
ctx.lineTo(200, 200); // Line to the bottom-left point
ctx.closePath(); // This automatically draws a line back to the start point

ctx.fillStyle = 'purple';
ctx.fill(); // Fill the path
ctx.strokeStyle = 'black';
ctx.lineWidth = 3;
ctx.stroke(); // Stroke the outline

Eventually, you’ll want to draw images, not just colored shapes. This is what drawImage() is for. But there’s a huge, gigantic, elephant-sized gotcha waiting for you: images load asynchronously. If you create an Image object and immediately try to draw it, it won’t work, because the browser is still busy downloading it. You absolutely must wait for the onload event to fire.

const myImage = new Image();
myImage.src = 'path/to/your/sprite.png'; // This starts the download

myImage.onload = function() {
  // This code only runs AFTER the image has been loaded
  ctx.drawImage(myImage, 50, 200); // Draw the image at coordinates (50, 200)
};

// If you put ctx.drawImage() here, it would fail silently.
// Don't do it.

The drawImage method is more powerful than it looks. It has overloads that let you scale the image as you draw it, or even draw just a small slice of a larger spritesheet, which is the cornerstone of 2D game animation. For example, to draw a 32×32 pixel sprite from a spritesheet located at (64, 0) in the source image and place it on the canvas at (100, 100) with a size of 64×64, you’d do this:

// Assume mySpritesheet is an Image object that has already loaded
const spriteX = 64; // x position on the spritesheet
const spriteY = 0;  // y position on the spritesheet
const spriteWidth = 32;
const spriteHeight = 32;

const canvasX = 100; // x position on the canvas
const canvasY = 100; // y position on the canvas
const renderWidth = 64; // make it bigger on the canvas
const renderHeight = 64;

// The full version of drawImage
ctx.drawImage(
  mySpritesheet,
  spriteX, spriteY, spriteWidth, spriteHeight, // Source rectangle
  canvasX, canvasY, renderWidth, renderHeight  // Destination rectangle
);

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *