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

Attributes

AttributeReact propTypeDefaultDescription
auto-fitautoFitbooleanfalseAutomatically fits content to the viewport instead of using explicit `x`/`y`/`scale`.
scalescalenumber1Current zoom scale factor.
xxnumber0Current horizontal pan offset in pixels.
yynumber0Current vertical pan offset in pixels.

CSS Parts

PartDescription
contentThe transformed wrapper. It applies the pan and zoom to the slotted content.

Events

EventDetailDescription
transform-changed{ x: number; y: number; scale: number; }Fires with `{ x, y, scale }` on every pan or zoom.

Methods

MethodDescription
screenToCanvas(screenX: number, screenY: number): { x: number; y: number }Convert screen coordinates, for example `event.clientX` and `event.clientY`, to canvas space.
canvasToScreen(canvasX: number, canvasY: number): { x: number; y: number }Convert canvas-space coordinates to screen space.
reset(): voidResets the pan and zoom to the identity transform (`x: 0, y: 0, scale: 1`).
fitToContent(padding?: number): voidCenters and scales the content to fit the viewport. The `padding` parameter sets the margin, as a number from 0 to 1.