2

I am trying to dynamically make and move Rectangles in React Konva. The problem is that sometimes my code runs perfectly fine, sometimes it throws error and sometimes rectangles don't get update and move properly.

I was not facing any problem till I made the functionality of updating state whenever the rectangle is moved. i am calling handleDragStart and handleDragEnd for this purpose. In handleDragStart I select the rectangle and put it in selectedrectangle variable and then in handleDragEnd I delete this rectangle from state and add the updated rectangle.

Problem 1 - The error shows that selectedRectangle is still null. It's not updated in handleDragStart function which it should.

Problem 2 - When I move Rectangles they move arbitrarily anywhere in the stage. When I draw new rectangle the shift back to their original drawn position.

Please help me find the problem and resolve it.

var index;
var selectedRectangle = null;

export default class MyRectangle extends React.Component {        
    constructor(props) {
        super(props);

        this.state = {
          shapes : [],
          isDrawing : false,
          isDrawingMode : true,
          image : null,
        }

        this.handleDragStart = this.handleDragStart.bind (this);
        this.handleDragEnd = this.handleDragEnd.bind (this);
      }

      componentDidMount() {
        const image = new window.Image();
        image.src = this.props.image;

        image.onload = () => {
          this.setState ({
            image: image
          });
        };
      }

      handleClick = (e) => {
        if (!this.state.isDrawingMode) {
          return;
        }

        if (this.state.isDrawing) {
          this.setState ({
            isDrawing: !this.state.isDrawing,
          })

          return;
        }

        const newShapes = this.state.shapes.slice();
        newShapes.push ({
          x: e.evt.layerX,
          y: e.evt.layerY,
          width: 0,
          height: 0,
        });

        this.setState ({
          isDrawing: true,
          shapes: newShapes,
        });
      }

      handleMouseMove = (e) => {
        if (!this.state.isDrawingMode) return;

        const mouseX = e.evt.layerX;
        const mouseY = e.evt.layerY;

        if (this.state.isDrawing) {

          const currShapeIndex = this.state.shapes.length - 1;
          const currShape = this.state.shapes[currShapeIndex];
          const newWidth = mouseX - currShape.x;
          const newHeight = mouseY - currShape.y;

          const newShapesList = this.state.shapes.slice();
          newShapesList[currShapeIndex] = {
            x: currShape.x,
            y: currShape.y,
            width: newWidth,
            height: newHeight
          }

          this.setState ({
            shapes: newShapesList,
          });
        }
      }

      handleCheckboxChange = () => {
        this.setState ({
          isDrawingMode: !this.state.isDrawingMode,
        })
      }

      handleDragStart(e) {
        this.state.shapes.map ((sh) => {
          if ((sh.x < e.evt.layerX) && (sh.y < e.evt.layerY) && ((sh.x + sh.width) > e.evt.layerX) && ((sh.y + sh.height) > e.evt.layerY)) {
            selectedRectangle = sh;
          }
        });

        console.log(selectedRectangle)
      }

      handleDragEnd (e) {

        console.log(selectedRectangle)
        index = this.state.shapes.indexOf (selectedRectangle);
        this.state.shapes.splice(index, 1);
        console.log(index);

        selectedRectangle.x = e.target._lastPos.x;
        selectedRectangle.y = e.target._lastPos.y;

        this.setState({shapes : [...this.state.shapes, selectedRectangle]});
        console.log(this.state.shapes)       
      }

      render() {
       return (
          <div>
            <input type = "checkbox" checked = {this.state.isDrawingMode} onChange = {this.handleCheckboxChange} />
            <label>Drawing Mode</label>

            <Stage 
                ref = 'stage'
                width = {window.innerWidth} 
                height = {window.innerHeight} 
                onContentClick = {this.handleClick} 
                onContentMouseMove = {this.handleMouseMove}
            >
              <Layer ref = 'layer'>
                <Image image = {this.state.image}   />

                {this.state.shapes.map((shape) => {
                  return (
                    <Group>
                      <Rect
                        x = {shape.x}
                        y = {shape.y}
                        width = {shape.width}
                        height = {shape.height}
                        isDrawingMode = {this.state.isDrawingMode}
                        strokeWidth = {2}
                        draggable = "true"
                        stroke="yellow"
                        fill="green"
                        opacity={0.4}
                        onDragStart = {(e) => this.handleDragStart(e)}
                        onDragEnd = {(e) => this.handleDragEnd(e)}
                      />                                              
                    </Group >
                  );
                })} 

              </Layer>
            </Stage>
            <button onClick={this.sendData}>Submit</button>
          </div>
        );
      }
}
Ketan
  • 83
  • 1
  • 1
  • 12

1 Answers1

0

I do not see where selectedRectangle is defined, so I assume you do not explicitly define it. As a result, here

  handleDragStart(e) {
    this.state.shapes.map ((sh) => {
      if ((sh.x < e.evt.layerX) && (sh.y < e.evt.layerY) && ((sh.x + sh.width) > e.evt.layerX) && ((sh.y + sh.height) > e.evt.layerY)) {
        selectedRectangle = sh;
      }
    });

    console.log(selectedRectangle)
  }

selectedRectangle becomes a local variable inside your arrow function and when you try to console.log it, you are outside its scope. You will need to properly define this variable.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • Sorry I skipped it. I declared it globally before the class declaration because of that problem only but it not works . – Ketan Aug 03 '18 at 10:55
  • @Ketan how is it not working now? What error/behavior do you experience? – Lajos Arpad Aug 03 '18 at 11:28
  • I am experiencing same problem as I stated in my question. I already used selectedRectangle as global variable in my code before your answer but I forgot to add in the code here. – Ketan Aug 03 '18 at 11:35
  • in handleDragEnd function in line selectedRectangle.x. It says cannot deine value to undefined. SelectedRectangle is still sometimes null. The problem is that the error not always arises. Sometimes the code runs smoothly – Ketan Aug 03 '18 at 11:48
  • @Ketan write console.log("initialized"); just after selectedRange = sh;. Also, write console.log("needed"); just before selectedRectangle.x = e.target._lastPos.x;. When you execute your script, what do you see in your console? – Lajos Arpad Aug 03 '18 at 12:27
  • Solved it. I put some brackets in my if statement above selectedRectangle and its working fine now. New if statement - if ((sh.x < e.evt.layerX) && (sh.y < e.evt.layerY) && ((sh.x + sh.width) > e.evt.layerX) && ((sh.y + sh.height) > e.evt.layerY)) – Ketan Aug 03 '18 at 12:40
  • @Ketan I do not see the difference between your original if and the improved if. Can you explain the difference? – Lajos Arpad Aug 03 '18 at 12:43
  • Yeah there's no difference. It is working for some reason. I don't know why. – Ketan Aug 03 '18 at 12:48
  • @Ketan there is a high chance that your JS file was cached into the browser and declaring the variable as a global was a correct fix, but the cache was emptied only recently. If this answer solved your problem, then you may consider accepting it as the correct answer. – Lajos Arpad Aug 03 '18 at 13:43