heartwood every commit a ring
3.0 KB raw
import React, { useEffect, useRef } from "react";
import styles from "@styles/components/canvas.module.css";
import PropTypes from "prop-types";

const Constellations = ({ options }) => {
  const isActive = options.isActive !== undefined ? options.isActive : true;
  const canvas = useRef(null);

  useEffect(() => {
    const cvs = canvas.current;

    cvs.width = cvs.offsetWidth;
    cvs.height = cvs.offsetHeight;

    const resizeCanvas = () => {
      cvs.width = cvs.offsetWidth;
      cvs.height = cvs.offsetHeight;
    };
    window.addEventListener("resize", resizeCanvas);

    return () => {
      window.removeEventListener("resize", resizeCanvas);
    };
  }, []);

  useEffect(() => {
    const cvs = canvas.current;
    const ctx = cvs.getContext("2d");

    let stars = [];
    let numStars = 0;
    const maxNumStars = options.numStars;
    while (numStars < maxNumStars) {
      const randomPoint = [
        cvs.width * Math.random(),
        cvs.height * Math.random(),
      ];
      stars.push({
        loc: randomPoint,
        dir: [Math.random() > 0.5 ? "+" : "-", Math.random() > 0.5 ? "+" : "-"],
      });
      numStars++;
    }

    let starsAnimationFrame = null;
    const starDistance = 150;
    const drawStars = () => {
      ctx.clearRect(0, 0, cvs.width, cvs.height);

      stars.map((star) => {
        ctx.beginPath();
        ctx.arc(...star.loc, 2, 0, 2 * Math.PI);
        ctx.fillStyle = "rgb(255, 255, 255)";
        ctx.fill();
        ctx.closePath();
        const closeStars = stars.filter((closeStar) => {
          return (
            Math.hypot(
              star.loc[0] - closeStar.loc[0],
              star.loc[1] - closeStar.loc[1]
            ) < starDistance
          );
        });
        closeStars.map((closeStar) => {
          ctx.beginPath();
          ctx.moveTo(...star.loc);
          ctx.lineTo(...closeStar.loc);
          ctx.strokeStyle = `rgba(255, 255, 255, ${
            (starDistance -
              Math.hypot(
                star.loc[0] - closeStar.loc[0],
                star.loc[1] - closeStar.loc[1]
              )) /
            starDistance
          })`;
          ctx.stroke();
          ctx.closePath();
        });
      });

      stars = stars.map((star) => {
        if (star.loc[0] < 0) star.dir[0] = "+";
        else if (star.loc[0] > cvs.width) star.dir[0] = "-";
        if (star.loc[1] < 0) star.dir[1] = "+";
        else if (star.loc[1] > cvs.height) star.dir[1] = "-";

        star.loc[0] += parseFloat(`${star.dir[0]}0.5`);
        star.loc[1] += parseFloat(`${star.dir[1]}0.5`);

        return star;
      });

      if (isActive) {
        starsAnimationFrame = window.requestAnimationFrame(drawStars);
      }
    };

    if (isActive) {
      starsAnimationFrame = window.requestAnimationFrame(drawStars);
    }

    return () => {
      window.cancelAnimationFrame(starsAnimationFrame);
    };
  }, [isActive]);

  return <canvas ref={canvas} className={styles.canvas} />;
};

Constellations.propTypes = {
  options: PropTypes.object,
};

export default Constellations;