How to use react to draw rectangles over objects in an image?

10,305

componentDidMount will be called only once from React, after the component is mounted.

Since you want to draw something every time a user clicks on the canvas, you have to do that in a place that it's called at every rendering, like inside the render function itself.

Something like this:

import React, { Component } from 'react'
import { render } from 'react-dom'
import {Row} from 'react-bootstrap';    

class TagImg extends Component {
  constructor(props){
    super(props);
    this.state={
      rects:[[110, 30, 70, 70], [40, 30, 70, 70]]
    };
    this.tagPerson = this.tagPerson.bind(this);
  }

  tagPerson(e){
    var x = e.offsetX,
        y = e.offsetY;

    for(var i=0;i<this.props.rects.length;i++) { // check whether:
        if(x > this.props.rects[i][0]            // mouse x between x and x + width
        && x < this.props.rects[i][0] + this.props.rects[i][2]
        && y > this.props.rects[i][1]            // mouse y between y and y + height
        && y < this.props.rects[i][1] + this.props.rects[i][3]) {
            alert('Rectangle ' + i + ' clicked');
        }
    }
  }

  drawRects() {
      const ctx = this.refs.canvas.getContext('2d');
      ctx.drawImage(myImage, 0, 0, 300, 200);

      ctx.beginPath();
      ctx.strokeStyle="white";
      // for(var i=0;i<this.state.rects.length;i++) {
      //   // ctx.rect(this.state.rects[i][0], // fill at (x, y) with (width, height)
      //   //          this.state.rects[i][1],
      //   //          this.state.rects[i][2],
      //   //          this.state.rects[i][3]);
      //   console.log("helloo");
      // }
      console.log(this.state.rects);

      ctx.stroke();

  }

  componentDidMount() {
    console.log("componentDidMount");
    var myImage = new Image();
    myImage.onload = this.drawRects.bind(this);
    myImage.src = this.props.path;
  }
  render() {
    drawRects();
    return (
      <div>
      <form id="afterUpload" action="" method="post" encType="multipart/form-data">
        <Row id="image_preview" className="row">
          <canvas ref="canvas" onClick={this.tagPerson}/>
        </Row>
      </form>
      </div>
    );
  }
}

export default TagImg;

In the code I'm downloading the image once, in the componentDidMount method. And I'm running the drawRects() method at every rendering, so every time you call a setState you'll have your new rects

Share:
10,305
ayakhaled
Author by

ayakhaled

Updated on July 02, 2022

Comments

  • ayakhaled
    ayakhaled almost 2 years

    I'm using react and HTML5 canvas to draw rectangles over faces on an image, but react life-cycle is limiting me from doing this.

    As I should use refs to reference to the canvas and this have to be done in componentDidMount() and I could not get props or states inside this function.

    Here is the code:

    import React, { Component } from 'react'
    import { render } from 'react-dom'
    import {Row} from 'react-bootstrap';    
    import '../App.css';
    
    class TagImg extends Component {
      constructor(props){
        super(props);
        this.state={
          rects:[[110, 30, 70, 70], [40, 30, 70, 70]],
          ctx : ''
        };
        this.tagPerson = this.tagPerson.bind(this);
      }
    
      tagPerson(e){
        var x = e.offsetX,
            y = e.offsetY;
    
        for(var i=0;i<this.props.rects.length;i++) { // check whether:
            if(x > this.props.rects[i][0]            // mouse x between x and x + width
            && x < this.props.rects[i][0] + this.props.rects[i][2]
            && y > this.props.rects[i][1]            // mouse y between y and y + height
            && y < this.props.rects[i][1] + this.props.rects[i][3]) {
                alert('Rectangle ' + i + ' clicked');
            }
        }
      }
    
      componentDidMount() {
        console.log("componentDidMount");
        const ctx = this.refs.canvas.getContext('2d');
        var myImage = new Image();
    
        myImage.onload = function() {
          var width = myImage.naturalWidth; // this will be 300
          var height = myImage.naturalHeight; // this will be 400
          ctx.drawImage(myImage, 0, 0, 300, 200);
    
          ctx.beginPath();
          ctx.strokeStyle="white";
          // for(var i=0;i<this.state.rects.length;i++) {
          //   // ctx.rect(this.state.rects[i][0], // fill at (x, y) with (width, height)
          //   //          this.state.rects[i][1],
          //   //          this.state.rects[i][2],
          //   //          this.state.rects[i][3]);
          //   console.log("helloo");
          // }
          console.log(this.state.rects);
    
          ctx.stroke();
        }
        myImage.src = this.props.path;
      }
      render() {
        return (
          <div>
          <form id="afterUpload" action="" method="post" encType="multipart/form-data">
            <Row id="image_preview" className="row">
              <canvas ref="canvas" onClick={this.tagPerson}/>
            </Row>
          </form>
          </div>
        );
      }
    }
    
    export default TagImg;
    

    After searching I figured out that I could use componentWillRecieveProps() but did not understand how to use it in my case.