Drawing Interactive Polygons Using Canvas and SVG in React

0

Web applications that allow users to mark points at specific coordinates and connect those points to form polygons are highly useful. Especially, allowing users to freely draw shapes through interaction can significantly enhance the user experience. Today, we’ll explore how to implement this feature in React using HTML5 `<canvas>` and SVG. Both methods have their own advantages, so you can choose the appropriate one based on your purpose.

Drawing Free Polygons Using HTML5 `<canvas>`

First, let’s look at how to store coordinates and draw polygons by using the click event in the HTML5 `<canvas>` element. `<canvas>` is well-suited for pixel-based graphics operations and is commonly used when real-time user interaction is crucial.

Example Code

import React, { useRef, useState } from 'react';

const CanvasPolygonDrawer = () => {
  const canvasRef = useRef(null);
  const [points, setPoints] = useState([]);

  // Add coordinates on canvas click
  const handleCanvasClick = (e) => {
    const canvas = canvasRef.current;
    const rect = canvas.getBoundingClientRect();
    
    // Convert the mouse click position relative to the canvas
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    setPoints((prevPoints) => [...prevPoints, { x, y }]);
  };

  // Draw points and lines
  const drawPolygon = () => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');

    // Clear the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    if (points.length > 1) {
      ctx.beginPath();
      ctx.moveTo(points[0].x, points[0].y);

      // Connect the points to draw lines
      points.forEach((point, index) => {
        if (index > 0) {
          ctx.lineTo(point.x, point.y);
        }
      });

      // Connect the first and last points
      ctx.closePath();
      ctx.stroke();
    }

    // Mark each point with a dot
    points.forEach((point) => {
      ctx.beginPath();
      ctx.arc(point.x, point.y, 3, 0, 2 * Math.PI);
      ctx.fill();
    });
  };

  // Complete the polygon on button click
  const handleComplete = () => {
    drawPolygon();
  };

  return (
    <div>
      <canvas
        ref={canvasRef}
        width={500}
        height={500}
        style={{ border: '1px solid black' }}
        onClick={handleCanvasClick}
      />
      <button onClick={handleComplete}>Complete</button>
    </div>
  );
};

export default CanvasPolygonDrawer;

Key Functionalities

  • Canvas Click: Every time the user clicks on the canvas, the clicked coordinates are saved in an array.
  • Drawing Points and Lines: A point is marked at each coordinate, and the points are connected to form lines. The first and last points are connected to complete the shape.
  • Completing the Polygon: When the ‘Complete’ button is clicked, the connected points form a finished polygon.

Drawing Free Polygons Using SVG

Now, let’s explore how to implement the same functionality using SVG. SVG is vector-based, so it scales well for zooming in and out, and CSS makes it easy to control styles. It’s ideal for drawing shapes or lines, and it’s useful when higher resolution is desired.

Example Code

import React, { useState } from 'react';

const SvgPolygonDrawer = () => {
  const [points, setPoints] = useState([]);

  // Add coordinates on SVG click
  const handleSvgClick = (e) => {
    const svg = e.target;
    const rect = svg.getBoundingClientRect();

    // Convert the click position relative to the SVG coordinates
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    setPoints((prevPoints) => [...prevPoints, { x, y }]);
  };

  // Complete the polygon by connecting the first and last points
  const handleComplete = () => {
    setPoints((prevPoints) => [...prevPoints, prevPoints[0]]);
  };

  return (
    <div>
      <svg
        width="500"
        height="500"
        style={{ border: '1px solid black' }}
        onClick={handleSvgClick}
      >
        {/* Draw points */}
        {points.map((point, index) => (
          <circle key={index} cx={point.x} cy={point.y} r="4" fill="black" />
        ))}

        {/* Draw lines connecting the points */}
        {points.length > 1 && (
          <polyline
            points={points.map((point) => `${point.x},${point.y}`).join(' ')}
            fill="none"
            stroke="black"
            strokeWidth="2"
          />
        )}
      </svg>
      <button onClick={handleComplete}>Complete</button>
    </div>
  );
};

export default SvgPolygonDrawer;

Key Functionalities

  • SVG Click: Every time the user clicks within the SVG area, the coordinates are saved in an array.
  • Drawing Points and Lines: Points are marked at each coordinate, and the points are connected to form lines.
  • Completing the Polygon: Clicking the ‘Complete’ button finishes the shape by connecting the first and last points automatically.

Conclusion

The ability to mark points at specific coordinates and connect them to draw polygons in React can be implemented using both HTML5 `<canvas>` and SVG. While `<canvas>` is better for pixel-based work and real-time interactions, SVG excels in vector-based operations and style control. Each method has its strengths, so you can choose the one that best fits your project’s characteristics.

References

  • MDN Web Docs, “CanvasRenderingContext2D”
  • MDN Web Docs, “SVG `<polygon>` and `<polyline>`”

Leave a Reply