How to create funky circle using svg path?

Viewed 62

I am trying to replicate what Patrick Heng (patrickheng.com) has done on his website. I want to create this funky effect when you hover over fixed circles. The closest I have come to is by using a path to create circle then change path values but that produce a very bad result and uncontrolled.

<path 
      d="
        M 25, 50
        a 25,25 0 1,1 50,0
        a 25,25 0 1,1 -50,0
        " />

we can change the values of M and a.

1 Answers

Here is something similar with a Quadratic Bezier path:

const angleFromCenter = (cx, cy, mx, my) => {
  const mAngle = Math.atan2(mx - cx, - my + cy);
  return mAngle > 0 ? mAngle : (Math.PI * 2 + mAngle);
};

const circlePath = (cx, cy, r, mx, my) => {
  const dist = Math.hypot(mx - cx, my - cy);
  let path = '';
  if (dist > r) {
    const angle = angleFromCenter(cx, cy, mx, my);
    const delta = Math.acos(r / dist); 
  
    const p0x = cx + r * Math.sin(angle - delta);
    const p0y = cy - r * Math.cos(angle - delta);
    const p2x = cx + r * Math.sin(angle + delta);
    const p2y = cy - r * Math.cos(angle + delta);
    const anchorR = dist > r * 2 ? r * 4 - dist : dist;
    const p1x = cx + anchorR * Math.sin(angle);
    const p1y = cy - anchorR * Math.cos(angle);
    path = `M ${p0x},${p0y} Q ${p1x},${p1y} ${p2x},${p2y}`;
  }
  return path + `M ${cx},${cy - r} A ${r},${r} 1 1 1 ${cx},${cy + r} A ${r},${r} 1 1 1 ${cx},${cy - r} Z`;
}

const svg = d3.select('svg')
const path = circlePath(100, 100, 50, 100, 100);
const circle = svg.append('path')
  .attr('d', path)
  .style('fill', 'blue');

svg.on('mousemove', (e) => {
  const d = circlePath(100, 100, 50, e.offsetX, e.offsetY);
  circle.attr('d', d);
});
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

<svg width="200" height="200">
</svg>

Related