Сохранение фона React Canvas в состоянии препятствует обновлению компонента - PullRequest
0 голосов
/ 31 марта 2020

Я использую ограничивающий прямоугольник модуля NPM, чтобы нарисовать прямоугольники на изображении и определить, когда на них наводят курсор мыши. Большая часть логики c находится внутри самого модуля NPM. Программа полностью работоспособна, когда я импортирую изображение stati c для использования в качестве фона холста. Однако, если я сохраню изображение в состоянии, программа потеряет всю функциональность. Конечная цель - позволить фону холста динамически меняться без перезагрузки приложения.

Я обратился к автору пакета, но, к сожалению, он слишком занят, чтобы помочь. Исходный код довольно маленький, мне просто не хватает знаний React, чтобы полностью это исправить. Мне интересно, есть ли быстрое решение этой проблемы, если я должен просто написать это с нуля, или если по какой-то причине невозможно сохранить фон холста в состоянии.

Из того, что я Можно сказать, что компонент вызывает componentWillReceiveProps каждый раз, когда мышь перемещается, когда она функционирует должным образом. Однако, когда я использую изображение из состояния, движения мыши не обнаруживаются, и componentWillReceiveProps никогда не срабатывает. Это насколько я получил. Любая помощь будет принята с благодарностью! Соответствующий исходный код пакета ниже.

/* global Image */

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import seedrandom from 'seedrandom';

class Boundingbox extends Component {

  constructor(props) {
    super(props);
    this.state = {
      canvasCreated: false,
      hoverIndex: -1,
    };
  }

  componentDidMount() {
    console.log("MOUNTING")
    const ctx = this.canvas.getContext('2d');

    const background = new Image();
    background.src = this.props.options.base64Image ?
      'data:image/png;base64,' + this.props.image
      :
      this.props.image;

    // Make sure the image is loaded first otherwise nothing will draw.
    background.onload = (() => {
      this.canvas.width = background.width;
      this.canvas.height = background.height;

      ctx.drawImage(background, 0, 0);
      this.renderBoxes();

      this.canvas.onmousemove = ((e) => {
        // Get the current mouse position
        console.log("mouse moving")
        const r = this.canvas.getBoundingClientRect();
        const scaleX = this.canvas.width / r.width;
        const scaleY = this.canvas.height / r.height;
        const x = (e.clientX - r.left) * scaleX;
        const y = (e.clientY - r.top) * scaleY;

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

        const selectedBox = { index: -1, dimensions: null };

        if(this.props.boxes &&
           this.props.boxes.length > 0) {

          this.props.boxes.forEach((box, index) => {

            if(!box || typeof box === 'undefined')
              return null;

            const coord = box.coord ? box.coord : box;

            let [bx, by, bw, bh] = [0, 0, 0, 0]

            if (
              typeof coord.xmin !== 'undefined' &&
              typeof coord.xmax !== 'undefined' &&
              typeof coord.ymin !== 'undefined' &&
              typeof coord.ymax !== 'undefined'
            ) {

              // coord is an object containing xmin, xmax, ymin, ymax attributes
              // width is absolute value of (xmax - xmin)
              // height is absolute value of (ymax - ymin)
              // absolute value takes care of various possible referentials:
              //   - sometimes 0,0 is top-left corner
              //   - sometimes 0,0 is bottom-left corner
              [bx, by, bw, bh] = [
                coord.xmin,
                coord.ymax,
                Math.abs(coord.xmax - coord.xmin),
                Math.abs(coord.ymax - coord.ymin)
              ];

            } else {

              // coord is an array containing [x, y, width, height] values
              [bx, by, bw, bh] = coord;

            }

            if (x >= bx && x <= bx + bw &&
               y >= by && y <= by + bh) {
                // The mouse honestly hits the rect
              const insideBox = !selectedBox.dimensions || (
                  bx >= selectedBox.dimensions[0] &&
                  bx <= selectedBox.dimensions[0] + selectedBox.dimensions[2] &&
                  by >= selectedBox.dimensions[1] &&
                  by <= selectedBox.dimensions[1] + selectedBox.dimensions[3]
                );
              if (insideBox) {
                selectedBox.index = index;
                selectedBox.dimensions = box;
              }
            }
          });

        }
        else if(this.state.pixelSegmentation &&
                this.state.pixelSegmentation.length > 0) {
          selectedBox.index = this.state.pixelSegmentation[x + this.canvas.width * y];
        }

        this.props.onSelected(selectedBox.index);
        this.setState({ hoverIndex: selectedBox.index });
      });

      this.canvas.onmouseout = () => {
        this.props.onSelected(-1);
        this.setState({ hoverIndex: -1 });
        // this.renderBoxes();
      };
    });
  }

  componentWillReceiveProps(nextProps) {

    const ctx = this.canvas.getContext('2d');
    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

    const background = new Image();
    background.src = nextProps.options.base64Image ?
      'data:image/png;base64,' + this.props.image
      :
      nextProps.image;

    // Check canvas dimension with loaded image dimension
    // in order to change canvas dimension if needed
    background.onload = (() => {
      console.log(this.canvas)
      if(
        this.canvas.width !== background.width &&
        this.canvas.height !== background.height
      ) {
        console.log("hit2")

        this.canvas.width = background.width;
        this.canvas.height = background.height;
        ctx.drawImage(background, 0, 0);
        this.renderBoxes(nextProps.boxes);
      }

    });

    ctx.drawImage(background, 0, 0);
    console.log("hit 3")
    this.renderBoxes(nextProps.boxes);

    this.setState({ hoverIndex: nextProps.selectedIndex });

    return true;
  }

  componentDidUpdate() {
    this.renderBoxes();
  }

  render() {

    return <div className={this.props.className}>
      <canvas
        className="boundingBoxCanvas"
        style={this.props.options.style}
        ref={(canvas) => {
          this.canvas = canvas;
        }}
      />
    </div>;
  }
}

export default Boundingbox;
...