Clue Mediator

Connect two rectangles with a line on canvas using React

📅January 19, 2021

In this article, we will show you how to connect two rectangles with a line on canvas using React. In previous articles, we have explained the drawing of rectangles, lines, and writing a text on canvas using React.

Check out the following articles related to the Canvas.

Demo Application

Output - Connect two rectangles with a line on canvas using React - Clue Mediator

Output - Connect two rectangles with a line on canvas using React - Clue Mediator

Steps to connect two rectangles with a line on canvas

  1. Create a react application
  2. Draw draggable rectangles on canvas
  3. Draw a line between two rectangles
  4. Output

1. Create a react application

Let’s create a react application using the `create-react-app` package. Run the following command.

npx create-react-app connect-rectangles-with-line-react

2. Draw draggable rectangles on canvas

Before drawing a line between two rectangles, we will draw draggable rectangles on canvas. Refer the following article to draw draggable rectangles.

Draggable Rectangle on Canvas using React

In this article, we will draw four rectangles for the demonstration.

3. Draw a line between two rectangles

To draw a line between two rectangles, we will use the list of the connectors where we can store the index of the start box and the end box as below.

const boxes = [
  { x: 200, y: 220, w: 80, h: 40 },
  { x: 100, y: 120, w: 70, h: 50 },
  { x: 20, y: 20, w: 60, h: 40 },
  { x: 250, y: 80, w: 55, h: 50 }
];
const connectors = [
  { startBoxIndex: 0, endBoxIndex: 1 },
  { startBoxIndex: 2, endBoxIndex: 3 }
];

Same as the boxes, we will draw the connectors based on the start box index and the end box index. Here we are fetching the center point of the start box and end box to draw the line using the `moveTo` and `lineTo` methods.

// draw rectangles and connectors
const draw = () => {
  ctx.clearRect(0, 0, canvas.current.clientWidth, canvas.current.clientHeight);
  boxes.map(info => drawFillRect(info));
  connectors.map(connector => drawConnector(connector));
}

// draw connector line between two rectangles
const drawConnector = (connector) => {
  var startBoxIndex = boxes[connector.startBoxIndex];
  var endBoxIndex = boxes[connector.endBoxIndex];
  ctx.beginPath();
  ctx.moveTo(startBoxIndex.x + startBoxIndex.w / 2, startBoxIndex.y + startBoxIndex.h / 2);
  ctx.lineTo(endBoxIndex.x + endBoxIndex.w / 2, endBoxIndex.y + endBoxIndex.h / 2);
  ctx.stroke();
}

You can also set the color and width of the line. Refer the following article for more information.

Draw a line on Canvas using React

Let’s combine all code together and see how it looks.

App.js

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

function App() {
  const canvas = useRef();
  let ctx = null;
  const boxes = [
    { x: 200, y: 220, w: 80, h: 40 },
    { x: 100, y: 120, w: 70, h: 50 },
    { x: 20, y: 20, w: 60, h: 40 },
    { x: 250, y: 80, w: 55, h: 50 }
  ];
  let isDown = false;
  let dragTarget = null;
  let startX = null;
  let startY = null;

  const connectors = [
    { startBoxIndex: 0, endBoxIndex: 1 },
    { startBoxIndex: 2, endBoxIndex: 3 }
  ];

  // initialize the canvas context
  useEffect(() => {
    // dynamically assign the width and height to canvas
    const canvasEle = canvas.current;
    canvasEle.width = canvasEle.clientWidth;
    canvasEle.height = canvasEle.clientHeight;

    // get context of the canvas
    ctx = canvasEle.getContext("2d");
  }, []);

  useEffect(() => {
    draw();
  }, []);

  // draw rectangles and connectors
  const draw = () => {
    ctx.clearRect(0, 0, canvas.current.clientWidth, canvas.current.clientHeight);
    boxes.map(info => drawFillRect(info));
    connectors.map(connector => drawConnector(connector));
  }

  // draw connector line between two rectangles
  const drawConnector = (connector) => {
    var startBoxIndex = boxes[connector.startBoxIndex];
    var endBoxIndex = boxes[connector.endBoxIndex];
    ctx.beginPath();
    ctx.moveTo(startBoxIndex.x + startBoxIndex.w / 2, startBoxIndex.y + startBoxIndex.h / 2);
    ctx.lineTo(endBoxIndex.x + endBoxIndex.w / 2, endBoxIndex.y + endBoxIndex.h / 2);
    ctx.stroke();
  }

  // draw rectangle with background
  const drawFillRect = (info, style = {}) => {
    const { x, y, w, h } = info;
    const { backgroundColor = 'black' } = style;

    ctx.beginPath();
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(x, y, w, h);
  }

  // identify the click event in the rectangle
  const hitBox = (x, y) => {
    let isTarget = null;
    for (let i = 0; i < boxes.length; i++) {
      const box = boxes[i];
      if (x >= box.x && x <= box.x + box.w && y >= box.y && y <= box.y + box.h) {
        dragTarget = box;
        isTarget = true;
        break;
      }
    }
    return isTarget;
  }

  const handleMouseDown = e => {
    startX = parseInt(e.nativeEvent.offsetX - canvas.current.clientLeft);
    startY = parseInt(e.nativeEvent.offsetY - canvas.current.clientTop);
    isDown = hitBox(startX, startY);
  }
  const handleMouseMove = e => {
    if (!isDown) return;

    const mouseX = parseInt(e.nativeEvent.offsetX - canvas.current.clientLeft);
    const mouseY = parseInt(e.nativeEvent.offsetY - canvas.current.clientTop);
    const dx = mouseX - startX;
    const dy = mouseY - startY;
    startX = mouseX;
    startY = mouseY;
    dragTarget.x += dx;
    dragTarget.y += dy;
    draw();
  }
  const handleMouseUp = e => {
    dragTarget = null;
    isDown = false;
  }
  const handleMouseOut = e => {
    handleMouseUp(e);
  }

  return (
    <div class="App">
      <h3>Connect two rectangles<br>with a line on canvas - <a href="http://www.cluemediator.com" target="_blank" rel="noopener noreferrer">Clue Mediator</a></h3>
      <canvas onmousedown={handleMouseDown} onmousemove={handleMouseMove} onmouseup={handleMouseUp} onmouseout={handleMouseOut} ref={canvas}></canvas>
    </div>
  );
}

export default App;

4. Output

Run the react application and check the output in the browser.

That’s it for today.
Thank you for reading. Happy Coding..!! 🙂

Demo & Source Code

Github Repository StackBlitz Project