ef-pan-zoom

Pan and zoom container. Drag or swipe to pan, pinch to zoom.

Wraps slotted content in a translate + scale transform. Drag or swipe to pan, pinch to zoom. Provides panZoomTransformContext to descendants so overlay elements like ef-overlay-layer stay in sync with the transform.

<ef-pan-zoom id="pz" class="w-[1920px] h-[1080px] pz-stage">
  <div class="pz-card">
    <p class="pz-title">Drag to pan</p>
    <p class="pz-sub">Pinch to zoom</p>
  </div>
</ef-pan-zoom>

<div class="pz-toolbar">
  <button class="pz-btn" data-ef-target="pz" data-ef-method="reset">Reset</button>
  <button class="pz-btn" data-ef-target="pz" data-ef-method="fitToContent">Fit to content</button>
</div>

<style>
  .pz-stage {
    background: #0f172a;
    cursor: grab;
    display: block;
  }
  .pz-stage:active { cursor: grabbing; }
  .pz-card {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 600px;
    height: 300px;
    background: #1e293b;
    border: 2px solid rgba(255,255,255,0.12);
    border-radius: 16px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    user-select: none;
  }
  .pz-title {
    color: white;
    font-size: 48px;
    font-weight: 700;
    margin: 0;
  }
  .pz-sub {
    color: rgba(255,255,255,0.4);
    font-size: 28px;
    margin: 0;
  }
  .pz-toolbar {
    display: flex;
    gap: 8px;
    padding: 10px 12px;
    background: #0f172a;
    border-top: 1px solid rgba(255,255,255,0.08);
  }
  .pz-btn {
    padding: 6px 14px;
    background: #1e293b;
    color: rgba(255,255,255,0.7);
    border: 1px solid rgba(255,255,255,0.12);
    border-radius: 6px;
    font-size: 13px;
    cursor: pointer;
  }
  .pz-btn:hover {
    background: #334155;
    color: white;
  }
</style>

The transform-changed event fires with { x, y, scale } on every pan or zoom:

document.querySelector('ef-pan-zoom').addEventListener('transform-changed', (e) => {
  console.log(e.detail); // { x: -120, y: -40, scale: 1.5 }
});

Public methods:

MethodDescription
reset()Returns to x=0, y=0, scale=1
fitToContent(padding?)Scales and centers content to fill the container
screenToCanvas(sx, sy)Converts screen coordinates to canvas coordinates
canvasToScreen(cx, cy)Converts canvas coordinates to screen coordinates