Problem when trying to rotate a triangle using the javascript canvas

Asked

Viewed 63 times

1

Guys, below, I have the following codes in a basic structure using html, css and javascript.
I tried anyway, but I couldn’t rotate the triangle in the middle when the user slides the input range. Detail, the native function of the canvas (Rotate) will not work for me, because I want to understand this concept using only operations with matrices, as it is, more or less, in the javascript section.

In advance, I thank everyone and if the question is not clear, execute the code, which is even rotating the triangle, but not as expected.

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

// Para criar o triângulo no meio do canvas.
const matriz = [
  [canvas.width / 2, canvas.height / 2 - 50],
  [canvas.width / 2 - 50, canvas.height / 2 + 50],
  [canvas.width / 2 + 50, canvas.height / 2 + 50],
]; // Pontos iniciais (coordenadas x e y) do triângulo no canvas.

let m = [...matriz]; // CLONA A MATRIZ

if (canvas) {
  if (ctx) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();

    ctx.moveTo(m[0][0], m[0][1]);
    ctx.lineTo(m[1][0], m[1][1]);
    ctx.lineTo(m[2][0], m[2][1]);

    ctx.stroke();

    ctx.fill();
  } else {
    alert('Sem contexto!');
  }
} else {
  alert('O navegador não pode renderizar o canvas');
}

// Fonte: https://stackoverflow.com/questions/27205018/multiply-2-matrices-in-javascript
function multiplyMatrices(matrizA, matrizB) {
  var result = [];
  for (var i = 0; i < matrizA.length; i++) {
    result[i] = [];
    for (var j = 0; j < matrizB[0].length; j++) {
      var sum = 0;
      for (var k = 0; k < matrizA[0].length; k++) {
        sum += matrizA[i][k] * matrizB[k][j];
      }
      result[i][j] = sum;
    }
  }
  return result[0];
}

document.getElementById('rotacao').oninput = function({
  target
}) {

  let angulo = parseInt(target.value, 10);

  let alfa = angulo * (Math.PI / 180);

  let matrizRotacao = [
    [Math.cos(alfa), -Math.sin(alfa)],
    [Math.sin(alfa), Math.cos(alfa)],
  ];

  m = m.map((el) => {
    return multiplyMatrices([el], matrizRotacao);
  });

  // ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.beginPath();
  ctx.moveTo(m[0][0], m[0][1]);
  ctx.lineTo(m[1][0], m[1][1]);
  ctx.lineTo(m[2][0], m[2][1]);
  ctx.fill();
};
body {
  font-family: 'Nunito Sans', sans-serif;
  text-transform: uppercase;
  overflow: hidden;
}

canvas {
  border: 1px solid #ccc;
}

.main {
  display: flex;
  flex-direction: column;
}

.controles-container {
  display: flex;
  flex-direction: row;
}

.input {
  text-align: center;
  border-left: 1px solid #ccc;
  border-top: 1px solid #ccc;
}

.end {
  border-right: 1px solid #ccc;
}
<div class="main">
  <div class="controles-container">
    <div class="input">
      ROTACIONAR<br />
      <input type="range" name="" step="1" min="0" max="360" id="rotacao" value="0" />
    </div>
  </div>
  <canvas id="thecanvas" width="600" height="600">
        Se você vir isso, significa que seu navegador não oferece suporte os
        novos elementos de tela HTML5 legais. Rapaz, é <em> você </em>
        perdendo! Você pode querer atualizar.
      </canvas>
</div>

1 answer

1


const canvas = document.getElementById('thecanvas');   

let dpr = window.devicePixelRatio || 1; //Pega a proporção entre um pixel CSS e um pixel físico
let rect = canvas.getBoundingClientRect(); //Pega o tamanho do canvas em pixels
canvas.width = rect.width * dpr;   //Ajusta a largura apropriada ao canvas.
canvas.height = rect.height * dpr; //Ajusta a altura apropriada ao canvas.
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);   //Escalona as operações gráficas para operar com tamanho do pixel CSS.

let rotacao = document.getElementById('rotacao'); //pega referência do range

//No caso eu girei o triangulo pelo baricentro. Pode ser usado o incêntro, ortocentro ou outro centro poligonal depende do efeito de rotação que queira aplicar.
function baricentro(t) {
  return [(t[0][0] + t[1][0] + t[2][0]) / 3,
          (t[0][1] + t[1][1] + t[2][1]) / 3]
}

//Coordenas do triângulo com relação a origem do canvas.
const matriz = [
  [0, -50],
  [-50, 50],
  [50, 50],
];

let m = [...matriz];

//Adiciona evento input.
rotacao.addEventListener('input', function({target}) {

  let angulo = parseInt(target.value, 10);

  let alfa = angulo * (Math.PI / 180);

  let bar = baricentro(m); //Calcula o baricentro
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.save(); //Salva a matriz de trasformação
  ctx.translate(bar[0], -bar[1]); //Translada o canvas para o baricentro do triangulo.
  ctx.translate(canvas.width / 2, canvas.height / 2); //Translada o canvas para o centro da tela.
  ctx.rotate(alfa); //Rotaciona o canvas.

  //Desenha o triangulo sempre nas mesmas coordenadas a diferença é o canvas cujo a origem foi transladada e suas bases canônicas foram rotacionadas.
  ctx.beginPath();  
  ctx.moveTo(m[0][0], m[0][1]);
  ctx.lineTo(m[1][0], m[1][1]);
  ctx.lineTo(m[2][0], m[2][1]);
  ctx.fill();

  ctx.restore(); //Restaura a matriz de trasformação
});

//Dispara o evento input de rotacao para inializar o programa com uma imagem no canvas
rotacao.dispatchEvent(new InputEvent('input'));
body {
  font-family: 'Nunito Sans', sans-serif;
  text-transform: uppercase;
  overflow: hidden;
}

canvas {
  border: 1px solid #ccc;
}

.main {
  display: flex;
  flex-direction: column;
}

.controles-container {
  display: flex;
  flex-direction: row;
}

.input {
  text-align: center;
  border-left: 1px solid #ccc;
  border-top: 1px solid #ccc;
}

.end {
  border-right: 1px solid #ccc;
}
<div class="main">
  <div class="controles-container">
    <div class="input">
      ROTACIONAR<br />
      <input type="range" name="" step="1" min="0" max="360" id="rotacao" value="0" />
    </div>
  </div>
  <canvas id="thecanvas" >
       
      </canvas>
</div>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.