javascript 变换后平移画布的方向不正确

wztqucjr  于 2023-09-29  发布在  Java
关注(0)|答案(1)|浏览(85)

我使用HTML5 canvas和Vanilla JavaScript创建了一个小型的HTML5图像查看器。
一切都工作得很好,除了一件事,那就是平移后的变换,如旋转,翻转和镜像。画布向不正确的方向移动,这是完全有道理的。
我能够解决这个问题,“平移后旋转”。我有一个矩阵,我用它来校正不同的Angular (0,90,180和270度)。也许不是最好的解决方案,但它有效。
但是,当我翻转或镜像画布时,画布仍会向错误的方向移动。现在我可以尝试用类似的方法解决它,就像我解决了“旋转后平移”的问题一样,但这似乎不正确。我知道应该用不同的方式,但是怎么做呢?
在我的代码中,我已经注解了'旋转后平移'的解决方案。有人能给予我一个例子,如何解决这个问题的正确方法?
代码有限的Codepen:https://codepen.io/r-w-c/pen/RwEZjGr
Codepen与查看器的完整代码在这里:https://codepen.io/r-w-c/pen/dywRKJL

const IMGURL = "https://i.imgur.com/hjNvQge.jpg";

const CANVASWITH = 500;
const CANVASHEIGHT = 325;
const PANSTEP = 20;

/*
var rotateMatrix = {};
rotateMatrix.left = [];
rotateMatrix.left[0] = [-1, 0];
rotateMatrix.left[90] = [0, 1];
rotateMatrix.left[180] = [1, 0];
rotateMatrix.left[270] = [0, -1];
rotateMatrix.right = [];
rotateMatrix.right[0] = [1, 0];
rotateMatrix.right[90] = [0, -1];
rotateMatrix.right[180] = [-1, 0];
rotateMatrix.right[270] = [0, 1];
rotateMatrix.up = [];
rotateMatrix.up[0] = [0, -1];
rotateMatrix.up[90] = [-1, 0];
rotateMatrix.up[180] = [0, 1];
rotateMatrix.up[270] = [1, 0];
rotateMatrix.down = [];
rotateMatrix.down[0] = [0, 1];
rotateMatrix.down[90] = [1, 0];
rotateMatrix.down[180] = [0, -1];
rotateMatrix.down[270] = [-1, 0];
*/

var canvas,
  context,
  image,
  scaledImgWidth,
  scaledImgHeight,
  startX,
  startY,
  canvasCenterX,
  canvasCenterY,
  scale,
  currentRotation;

window.onload = () => {
  loadImage();
  addEventListeners();
};

function loadImage() {
  canvas = document.getElementById("canvas");
  canvas.width = CANVASWITH;
  canvas.height = CANVASHEIGHT;
  context = canvas.getContext("2d");

  image = new Image();

  image.onload = function () {
    init();
    reset();
  };
  image.src = IMGURL;
}

function init() {
  scale = Math.min(canvas.width / image.width, canvas.height / image.height);
  scaledImgWidth = image.width * scale;
  scaledImgHeight = image.height * scale;
  startX = (canvas.width - scaledImgWidth) / 2;
  startY = (canvas.height - scaledImgHeight) / 2;
  canvasCenterX = canvas.width / 2;
  canvasCenterY = canvas.height / 2;
  context.save();
}

function drawImageToCanvas() {
  context.save();
  context.setTransform(1, 0, 0, 1, 0, 0);
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.restore();
  context.drawImage(image, startX, startY, scaledImgWidth, scaledImgHeight);
}

function pan(x, y) {
  context.translate(x, y);
  drawImageToCanvas();
}
function rotate(degrees) {
  context.translate(canvasCenterX, canvasCenterY);
  context.rotate((Math.PI / 180) * degrees);
  context.translate(-canvasCenterX, -canvasCenterY);
  drawImageToCanvas();
}

function mirror() {
  context.translate(canvasCenterX, canvasCenterY);
  context.scale(-1, 1);
  context.translate(-canvasCenterX, -canvasCenterY);
  drawImageToCanvas();
}

function flip() {
  context.translate(canvasCenterX, canvasCenterY);
  context.scale(1, -1);
  context.translate(-canvasCenterX, -canvasCenterY);
  //currentRotation = (currentRotation + 360 + 180) % 360;
  drawImageToCanvas();
}

function reset() {
  context.restore();
  context.save();
  drawImageToCanvas();
  //currentRotation = 0;
}

function addEventListeners() {
  document.getElementById("rotateRight").addEventListener("click", function () {
    //currentRotation = (currentRotation + 90) % 360;
    rotate(90);
  });
  document
    .getElementById("rotateLeft")
    .addEventListener("click", function (event) {
      //currentRotation = (currentRotation + 270) % 360;
      rotate(-90);
    });
  document.getElementById("mirror").addEventListener("click", function (event) {
    mirror();
  });
  document.getElementById("flip").addEventListener("click", function (event) {
    flip();
  });
  document
    .getElementById("panLeft")
    .addEventListener("click", function (event) {
      pan(-PANSTEP, 0);
      //pan(
      //rotateMatrix.left[currentRotation][0] * PANSTEP,
      //rotateMatrix.left[currentRotation][1] * PANSTEP
      //);
    });
  document
    .getElementById("panRight")
    .addEventListener("click", function (event) {
      pan(PANSTEP, 0);
      //pan(
      //rotateMatrix.right[currentRotation][0] * PANSTEP,
      //rotateMatrix.right[currentRotation][1] * PANSTEP
      //);
    });
  document.getElementById("panUp").addEventListener("click", function (event) {
    pan(0, -PANSTEP);
    //pan(
    //rotateMatrix.up[currentRotation][0] * PANSTEP,
    //rotateMatrix.up[currentRotation][1] * PANSTEP
    //);
  });
  document
    .getElementById("panDown")
    .addEventListener("click", function (event) {
      pan(0, PANSTEP);
      //pan(
      //rotateMatrix.down[currentRotation][0] * PANSTEP,
      //rotateMatrix.down[currentRotation][1] * PANSTEP
      //);
    });
}
.canvas-container {
  flex-direction: column;
  border: 2px solid #333;
  padding: 10px;
}

canvas {
  background: #777;
  cursor: crosshair;
}

.canvas-controls {
  padding: 5px;
}

button span {
  display: inline-block;
  width: 30px;
}

span.rot {
  transform: rotate(90deg);
}
<div class="canvas-container">
      <canvas id="canvas">
      </canvas>
   
    <div class="canvas-controls flex">
      <button id="panLeft" title="Pan left"><span>&lArr;</span></button>
      <button id="panRight" title="Pan right"><span>&rArr;</span></button>
      <button id="panUp" title="Pan up"><span>&uArr;</span></button>
      <button id="panDown" title="Pan down"><span>&dArr;</span></button>      
      <button id="rotateLeft" title="Rotate left"><span>&#10226;</span></button>
      <button id="rotateRight" title="Rolotate right"><span>&#10227;</span></button>
      <button id="mirror" title="Mirror"><span>&#8697;</span></button>
      <button id="flip" title="Flip"><span class="rot">&#8697;</span></button>
    </div>
  </div>

xxe27gdn

xxe27gdn1#

在翻转或镜像画布后平移时遇到的问题与画布变换如何影响平移方向有关。当您应用翻转或镜像等变换时,它会更改画布的坐标系,进而影响平移的方向。
要正确解决此问题,您需要跟踪画布的当前变换状态并相应地调整平移。您可以通过维护变换矩阵并将其应用于平移操作来实现此目的。下面是如何修改代码来实现这一点:

const transformMatrix = {
  a: 1, // Horizontal scaling
  b: 0, // Vertical skewing
  c: 0, // Horizontal skewing
  d: 1, // Vertical scaling
  e: 0, // Horizontal translation (panning)
  f: 0, // Vertical translation (panning)
};

function applyMatrix() {
  context.setTransform(
    transformMatrix.a,
    transformMatrix.b,
    transformMatrix.c,
    transformMatrix.d,
   transformMatrix.e,
   transformMatrix.f
  );
}

function pan(x, y) {
  transformMatrix.e += x;
  transformMatrix.f += y;
  applyMatrix();
  drawImageToCanvas();
}

function mirror() {
  transformMatrix.a *= -1; // Flip horizontally
  applyMatrix();
  drawImageToCanvas();
}

function flip() {
  transformMatrix.d *= -1; // Flip vertically
  applyMatrix();
  drawImageToCanvas();
}

function reset() {
  // Reset the transformation matrix
  transformMatrix.a = 1;
  transformMatrix.b = 0;
  transformMatrix.c = 0;
  transformMatrix.d = 1;
  transformMatrix.e = 0;
  transformMatrix.f = 0;
  applyMatrix();
  drawImageToCanvas();
}

通过这些修改,您现在可以跟踪当前的转换矩阵,并使用setTransform将其应用到画布。这样可以确保平移、翻转和镜像正确地一起工作,因为它们会考虑画布的当前变换状态。
请记住在事件侦听器中相应地更新平移、镜像和翻转函数。通过这些更改,您的画布在翻转或镜像后应正确平移,而无需任何其他矩阵用于不同Angular 。
代码片段:

const IMGURL = "https://i.imgur.com/hjNvQge.jpg";

const CANVASWITH = 500;
const CANVASHEIGHT = 325;
const PANSTEP = 20;

const transformMatrix = {
  a: 1, // Horizontal scaling
  b: 0, // Vertical skewing
  c: 0, // Horizontal skewing
  d: 1, // Vertical scaling
  e: 0, // Horizontal translation (panning)
  f: 0, // Vertical translation (panning)
};

var canvas,
  context,
  image,
  scaledImgWidth,
  scaledImgHeight,
  startX,
  startY,
  canvasCenterX,
  canvasCenterY,
  scale;

window.onload = () => {
  loadImage();
  addEventListeners();
};

function loadImage() {
  canvas = document.getElementById("canvas");
  canvas.width = CANVASWITH;
  canvas.height = CANVASHEIGHT;
  context = canvas.getContext("2d");

  image = new Image();

  image.onload = function() {
    init();
    reset();
  };
  image.src = IMGURL;
}

function init() {
  scale = Math.min(canvas.width / image.width, canvas.height / image.height);
  scaledImgWidth = image.width * scale;
  scaledImgHeight = image.height * scale;
  startX = (canvas.width - scaledImgWidth) / 2;
  startY = (canvas.height - scaledImgHeight) / 2;
  canvasCenterX = canvas.width / 2;
  canvasCenterY = canvas.height / 2;
  context.save();
}

function drawImageToCanvas() {
  context.save();
  context.setTransform(1, 0, 0, 1, 0, 0);
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.restore();
  context.drawImage(image, startX, startY, scaledImgWidth, scaledImgHeight);
}

function pan(x, y) {
  transformMatrix.e += x;
  transformMatrix.f += y;
  applyMatrix();
  drawImageToCanvas();
}

function rotate(degrees) {
  context.translate(canvasCenterX, canvasCenterY);
  context.rotate((Math.PI / 180) * degrees);
  context.translate(-canvasCenterX, -canvasCenterY);
  drawImageToCanvas();
}

function mirror() {
  transformMatrix.a *= -1; // Flip horizontally
  applyMatrix();
  drawImageToCanvas();
}

function flip() {
  transformMatrix.d *= -1; // Flip vertically
  applyMatrix();
  drawImageToCanvas();
}

function reset() {
  // Reset the transformation matrix
  transformMatrix.a = 1;
  transformMatrix.b = 0;
  transformMatrix.c = 0;
  transformMatrix.d = 1;
  transformMatrix.e = 0;
  transformMatrix.f = 0;
  applyMatrix();
  drawImageToCanvas();
}

function applyMatrix() {
  context.setTransform(
    transformMatrix.a,
    transformMatrix.b,
    transformMatrix.c,
    transformMatrix.d,
    transformMatrix.e,
    transformMatrix.f
  );
}

function addEventListeners() {
  document.getElementById("rotateRight").addEventListener("click", function() {
    rotate(90);
  });
  document
    .getElementById("rotateLeft")
    .addEventListener("click", function(event) {
      rotate(-90);
    });
  document.getElementById("mirror").addEventListener("click", function(event) {
    mirror();
  });
  document.getElementById("flip").addEventListener("click", function(event) {
    flip();
  });
  document
    .getElementById("panLeft")
    .addEventListener("click", function(event) {
      pan(-PANSTEP, 0);
    });
  document
    .getElementById("panRight")
    .addEventListener("click", function(event) {
      pan(PANSTEP, 0);
    });
  document.getElementById("panUp").addEventListener("click", function(event) {
    pan(0, -PANSTEP);
  });
  document
    .getElementById("panDown")
    .addEventListener("click", function(event) {
      pan(0, PANSTEP);
    });
}
.canvas-container {
  flex-direction: column;
  border: 2px solid #333;
  padding: 10px;
}

canvas {
  background: #777;
  cursor: crosshair;
}

.canvas-controls {
  padding: 5px;
}

button span {
  display: inline-block;
  width: 30px;
}

span.rot {
  transform: rotate(90deg);
}
<div class="canvas-container">
  <canvas id="canvas">
      </canvas>

  <div class="canvas-controls flex">
    <button id="panLeft" title="Pan left"><span>&lArr;</span></button>
    <button id="panRight" title="Pan right"><span>&rArr;</span></button>
    <button id="panUp" title="Pan up"><span>&uArr;</span></button>
    <button id="panDown" title="Pan down"><span>&dArr;</span></button>
    <button id="rotateLeft" title="Rotate left"><span>&#10226;</span></button>
    <button id="rotateRight" title="Rolotate right"><span>&#10227;</span></button>
    <button id="mirror" title="Mirror"><span>&#8697;</span></button>
    <button id="flip" title="Flip"><span class="rot">&#8697;</span></button>
  </div>
</div>

相关问题