setTransform 和 Scale 以适合给定图像 Canvas

setTransform and Scale to fit Canvas for the given image

我在 Canvas 上有一张图片。 渲染图像后,我将 canvas 顺时针旋转 90 度,然后必须缩放以适合 canvas。

Scaling an image to fit on canvas

有很多可用的解决方案,但不幸的是 none 其中对我有用

这就是我现在拥有的。

width = 300; // after rotation
                    height = 400; // after rotation
                    var scale = width / img.height; // how much to scale the image to fit

                    console.log(scale);
                    canvas.width = width;
                    canvas.height = height;
                    ctx.setTransform(
                         0, scale, // x axis down the screen
                        -scale, 0, // y axis across the screen from right to left
                        width,    // x origin is on the right side of the canvas
                        0         // y origin is at the top
                    );
                    ctx.drawImage(img, 0, 0);
                    ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default

目前,canvas 可以正常旋转,但图像无法根据 canvas 宽度和高度进行适当调整或缩放。 原始图像很大,我想将其缩放以适应 canvas 上的 300x400。

原图 -

实际结果图像看起来像这样,没有缩放完整图像 -

要使图像适合显示区域,请使用最小比例以适合高度或适合宽度。

// size of canvas
const width = 100; 
const height = 298;

// image is assumed loaded and ready to be drawn

// Get min scale to fit
var scale = Math.min(width / image.width , height / image.height);

// you can also scale to fill using max
// var scale = Math.max(width / image.width , height / image.height);

canvas.width = width;
canvas.height = height;

// // draw image from top left corner
ctx.setTransform(scale, 0, 0, scale, 0, 0);
ctx.drawImage(image, 0, 0);
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default

更新

重新评论

由于您遇到了麻烦,而且我仍然不确定您要做什么图像,这里有一个通用功能,可以将图像旋转 90 度,并缩放以适应、填充和自然。 (底部代码片段中的演示版本稍微不那么冗长)

// img image to draw
// rot 1 unit = 90 deg. scaleType fit or fill
function drawRotatedImage(img, rot = 1, scaleType = "fit") {
  const w = img.naturalWidth;
  const h = img.naturalHeight;

  // direction of xAxis
  var xAxisX = Math.cos(rot * Math.PI / 2);
  var xAxisY = Math.sin(rot * Math.PI / 2);

  // modified transform image width and height to match the xAxis
  const tw = Math.abs(w * xAxisX - h * xAxisY);
  const th = Math.abs(w * xAxisY + h * xAxisX);

  var scale = 1; 
  if (scaleType === "fit") {
    scale = Math.min(canvas.width / tw, canvas.height / th);
  } else if (scaleType === "fill") {
    scale = Math.max(canvas.width / tw, canvas.height / th);
  }

  xAxisX *= scale;
  xAxisY *= scale;

  // Rotate scale to match scaleType . Center on canvas
  ctx.setTransform(xAxisX, xAxisY, -xAxisY, xAxisX, canvas.width / 2, canvas.height / 2);

  // Image drawn offset so center is at canvas center
  ctx.drawImage(img, -w / 2, -h / 2, w, h);
}

并确保以下是使用您的图像经过测试的运行示例。

const canWidth = 300;
const canHeight = 400;
const rot = 1;           
const scaleType = "fit"; 
const PI90 = Math.PI / 2;

function drawRotatedImage(img, rot, scale) {
  ctx.setTransform(1, 0, 0, 1, 0, 0); 
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  const w = img.naturalWidth, h = img.naturalHeight;
  var xAX = Math.cos(rot *= PI90 ), xAY = Math.sin(rot);
  const tw = Math.abs(w * xAX - h * xAY);
  const th = Math.abs(w * xAY + h * xAX);
  scale = Math[scale === "fit" ? "min" : "max" ](canvas.width / tw, canvas.height / th);
  xAX *= scale;
  xAY *= scale;
  ctx.setTransform(xAX, xAY, -xAY, xAX, canvas.width / 2, canvas.height / 2);
  ctx.drawImage(img, -w / 2, -h / 2, w, h);
}









// Stuff for demo and test. unrelated to answer.


const ctx = canvas.getContext("2d");  
// size canvas
canvas.width = canWidth;
canvas.height = canHeight;
const img = new Image;
const tests = [[0,"fit"], [1,"fit"], [2,"fit"], [3,"fit"],[0,"fill"], [1,"fill"], [2,"fill"], [3,"fill"]];

tests.clicker = 0;
img.src = "https://i.stack.imgur.com/SQmuv.jpg";
img.addEventListener("load", () => {
  info.textContent = ((rot % 4) * 90) + "deg CW scaled to " + scaleType.toUpperCase() + " click canvas to rotate and switch scales";
  drawRotatedImage(img, rot, scaleType)
});


canvas.addEventListener("click",() => {
  const test = tests[tests.clicker++ % tests.length];
  info.textContent = ((test[0] % 4) * 90) + "deg CW scaled to " + test[1].toUpperCase();
  drawRotatedImage(img, ...test)
});
body {
   font-family: "arial black";
}
canvas {
  border: 1px solid black;
  position: absolute;
  top: 30px;
  left: 10px;    
}
#info {
  position: absolute;
  top: 2px;
  left: 10px;
}
<canvas id="canvas"></canvas>

<div id="info"></div>