2D camera controls

by Gregg Tavares

HTML

<canvas></canvas>
<ul>
<li>wheel to zoom</li>
<li>drag to move</li>
<li>shift drag to rotate</li>
</ul>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/m3.js"></script>

CSS

canvas { border: 1px solid black; }

JavaScript

'use strict';

/* global document, window, twgl, m3 */

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

const vs = `
attribute vec2 a_position;
uniform mat3 u_matrix;
void main() {
  gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
}
`;

const fs = `
precision mediump float;
uniform vec4 u_color;
void main() {
  gl_FragColor = u_color;
}
`;

// compiles shaders, links program, looks up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// calls gl.createBuffer, gl.bindBuffer, gl.bufferData
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
  a_position: {
    numComponents: 2,
    data: [
       0,  0, // 0----1
      40,  0, // |    |
      40, 10, // | 3--2
      10, 10, // | |
      10, 20, // | 4-5
      30, 20, // |   |
      30, 30, // | 7-6
      10, 30, // | |
      10, 50, // 9-8
       0, 50,
    ],
  },
  indices: [
    0, 1, 2,
    0, 2, 3,
    0, 3, 8,
    0, 8, 9,
    4, 5, 6,
    4, 6, 7,
  ],
});

const sphereVerts = twgl.primitives.createSphereVertices(1, 24, 12);
const sphereBufferInfo = twgl.createBufferInfoFromArrays(gl, {
  a_position: sphereVerts.position,
  indices: sphereVerts.indices,
});

const camera = {
  x: 0,
  y: 0,
  rotation: 0,
  zoom: 1,
};

const scene = [
  { x:  20, y:  20, rotation: 0,       scale: 1,   color: [1,   0, 0, 1], bufferInfo},
  { x: 100, y:  50, rotation: Math.PI, scale: 0.5, color: [0, 0.5, 0, 1], bufferInfo},
  { x: 100, y:  50, rotation: 0,       scale: 2,   color: [0,   0, 1, 1], bufferInfo},
  { x: 200, y: 100, rotation: 0.7,     scale: 1,   color: [1,   0, 1, 1], bufferInfo},
];

let viewProjectionMat;

function makeCameraMatrix() {
  const zoomScale = 1 / camera.zoom;
  let cameraMat = m3.identity();
  cameraMat = m3.translate(cameraMat, camera.x, camera.y);
  cameraMat = m3.rotate(cameraMat, camera.rotation);
  cameraMat = m3.scale(cameraMat, zoomScale, zoomScale);
  return cameraMat;
}

function updateViewProjection() {
  //...