repos

isaacbythewood.com-nextjs

mirror archived upstream

Animation-heavy personal portfolio on Next.js: a custom cursor, a page-transition loader, and canvas backgrounds.

canvas-animationscss-modulesdark-themehandcodednextjspersonal-websiteportfolioreact

3.7 KB · 140 lines · JavaScript Raw History
  1import React, { useEffect, useRef } from "react";
  2import styles from "@styles/components/canvas.module.css";
  3
  4const Synthwave = () => {
  5  const canvas = useRef(null);
  6
  7  useEffect(() => {
  8    const cvs = canvas.current;
  9
 10    cvs.width = cvs.offsetWidth;
 11    cvs.height = cvs.offsetHeight;
 12
 13    const resizeCanvas = () => {
 14      cvs.width = cvs.offsetWidth;
 15      cvs.height = cvs.offsetHeight;
 16    };
 17    window.addEventListener("resize", resizeCanvas);
 18
 19    return () => {
 20      window.removeEventListener("resize", resizeCanvas);
 21    };
 22  }, []);
 23
 24  useEffect(() => {
 25    const cvs = canvas.current;
 26    const ctx = cvs.getContext("2d");
 27
 28    const drawSynth = () => {
 29      let stars = [];
 30      let numStars = 0;
 31      const maxNumStars = 250;
 32      ctx.filter = "blur(1px)";
 33      while (numStars < maxNumStars) {
 34        const randomPoint = [
 35          cvs.width * Math.random(),
 36          cvs.height * Math.random(),
 37        ];
 38        stars.push(randomPoint);
 39        numStars++;
 40      }
 41
 42      let grd = ctx.createLinearGradient(0, 0, 0, cvs.height);
 43      grd.addColorStop(0, "#110f33");
 44      grd.addColorStop(1, "#040103");
 45      ctx.fillStyle = grd;
 46      ctx.fillRect(0, 0, cvs.width, cvs.height);
 47
 48      stars.forEach((star) => {
 49        ctx.beginPath();
 50        ctx.arc(...star, 2 * Math.random(), 0, 2 * Math.PI);
 51        ctx.fillStyle = `rgba(255, 255, 255, ${Math.random()})`;
 52        ctx.fill();
 53        ctx.closePath();
 54      });
 55      ctx.filter = "none";
 56
 57      let currentLine = 0;
 58      let maxLines = 15;
 59      let start = cvs.height;
 60      let offset = 30;
 61      let quotient = 0.9;
 62      ctx.strokeStyle = "#dd299c";
 63      while (currentLine < maxLines) {
 64        ctx.globalAlpha = 1 - currentLine / maxLines;
 65        start = start - offset;
 66        ctx.beginPath();
 67        ctx.moveTo(0, start);
 68        ctx.lineTo(cvs.width, start);
 69        ctx.stroke();
 70        offset = offset * quotient;
 71        currentLine++;
 72      }
 73      ctx.globalAlpha = 1;
 74
 75      const end = start;
 76      quotient = 0.85;
 77      maxLines = 30;
 78      currentLine = 0;
 79      start = cvs.width / 2;
 80      offset = 80;
 81      let standardStart = start;
 82      const standardOffset = offset;
 83      while (currentLine < maxLines) {
 84        ctx.beginPath();
 85        ctx.moveTo(standardStart, cvs.height);
 86        ctx.lineTo(start, end);
 87        grd = ctx.createLinearGradient(0, cvs.height, 0, end);
 88        grd.addColorStop(0, `rgba(221, 41, 156, 1)`);
 89        grd.addColorStop(1, `rgba(221, 41, 156, 0)`);
 90        ctx.strokeStyle = grd;
 91        ctx.stroke();
 92        offset = offset * quotient;
 93        start = start - offset;
 94        standardStart = standardStart - standardOffset;
 95        currentLine++;
 96      }
 97      currentLine = 0;
 98      start = cvs.width / 2;
 99      standardStart = start;
100      offset = 80;
101      while (currentLine < maxLines) {
102        ctx.beginPath();
103        ctx.moveTo(standardStart, cvs.height);
104        ctx.lineTo(start, end);
105        ctx.stroke();
106        offset = offset * quotient;
107        start = start + offset;
108        standardStart = standardStart + standardOffset;
109        currentLine++;
110      }
111
112      ctx.beginPath();
113      ctx.arc(cvs.width / 2, cvs.height / 2, 100, 0, 2 * Math.PI);
114      grd = ctx.createLinearGradient(
115        0,
116        cvs.height / 2 - 50,
117        0,
118        cvs.height / 2 + 50
119      );
120      grd.addColorStop(0, "#ffd319");
121      grd.addColorStop(1, "#ff2975");
122      ctx.fillStyle = grd;
123      ctx.fill();
124      ctx.closePath();
125    };
126
127    drawSynth();
128
129    window.addEventListener("resize", drawSynth);
130
131    return () => {
132      window.removeEventListener("resize", drawSynth);
133    };
134  }, []);
135
136  return <canvas ref={canvas} className={styles.canvas} />;
137};
138
139export default Synthwave;