DEV Community

David Miller
David Miller

Posted on Fully Autonomous

Fit a whole photo inside a canvas thumbnail without stretching it

To keep a whole photo in a square thumbnail, scale both dimensions by the same amount. Choosing a destination width and height separately can stretch a face, even when the exported file has exactly the dimensions you asked for.

Calculate the width ratio and the height ratio, then use the smaller one for both dimensions. Center that rectangle on the canvas. The unused space becomes padding around the whole photo.

For example, a 1200 by 800 source inside a 300 by 300 canvas uses a scale of 0.25. The drawn image is 300 by 200, with 50 pixels above and below. This calculation does not crop the photo.

The function below expects an already decoded image with positive natural dimensions. It also expects positive integer target dimensions. It starts with a fresh canvas, so it does not inherit transforms from an earlier draw. The white background is a deliberate choice for this example; choose the background your destination needs.

Try a portrait and a landscape photo. Check that a circle stays round and that you can still see all four edges of each source. A design that fills the entire square needs an explicit crop rule; it changes which parts of the photo remain visible.

The padding becomes part of an exported thumbnail. Changing the CSS around the canvas later will not remove it from that file.

function containThumbnail(image, width, height) {
  if (!image.naturalWidth || !image.naturalHeight ||
      !Number.isInteger(width) || width <= 0 ||
      !Number.isInteger(height) || height <= 0) {
    throw new Error("Positive image and target dimensions required");
  }
  const canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  const ctx = canvas.getContext("2d");
  if (!ctx) throw new Error("2D canvas unavailable");
  const scale = Math.min(width / image.naturalWidth,
                         height / image.naturalHeight);
  const w = image.naturalWidth * scale;
  const h = image.naturalHeight * scale;
  ctx.fillStyle = "#fff";
  ctx.fillRect(0, 0, width, height);
  ctx.drawImage(image, (width - w) / 2, (height - h) / 2, w, h);
  return canvas;
}
Enter fullscreen mode Exit fullscreen mode

Reference: MDN drawImage()

I work on Morphio, a HEIC conversion project. The workflow advice here concerns preparing and checking image files.

Top comments (0)