DEV Community

Cover image for React interview question - Detect overlapping circles
Prashant Yadav
Prashant Yadav

Posted on • Originally published at learnersbucket.com

React interview question - Detect overlapping circles


I have written an Ebook that has 100 JavaScript, 20 React, & 2 Frontend system design questions. Get a free preview or grab your copy for $10 or ₹599.


I have been collecting frontend interview questions since the last 4 years and have been writing about it on my blog learnersbucket.com

Lately I have came across this interesting question that has been asked in an Uber interview and I tried to solve it.

Question

Draw circles on the screen on the click and whenever two circles overlap change the color of the second circle.

  • When a user clicks anywhere on the DOM, create a circle around it of a radius of 100px with red background.
  • If two or more circles overlap, change the background of the later circle.

Approach

Let us understand the logic of creating the circle first.

As we have to create the circle with a radius of 100px (200px diameter), rather than generating the circle on the click, we will store the coordinates of the position where the circle should be generated when the user clicks and then create circles out of these co-ordinates.

As all the circles will be in absolute position so that they can be freely placed on the screen, we will calculate the top, bottom, left, and right positions that will help in placement as well as detecting if two circles are colliding.

Get the clientX and clientY coordinates when the user clicks and align the circle around with a simple calculation so that it is placed in the center. Also before updating the state check if the current circle is overlapping with the existing circles then update the background color of the current.

// helper function to gather configuration when user clicks
const draw = (e) => {
  // get the co-ordinates where user has clicked
  const { clientX, clientY } = e;

  // decide the position where circle will be created and placed
  // as the circle is of 100 radius (200 diameter), we are subtracting the values
  // so that circle is placed in the center
  // set the initial background color to red
  setElementsCoordinates((prevState) => {
    const current = {
      top: clientY - 100,
      left: clientX - 100,
      right: clientX - 100 + 200,
      bottom: clientY - 100 + 200,
      background: "red",
    };

    // before making the new entry
    // check with the exisitng circles
    for (let i = 0; i < prevState.length; i++) {
      // if the current circle is colliding with any existing
      // update the background color of the current
      if (elementsOverlap(current, prevState[i])) {
        current.background = getRandomColor();
        break;
      }
    }

    return [...prevState, current];
  });
};
Enter fullscreen mode Exit fullscreen mode

Assign the event listener and draw the circle on the click.

// assign the click event
  useEffect(() => {
    document.addEventListener("click", draw);
    return () => {
      document.removeEventListener("click", draw);
    };
  }, []);
Enter fullscreen mode Exit fullscreen mode

Helper function to detect collision and generate random colors.

// helper function to generate a random color
const getRandomColor = () => {
  const letters = "0123456789ABCDEF";
  let color = "#";
  for (let i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
};

// helper function to detect if two elements are overlapping
const elementsOverlap = (rect1, rect2) => {
  const collide = !(
    rect1.top > rect2.bottom ||
    rect1.right < rect2.left ||
    rect1.bottom < rect2.top ||
    rect1.left > rect2.right
  );

  return collide;
};
Enter fullscreen mode Exit fullscreen mode

Generate the circles from the coordinates which we have stored after the user has clicked. As the detection is done before making entry into the state, the circles are generated with different colors if they will be colliding.

// circle element
const Circle = ({ top, left, background }) => {
  return (
    <div
      style={{
        position: "absolute",
        width: "200px",
        height: "200px",
        borderRadius: "50%",
        opacity: "0.5",
        background,
        top,
        left,
      }}
    ></div>
  );
};

return (
  <div>
    {/* render each circle */}
    {elementsCoordinates.map((e) => (
      <Circle {...e} key={e.top + e.left + e.right} />
    ))}
  </div>
);
Enter fullscreen mode Exit fullscreen mode

Putting everything together.

import { useEffect, useState } from "react";

// helper function to generate a random color
const getRandomColor = () => {
  const letters = "0123456789ABCDEF";
  let color = "#";
  for (let i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
};

// helper function to detect if two elements are overlapping
const elementsOverlap = (rect1, rect2) => {
  const collide = !(
    rect1.top > rect2.bottom ||
    rect1.right < rect2.left ||
    rect1.bottom < rect2.top ||
    rect1.left > rect2.right
  );

  return collide;
};

const Example = () => {
  // store the configuration of each circle
  const [elementsCoordinates, setElementsCoordinates] = useState([]);

  // helper function to gather configuration when user clicks
  const draw = (e) => {
    // get the co-ordinates where user has clicked
    const { clientX, clientY } = e;

    // decide the position where circle will be created and placed
    // as the circle is of 100 radius (200 diameter), we are subtracting the values
    // so that circle is placed in the center
    // set the initial background color to red
    setElementsCoordinates((prevState) => {
      const current = {
        top: clientY - 100,
        left: clientX - 100,
        right: clientX - 100 + 200,
        bottom: clientY - 100 + 200,
        background: "red",
      };

      // before making the new entry
      // check with the exisitng circles
      for (let i = 0; i < prevState.length; i++) {
        // if the current circle is colliding with any existing
        // update the background color of the current
        if (elementsOverlap(current, prevState[i])) {
          current.background = getRandomColor();
          break;
        }
      }

      return [...prevState, current];
    });
  };

  // assign the click event
  useEffect(() => {
    document.addEventListener("click", draw);
    return () => {
      document.removeEventListener("click", draw);
    };
  }, []);

  // circle element
  const Circle = ({ top, left, background }) => {
    return (
      <div
        style={{
          position: "absolute",
          width: "200px",
          height: "200px",
          borderRadius: "50%",
          opacity: "0.5",
          background,
          top,
          left,
        }}
      ></div>
    );
  };

  return (
    <div>
      {/* render each circle */}
      {elementsCoordinates.map((e) => (
        <Circle {...e} key={e.top + e.left + e.right} />
      ))}
    </div>
  );
};

export default Example;
Enter fullscreen mode Exit fullscreen mode

Output

Detect overlapping circles

Top comments (0)