October 27, 2024
Chicago 12, Melborne City, USA
HTML

Creating Pixel Stars Using Canvas


I’m looking for a person who will help refine the code that displays the stars.
I recently saw the factorio update and their website – https://factorio.com/galaxy. I was interested in the concept of stars, namely how they are arranged. I tried to make similar stars myself, but it didn’t work out. I tried to use ChatGPT, but it doesn’t give out the right or poignant questions.
galaxy
Here is one example, as we see in the middle we have a square and the star itself is made using a square and highlights. I didn’t have any problems with the first one for rendering, but it doesn’t work very well with the second one. It is not entirely clear how to draw further.

import { useEffect, useRef, useState } from "react";
import Head from "next/head";
import styles from './star.module.css'; 
import starImage5 from '../public/star/5.png'; 
import starImage4 from '../public/star/4.png'; 
import starImage3 from '../public/star/3.png';
import starImage2 from '../public/star/2.png'; 
import starImage1 from '../public/star/1.png'; 


const generateRandomCoordinates = (numStars, offsetRange) => {
  const coordinates = [];
  const positions = [];

  for (let i = 0; i < numStars; i++) {
    const x = Math.random() * (offsetRange * 2) - offsetRange; 
    const y = Math.random() * (offsetRange * 2) - offsetRange; 

    coordinates.push(x);
    positions.push(y);
  }

  return { coordinates, positions };
};

export default function Home() {
  const canvasRef = useRef(null);
  const [offset, setOffset] = useState({ x: 0, y: 0 }); 
  const [isDragging, setIsDragging] = useState(false); 
  const [lastMousePosition, setLastMousePosition] = useState({ x: 0, y: 0 });
  
  const starData = {
    colors: [...Array(100).keys()].map(() => Math.floor(Math.random() * 16777215)),
    ids: [...Array(100).keys()].map(() => Math.floor(Math.random() * 1000000000)), 
    names: [...Array(100).keys()].map((_, i) => `Star ${i + 1}`), 
  };
  
  const { coordinates, positions } = generateRandomCoordinates(100, 200); 

  useEffect(() => {
    const images = [starImage1, starImage2, starImage3, starImage4, starImage5].map(src => {
      const img = new Image();
      img.src = src.src;
      return img;
    });

    Promise.all(images.map(img => new Promise(resolve => img.onload = resolve)))
      .then(() => {
        const canvas = canvasRef.current;
        const ctx = canvas.getContext("2d");

        canvas.width = window.innerWidth; 
        canvas.height = window.innerHeight; 

        ctx.imageSmoothingEnabled = false; 

        const drawStars = () => {
          ctx.clearRect(0, 0, canvas.width, canvas.height);

          const numStars = Math.min(starData.colors.length, coordinates.length, positions.length);

     
          for (let index = 0; index < numStars; index++) {
            const x = coordinates[index]; 
            const y = positions[index];
            const color = `#${starData.colors[index].toString(16).padStart(6, '0')}`; 

            const starX = x + offset.x + canvas.width / 2;
            const starY = y + offset.y + canvas.height / 2; 
            
            const randomStarIndex = Math.floor(Math.random() * images.length);
            const starImage = images[randomStarIndex];

            const tempCanvas = document.createElement('canvas');
            const tempCtx = tempCanvas.getContext('2d');

            tempCanvas.width = starImage.width;
            tempCanvas.height = starImage.height;
            
            tempCtx.drawImage(starImage, 0, 0);

            tempCtx.globalCompositeOperation = 'source-in';
            tempCtx.fillStyle = color;
            tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
            
            const starSize = 16; 
            ctx.drawImage(tempCanvas, starX - starSize / 2, starY - starSize / 2, starSize, starSize);
          }
        };
        drawStars();

        const handleMouseMove = (event) => {
          if (isDragging) {
            const dx = event.clientX - lastMousePosition.x;
            const dy = event.clientY - lastMousePosition.y;
            setOffset(prev => ({
              x: prev.x + dx,
              y: prev.y + dy,
            }));
            setLastMousePosition({ x: event.clientX, y: event.clientY });
            drawStars(); // Переотрисовываем звезды
          }
        };

        const handleMouseDown = (event) => {
          setIsDragging(true);
          setLastMousePosition({ x: event.clientX, y: event.clientY });
        };

        const handleMouseUp = () => {
          setIsDragging(false);
        };

        canvas.addEventListener("mousemove", handleMouseMove);
        canvas.addEventListener("mousedown", handleMouseDown);
        canvas.addEventListener("mouseup", handleMouseUp);
        canvas.addEventListener("mouseleave", handleMouseUp);

        return () => {
          canvas.removeEventListener("mousemove", handleMouseMove);
          canvas.removeEventListener("mousedown", handleMouseDown);
          canvas.removeEventListener("mouseup", handleMouseUp);
          canvas.removeEventListener("mouseleave", handleMouseUp);
        };
      });
  }, [offset.x, offset.y]);
  return (
    <>
      <div className={styles.imageContainer}>
        <canvas ref={canvasRef} className={styles.pixelCanvas} />
      </div>
    </>
  );
}

The developers themselves did it using Three.js

I tried to create highlights by applying layers on top, it didn’t work out very well

entersource code of the star
I also noticed the source code of the star on the site, through the developer console (Network)



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video