Wimal Weerawansa
Wimal Weerawansa

Reputation: 157

Scale stage and drag element relative positions not working

Im having small issue with calculating realtime position while scaled stage in KonvaJS React. In my example I have two Rect's small (red) one position is relative to Big Rect (yellow), While dragging big rectangle small one moves relative to big one but when I scale UP or DOWN small one jumps some pixels in x, y positions.

Working example

enter image description here

import React from "react";
import ReactDOM from "react-dom";
import { Rect, Stage, Layer } from "react-konva";

import "./styles.css";

function Box({ attrs, draggable, updateAttr }) {
  const onDragStart = e => {
    const element = e.target;
    if (typeof updateAttr === "function") {
      updateAttr(element._lastPos);
    }
  };

  const onDragMove = e => {
    const element = e.target;
    if (typeof updateAttr === "function") {
      updateAttr(element._lastPos);
    }
  };

  const onDragEnd = e => {
    const element = e.target;
    if (typeof updateAttr === "function") {
      updateAttr(element._lastPos);
    }
  };

  return (
    <Rect
      draggable={draggable}
      onDragEnd={onDragEnd}
      onDragMove={onDragMove}
      onDragStart={onDragStart}
      fill={attrs.fill}
      x={attrs.x}
      y={attrs.y}
      width={attrs.width}
      height={attrs.height}
    />
  );
}

const calculatePos = ({ r1, r2 }) => ({
  ...r2,
  x: parseInt(r1.width + r1.x + 10, 10),
  y: parseInt(r1.y + r1.height / 2 - r2.height / 2, 10)
});

const boxAttr = {
  x: 50,
  y: 50,
  width: 200,
  height: 150,
  fill: "yellow"
};

const secondAttr = calculatePos({
  r2: {
    fill: "red",
    width: 20,
    height: 20
  },
  r1: boxAttr
});

class App extends React.Component {
  state = {
    scale: 1,
    boxAttr,
    secondAttr
  };
  updateScale = scale => {
    this.setState({ scale });
  };

  updateAttr = pos => {
    const { secondAttr, boxAttr } = this.state;
    this.setState({
      secondAttr: calculatePos({
        r2: secondAttr,
        r1: { ...boxAttr, ...pos }
      })
    });
  };

  render() {
    const { scale, boxAttr, secondAttr } = this.state;

    return (
      <div className="App">
        <button
          onClick={() => {
            this.updateScale((parseFloat(scale) + 0.1).toFixed(1));
          }}
        >
          + Scale ({scale})
        </button>

        <button
          onClick={() => {
            this.updateScale((parseFloat(scale) - 0.1).toFixed(1));
          }}
        >
          - Scale ({scale})
        </button>
        <Stage
          scaleX={scale}
          scaleY={scale}
          width={window.innerWidth}
          height={window.innerHeight}
        >
          <Layer>
            <Box
              updateAttr={this.updateAttr}
              draggable={true}
              attrs={boxAttr}
            />
            <Box draggable={false} attrs={secondAttr} />
          </Layer>
        </Stage>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Please help me to update correct x,y for red rectangle.


I know Grouped Draggable solution will fix this issue, but I cannot implement in my real application due to complex relations that cannot be group for draggable reason

Upvotes: 2

Views: 939

Answers (1)

P&#228;rt Johanson
P&#228;rt Johanson

Reputation: 1650

When the first box is dragged, you have to translate the mouse coordinates on the canvas to the coordinate system inside the scaled canvas to move the second box.

I modified the updateAttr method to do so.

There is a catch however, when the updateAttr is called by konva itself (after the dragEnd event) it does so with translated coordinates. That is why I added a second argument to the updateAttr called doScale. When calling it yourself, set it true and it will translate the coordinates, if konva calls it, there is no second argument and it evaluates to false and does no translation.

The code is available at: https://codesandbox.io/s/6126wz0kkk

Upvotes: 1

Related Questions