How can I draw a scaled image in a fixed canvas and rotate it in its center?

Viewed 30

I have a canvas with fixed width and height and I want draw a scaled/fitted image to the canvas and rotate the image on it's center. When I put any value in angle besides 0, the image is not centered and skews out of the canvas.

This is what I have right now:

const box = document.getElementById("box");
const box2 = document.getElementById("box2");

(function() {
  const image = new Image();
  image.onload = () => {
    const myCanvas = drawImage(0, image);
    const img = document.createElement("img");
    img.src = myCanvas.toDataURL("image/png", 1);
    img.style = "border: 1px solid black;";
    box.append(img)
    
    const myCanvas2 = drawImage(15, image);
    const img2 = document.createElement("img");
    img2.src = myCanvas2.toDataURL("image/png", 1);
    img2.style = "border: 1px solid black;";
    box2.append(img2)
    
  };
  image.src = "https://raw.githubusercontent.com/hamza-ameer/Google-Maps-SmoothCarAnimation/Uodated/car_marker.png";
  image.crossOrigin="anonymous"
})()

function drawImage(angle, image) {
  const canvasWidth = 100;
  const canvasHeight = 100;
  
  const myCanvas = document.createElement("canvas");
  myCanvas.width = canvasWidth;
  myCanvas.height = canvasHeight;


  const context = myCanvas.getContext("2d");

  const imageWidth = image.width;
  const imageHeight = image.height;

  const scaler = scalePreserveAspectRatio(imageWidth, imageHeight, canvasWidth, canvasHeight);

  const scaledWidth = imageWidth * scaler;
  const scaledHeight = imageHeight * scaler;

  context.save();
  context.clearRect(0, 0, canvasWidth, canvasHeight);
  context.rotate((angle * Math.PI) / 180);
  context.translate((canvasWidth - scaledWidth) / 2, (canvasHeight - scaledHeight) / 2);
  context.drawImage(image, 0, 0, imageWidth, imageHeight, 0, 0, scaledWidth, scaledHeight);
  context.strokeRect(0, 0, imageWidth * scaler, imageHeight * scaler);

  context.restore();
  
  return myCanvas;
}

function scalePreserveAspectRatio(imgW, imgH, maxW, maxH) {
  return Math.min(maxW / imgW, maxH / imgH);
}
<div id="box"></div>
<div id="box2"></div>

Thanks for the help.

2 Answers

To set the origin point in the center of an object you can draw the abject at -objectsWidth/2 and -objectsHeight/2. That will place the origin (0,0) point of the canvas in in the objects center. Now you can just translate the object wherever you want on the canvas. Keep in mind when you translate you are moving the (0,0) coordinate of the canvas to a specific spot.

Please keep in mind I purposly did not change the origin point of the stroke because I wanted you to see the difference. You would however do the same thing and draw it with -objectsWidth/2 and -objectsHeight/2.

 context.strokeRect(-scaledWidth/2, -scaledHeight/2, imageWidth * scaler, imageHeight * scaler);

This is the easiest way to place items and set the origin to the center. And btw you don't need to translate back after. That is the point of using save() and restore()

const box = document.getElementById("box");
const box2 = document.getElementById("box2");

(function() {
  const image = new Image();
  image.onload = () => {
    const myCanvas = drawImage(0, image);
    const img = document.createElement("img");
    img.src = myCanvas.toDataURL("image/png", 1);
    img.style = "border: 1px solid black;";
    box.append(img)
    
    const myCanvas2 = drawImage(15, image);
    const img2 = document.createElement("img");
    img2.src = myCanvas2.toDataURL("image/png", 1);
    img2.style = "border: 1px solid black;";
    box2.append(img2)
    
  };
  image.src = "https://raw.githubusercontent.com/hamza-ameer/Google-Maps-SmoothCarAnimation/Uodated/car_marker.png";
  image.crossOrigin="anonymous"
})()

function drawImage(angle, image) {
  const canvasWidth = 100;
  const canvasHeight = 100;
  
  const myCanvas = document.createElement("canvas");
  myCanvas.width = canvasWidth;
  myCanvas.height = canvasHeight;


  const context = myCanvas.getContext("2d");

  const imageWidth = image.width;
  const imageHeight = image.height;

  const scaler = scalePreserveAspectRatio(imageWidth, imageHeight, canvasWidth, canvasHeight);

  const scaledWidth = imageWidth * scaler;
  const scaledHeight = imageHeight * scaler;

  context.save();
  context.clearRect(0, 0, canvasWidth, canvasHeight);
  context.translate(canvasWidth/2, canvasHeight/2);
  context.rotate((angle * Math.PI) / 180);
  context.drawImage(image, 0, 0, imageWidth, imageHeight, -scaledWidth/2, -scaledHeight/2, scaledWidth, scaledHeight);
  context.strokeRect(0, 0, imageWidth * scaler, imageHeight * scaler);
  context.restore();
  
  return myCanvas;
}

function scalePreserveAspectRatio(imgW, imgH, maxW, maxH) {
  return Math.min(maxW / imgW, maxH / imgH);
}
<div id="box"></div>
<div id="box2"></div>

You just need to set the origin (point of rotation) to the center with context.translate before using context.rotate and reset it afterwards.

const box = document.getElementById("box");
const box2 = document.getElementById("box2");

(function() {
  const image = new Image();
  image.onload = () => {
    const myCanvas = drawImage(0, image);
    const img = document.createElement("img");
    img.src = myCanvas.toDataURL("image/png", 1);
    img.style = "border: 1px solid black;";
    box.append(img)
    
    const myCanvas2 = drawImage(15, image);
    const img2 = document.createElement("img");
    img2.src = myCanvas2.toDataURL("image/png", 1);
    img2.style = "border: 1px solid black;";
    box2.append(img2)
    
  };
  image.src = "https://raw.githubusercontent.com/hamza-ameer/Google-Maps-SmoothCarAnimation/Uodated/car_marker.png";
  image.crossOrigin="anonymous"
})()

function drawImage(angle, image) {
  const canvasWidth = 100;
  const canvasHeight = 100;
  
  const myCanvas = document.createElement("canvas");
  myCanvas.width = canvasWidth;
  myCanvas.height = canvasHeight;


  const context = myCanvas.getContext("2d");

  const imageWidth = image.width;
  const imageHeight = image.height;

  const scaler = scalePreserveAspectRatio(imageWidth, imageHeight, canvasWidth, canvasHeight);

  const scaledWidth = imageWidth * scaler;
  const scaledHeight = imageHeight * scaler;

  context.save();
  context.clearRect(0, 0, canvasWidth, canvasHeight);
  //set origin at center
  context.translate((canvasWidth - scaledWidth) / 2, (canvasHeight - scaledHeight) / 2);
  context.rotate((angle * Math.PI) / 180);
  //restore origin to left-top corner
  context.translate(-(canvasWidth - scaledWidth) / 2, -(canvasHeight - scaledHeight) / 2);
  context.drawImage(image, 0, 0, imageWidth, imageHeight, 0, 0, scaledWidth, scaledHeight);
  context.strokeRect(0, 0, imageWidth * scaler, imageHeight * scaler);

  context.restore();
  
  return myCanvas;
}

function scalePreserveAspectRatio(imgW, imgH, maxW, maxH) {
  return Math.min(maxW / imgW, maxH / imgH);
}
<div id="box"></div>
<div id="box2"></div>

Related