Transformações 2D
Como mover, rotacionar e escalar objetos usando matrizes 3×3 com coordenadas homogêneas — a base de todo motor gráfico, CSS transforms e SVG.
Transformações como matrizes
Para mover um sprite de jogo, você soma um offset à sua posição. Para rotacioná-lo, você aplica seno e cosseno. Para escaloná-lo, você multiplica. São três operações diferentes — mas existe um formalismo que unifica tudo: matrizes de transformação.
O truque são as coordenadas homogêneas: adicionar uma terceira coordenada w=1
ao vetor 2D (x, y) permite que translação também vire uma multiplicação de matriz 3×3.
Com isso, qualquer sequência de transformações — por mais complexa que seja — se reduz a
uma única multiplicação de matriz.
transform, SVG transform, Canvas 2D,
e todo motor de jogo 2D usam exatamente esse formalismo por baixo. Quando você escreve
transform: rotate(45deg) scale(2), o browser calcula um produto de matrizes 3×3
e aplica ao elemento.
Explore as transformações
Mova os sliders e veja a forma "F" ser transformada em tempo real. A forma fantasma mostra a posição original. A matriz 3×3 exibe os valores calculados.
As três matrizes e como combiná-las
// Transformações 2D com coordenadas homogêneas // Translação: ponto (x,y) se move para (x+tx, y+ty) const T = [ 1, 0, tx, 0, 1, ty, 0, 0, 1, ]; // Rotação por ângulo θ (em radianos) const R = [ Math.cos(θ), -Math.sin(θ), 0, Math.sin(θ), Math.cos(θ), 0, 0, 0, 1, ]; // Escala const S = [ sx, 0, 0, 0, sy, 0, 0, 0, 1, ]; // Combinar T * R * S numa única matriz: const M = matMul3x3(T, matMul3x3(R, S)); // Aplicar ao vértice (x, y): const v = [x, y, 1]; // coord. homogênea const vt = matVec3(M, v); // → [x', y', 1]
Por que coordenadas homogêneas?
// Por que "coordenadas homogêneas"? // Sem elas, translação NÃO é uma multiplicação de matriz. // Com 2D puro (2×2): translação seria uma SOMA separada const pt2D = [x + tx, y + ty]; // não é produto de matriz // Com coordenadas homogêneas (3×3): translação VIRA produto // → podemos combinar T, R e S numa única matriz! // Ordem importa: T * R * S ≠ R * T * S // "Escala, depois rotaciona, depois translada" = T * R * S // "Translada, depois rotaciona, depois escala" = S * R * T // Canvas 2D usa a mesma ideia: ctx.translate(tx, ty); ctx.rotate(angle); ctx.scale(sx, sy); ctx.drawImage(...); // aplica T * R * S ao drawImage
Motor de sprites 2D
Mini projeto: implemente uma função applyTransform(vertices, tx, ty, angle, sx, sy) que transforma um array de pontos 2D com a matriz composta T×R×S. Teste com um triângulo apontado para cima.
Projeto principal: crie um sistema de hierarquia de objetos (scene graph): um objeto filho herda a transformação do pai. Por exemplo, uma roda que gira em torno de si mesma enquanto orbita o centro da tela — como a Lua ao redor da Terra. Implemente com multiplicação de matrizes acumuladas.
Desafio extra: implemente transformação de ponto fixo — rotacionar em torno de um ponto arbitrário (não a origem). Dica: translate para mover o ponto de pivot à origem, rotate, translate de volta. Verifique que isso equivale a uma única multiplicação de T×R×S cuidadosamente escolhida.
Teste sua intuição
rotate(45°) × translate(100,0) e translate(100,0) × rotate(45°)?
transform: rotate(45deg) scale(2)?
Onde você encontra isso
Engines de jogos 2D
Unity, Godot e Phaser representam cada objeto com uma matriz de transformação. Parent-child hierarchy multiplica matrizes em cadeia. A GPU recebe a matriz final pré-calculada — transformar 10.000 sprites é só 10.000 multiplicações de matrizes.
CSS e SVG
transform: matrix(a, b, c, d, e, f) é exatamente uma matriz 3×3 em coordenadas homogêneas. Animações CSS com will-change: transform são processadas direto na GPU, bypassing o layout engine do browser.
Renderização de UI
Frameworks como Flutter e SwiftUI constroem uma árvore de widgets, cada um com sua própria matriz de transformação. O "compositor" da GPU concatena as matrizes ao longo da hierarquia e renderiza tudo num único draw call quando possível.