<div class="controls">
  <button class="resize-handle">
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
      <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 19.5l-15-15m0 0v11.25m0-11.25h11.25" />
    </svg>

  </button>
  <button class="resize-handle">
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
      <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 4.5l15 15m0 0V8.25m0 11.25H8.25" />
    </svg>
  </button>
</div>
<div class="container">
  <img src="https://picsum.photos/1080/1080?random=12" alt="">
</div>
@layer demo {
  .container {
    position: absolute;
    inset: anchor(--handle-1 top) anchor(--handle-2 right)
      anchor(--handle-2 bottom) anchor(--handle-1 left);
  }

  img {
    height: 100%;
    width: 100%;
    object-fit: cover;
    pointer-events: none;
    user-select: none;
  }
  .controls {
    position: relative;
  }
  .resize-handle {
    height: 48px;
    aspect-ratio: 1;
    background: hsl(0 0% 100% / 0.75);
    position: absolute;
    z-index: 2;
    opacity: 0.5;
    transition: opacity 0.2s, background 0.2s;
  }
  .resize-handle:is(:hover, :active) {
    opacity: 1;
    background: hsl(0 0% 100% / 1);
  }
  .resize-handle:first-of-type {
    anchor-name: --handle-1;
    left: calc(50% - 100px);
    top: calc(50% - 100px);
  }
  .resize-handle:last-of-type {
    anchor-name: --handle-2;
    left: calc(50% + 100px);
    top: calc(50% + 100px);
  }
  svg {
    stroke-width: 3;
  }
}

* {
  box-sizing: border-box;
}

:root {
}

:where(html) {
  background-color: transparent;
  color-scheme: none;
}

body {
  min-height: 100vh;
  height: 100vh;
  overflow: hidden;
  display: grid;
  place-items: center;
  position: relative;
  background-color: var(--surface-2);
}
import gsap from "https://cdn.skypack.dev/gsap@3.11.4";
import Draggable from "https://cdn.skypack.dev/gsap@3.11.4/Draggable";

gsap.registerPlugin(Draggable);

const IMG = document.querySelector("img");
const HANDLES = document.querySelectorAll(".resize-handle");

const createHandle = (el, limiter) => {
  Draggable.create(el, {
    type: "left, top",
    allowContextMenu: true,
    onDragStart: limiter
  });
};
createHandle(HANDLES[0], function () {
  const BOUNDS = IMG.getBoundingClientRect();
  this.applyBounds({
    top: window.innerHeight * -0.5,
    left: window.innerWidth * -0.5,
    width:
      window.innerWidth * 0.5 + BOUNDS.right - window.innerWidth * 0.5 - 48,
    height:
      window.innerHeight * 0.5 + BOUNDS.bottom - window.innerHeight * 0.5 - 48
  });
});
createHandle(HANDLES[1], function () {
  const BOUNDS = IMG.getBoundingClientRect();
  this.applyBounds({
    top: 0 - (window.innerHeight * 0.5 - BOUNDS.top) + 48,
    left: 0 - (window.innerWidth * 0.5 - BOUNDS.left) + 48,
    width: window.innerWidth * 0.5 + (window.innerWidth * 0.5 - BOUNDS.left),
    height: window.innerHeight * 0.5 + (window.innerHeight * 0.5 - BOUNDS.top)
  });
});

External CSS

  1. https://codepen.io/web-dot-dev/pen/XWqWYgB.css
  2. https://codepen.io/web-dot-dev/pen/ZExZWBQ.css

External JavaScript

  1. https://codepen.io/web-dot-dev/pen/XWqWYgB.js
  2. https://codepen.io/web-dot-dev/pen/ZExZWBQ.js