ef-transform-handles

Resize and drag handles for a selected canvas element.

Renders an outlined bounding box with corner/edge resize handles and an optional rotation handle, positioned at bounds. Fires bounds-change on every drag or resize move. pointer-events: none on the host — only the handles themselves capture input.

Consumes panZoomTransformContext to correctly map pointer coordinates when the canvas is panned or zoomed.

<ef-timegroup id="demo" mode="fixed" duration="5s" class="w-[1920px] h-[1080px] bg-slate-900 flex items-center justify-center">
  <style>
    @keyframes pulse {
      0%, 100% { transform: scale(1); }
      50% { transform: scale(1.08); }
    }
    .demo-text { animation: pulse 2.4s ease-in-out infinite; display: inline-block; }
  </style>
  <ef-text class="demo-text text-white text-7xl font-bold">Hello</ef-text>
</ef-timegroup>

<ef-controls target="demo">
  <div class="th-chrome">
    <ef-toggle-play>
      <button slot="play" class="th-btn">▶</button>
      <button slot="pause" class="th-btn">⏸</button>
    </ef-toggle-play>
    <span class="th-hint">ef-transform-handles wraps a canvas element to enable resize and drag</span>
  </div>
</ef-controls>

<style>
  .th-chrome {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 16px;
    border-top: 1px solid rgba(255,255,255,0.08);
  }
  .th-btn { cursor: pointer; padding: 4px 10px; font-size: 13px; }
  .th-hint { font-size: 11px; opacity: 0.4; }
</style>

Typical usage inside an ef-overlay-layer:

<ef-overlay-layer>
  <ef-overlay-item element-id="my-text">
    <ef-transform-handles
      enable-rotation
      enable-resize
      canvas-scale="1"
    ></ef-transform-handles>
  </ef-overlay-item>
</ef-overlay-layer>

Events:

EventDetailDescription
bounds-change{ bounds: TransformBounds }Fires on every drag/resize move
rotation-change{ rotation: number }Fires during rotation drag

Interaction modifiers:

KeyBehavior
ShiftLock aspect ratio during resize
Ctrl / MetaResize from center