Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 920
ayakhaled Avatar asked Nov 08 '22 00:11

ayakhaled


1 Answers

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

like image 118
maxgallo Avatar answered Nov 14 '22 20:11

maxgallo