<svg id="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 400">
  <path id="path" fill="#673ab7" d=""/>
</svg>
svg {
  width: 300px;
  outline: 1px dashed gray;
}
const svgEl = document.querySelector("#svg");
const pathEl = document.querySelector("#path");
const w = 300;
const h = 400;
const SVGRect = svgEl.getBoundingClientRect();
const maxDist = Math.hypot(w, h);

const corners = {
  tl: { x: 0, y: 0, sx: 0, sy: 0, r: 0, minr: 0, maxr: 50, shift: 50 },
  tr: { x: w, y: 0, sx: w, sy: 0, r: 0, minr: 0, maxr: 50, shift: 50 },
  br: { x: w, y: h, sx: w, sy: h, r: 0, minr: 0, maxr: 50, shift: 50 },
  bl: { x: 0, y: h, sx: 0, sy: h, r: 0, minr: 0, maxr: 50, shift: 50 }
};

const sides = {
  t: { s: 0, mins: 0, maxs: 20 },
  r: { s: 0, mins: 0, maxs: 20 },
  b: { s: 0, mins: 0, maxs: 20 },
  l: { s: 0, mins: 0, maxs: 20 }
};

function updateShape(corners, sides) {
  return [
    {
      type: "M",
      x: corners.tl.x,
      y: corners.tl.y + corners.tl.r
    },
    {
      type: "A",
      rx: corners.tl.r,
      ry: corners.tl.r,
      x: corners.tl.x + corners.tl.r,
      y: corners.tl.y
    },
    {
      type: "Q",
      x1: (corners.tl.x + corners.tr.x) / 2,
      y1: (corners.tl.y + corners.tr.y) / 2 + sides.t.s,
      x: corners.tr.x - corners.tr.r,
      y: corners.tr.y
    },
    {
      type: "A",
      rx: corners.tr.r,
      ry: corners.tr.r,
      x: corners.tr.x,
      y: corners.tr.y + corners.tr.r
    },
    {
      type: "Q",
      x1: (corners.tr.x + corners.br.x) / 2 - sides.r.s,
      y1: (corners.tr.y + corners.br.y) / 2,
      x: corners.br.x,
      y: corners.br.y - corners.br.r
    },
    {
      type: "A",
      rx: corners.br.r,
      ry: corners.br.r,
      x: corners.br.x - corners.br.r,
      y: corners.br.y
    },
    {
      type: "Q",
      x1: (corners.bl.x + corners.br.x) / 2,
      y1: (corners.bl.y + corners.br.y) / 2 - sides.b.s,
      x: corners.bl.x + corners.bl.r,
      y: corners.bl.y
    },
    {
      type: "A",
      rx: corners.bl.r,
      ry: corners.bl.r,
      x: corners.bl.x,
      y: corners.bl.y - corners.bl.r
    },
    {
      type: "Q",
      x1: (corners.bl.x + corners.tl.x) / 2 + sides.l.s,
      y1: (corners.bl.y + corners.tl.y) / 2,
      x: corners.tl.x,
      y: corners.tl.y + corners.tl.r
    }
  ];
}

function pointsToD(points) {
  let d = "";
  for (let p of points) {
    switch (p.type) {
      case "M":
        d += `M ${p.x} ${p.y}`;
        break;
      case "A":
        d += `, A ${p.rx} ${p.ry} 0 0 1 ${p.x} ${p.y}`;
        break;
      case "Q":
        d += `, Q ${p.x1} ${p.y1} ${p.x} ${p.y}`;
        break;
    }
  }
  return d;
}

svgEl.addEventListener("mousemove", onMouseMove);

onMouseMove({
  clientX: SVGRect.x + SVGRect.width / 2,
  clientY: SVGRect.y + SVGRect.height / 2
});

function onMouseMove(event) {
  let { clientX: mx, clientY: my } = event;
  let x = mx - SVGRect.x;
  let y = my - SVGRect.y;

  let dist, vec, radius;
  for (let cor of Object.values(corners)) {
    dist = 1 - Math.hypot(cor.sx - x, cor.sy - y) / maxDist;
    dist = clamp(dist, 0, 1);

    vec = normalize(x - cor.sx, y - cor.sy);
    cor.x = cor.sx + vec.x * cor.shift * dist;
    cor.y = cor.sy + vec.y * cor.shift * dist;

    cor.r = lerp(cor.minr, cor.maxr, dist);
  }

  sides.t.s = sides.t.mins + sides.t.maxs * (1 - y / h);
  sides.b.s = sides.b.mins + (sides.b.maxs * y) / h;
  sides.l.s = sides.l.mins + sides.l.maxs * (1 - x / w);
  sides.r.s = sides.r.mins + (sides.r.maxs * x) / w;

  const points = updateShape(corners, sides);
  pathEl.setAttribute("d", pointsToD(points));
}

function normalize(x, y) {
  let len = x * x + y * y;
  if (len > 0) {
    len = 1 / Math.sqrt(len);
  }
  x = x * len;
  y = y * len;
  return { x, y };
}

function clamp(number, min, max) {
  return Math.max(min, Math.min(number, max));
}

function lerp(v0, v1, t) {
  return v0 * (1 - t) + v1 * t;
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.