Zooms and pans interactive canvas content

by Rajesh Danabal

HTML

<canvas id="myCanvas" width="600" height="400" style="border:1px solid #ccc;"></canvas>
<button onclick="resetZoomPan()">Reset Zoom/Pan</button>
<script>
  const canvas = document.getElementById('myCanvas');
  const ctx = canvas.getContext('2d');

  let scale = 1;
  let offsetX = 0;
  let offsetY = 0;

  let targetScale = scale;
  let targetOffsetX = offsetX;
  let targetOffsetY = offsetY;

  let animationFrame = null;

  function lerp(a, b, t) {
    return a + (b - a) * t;
  }

  function animateViewportTransition() {
    if (animationFrame) cancelAnimationFrame(animationFrame);

    const duration = 400;
    const startTime = performance.now();

    const startScale = scale;
    const startOffsetX = offsetX;
    const startOffsetY = offsetY;

    function animate() {
      const now = performance.now();
      const t = Math.min(1, (now - startTime) / duration);
      const eased = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;

      scale = lerp(startScale, targetScale, eased);
      offsetX = lerp(startOffsetX, targetOffsetX, eased);
      offsetY = lerp(startOffsetY, targetOffsetY, eased);

      render();

      if (t < 1) {
        animationFrame = requestAnimationFrame(animate);
      } else {
        animationFrame = null;
      }
    }

    animationFrame = requestAnimationFrame(animate);
  }

  function zoomTo(scaleValue, centerX, centerY) {
    const worldX = (centerX - offsetX) / scale;
    const worldY = (centerY - offsetY) / scale;

    targetScale = scaleValue;
    targetOffsetX = centerX - worldX * scaleValue;
    targetOffsetY = centerY - worldY * scaleValue;

    animateViewportTransition();
  }

  function panTo(x, y) {
    targetOffsetX = x;
    targetOffsetY = y;
    targetScale = scale;
    animateViewportTransition();
  }

  function resetZoomPan() {
    targetScale = 1;
    targetOffsetX = 0;
    targetOffsetY = 0;
    animateViewportTransition();
  }

  function render() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
   ...