Search code examples
positioningkonvajs

scale konva image from the center and hold position on redraw


Hey guys I have a slight problem here I have a Konva.js app which is working pretty well. I have this function called SET_STAGE_DATA that is supposed to save the images current position on the canvas so it can be redrawn when the canvas disapears. It works but when you scale the image from the left the image shifts. I am not sure how to fix this. I thought I may be able to fix it with offset but to no avail.

Heres my SET_STAGE_DATA function

  [SET_STAGE_DATA](state){
    const isStage = state.stage;
    if(isStage){
    const groups = state.stage.find("Group");
    const stageData = [];
    for (let x = 0; x < groups.length; x++) {
      let g = groups[x];;
      let i = g.findOne("Image").getAttrs();
      let position = g.findOne("Image").position();
      console.log("this is position", position);
      let group = { x: g.getX(), y: g.getY() };
      let image = { x: i.width, y: i.height, position };
      let obj = { image, group };
      stageData.push(obj);
    }

I use the stageData just before I build the image like so

  let groupPos; 
    let imagePos;
    if(state.stageData){
      console.log(true);
      for(let i =0; i<state.stageData.length; i++){
      imageObj.width = state.stageData[i].image.x;
      imageObj.height = state.stageData[i].image.y;
     imagePos = state.stageData[i].position;
      groupPos = state.stageData[i].group;
      }
    } else {
      groupPos = {
        x: state.stage.width() / 2 - imageObj.width / 2,
        y: state.stage.height() / 2 - imageObj.height / 2
      };
    }
    console.log("data", {groupPos, image: {width: imageObj.width, height: imageObj.height}});
    const image = new Konva.Image({
      image: imageObj,
      width: imageObj.width,
      height: imageObj.height,
      strokeWidth: 10,
      stroke: "blue",
      strokeEnabled: false
    });
    if(imagePos){
    image.position(imagePos)
    }
    const group = new Konva.Group({
      draggable: true,
      x: groupPos.x,
      y: groupPos.y
    });

I also have the typical update function found in the documentation:

  function update(activeAnchor) {
      const group = activeAnchor.getParent();

      let topLeft = group.get(".topLeft")[0];
      let topRight = group.get(".topRight")[0];
      let bottomRight = group.get(".bottomRight")[0];
      let bottomLeft = group.get(".bottomLeft")[0];
      let image = group.get("Image")[0];

      let anchorX = activeAnchor.getX();
      let anchorY = activeAnchor.getY();

      // update anchor positions
      switch (activeAnchor.getName()) {
        case "topLeft":
          topRight.y(anchorY);
          bottomLeft.x(anchorX);
          break;
        case "topRight":
          topLeft.y(anchorY);
          bottomRight.x(anchorX);
          break;
        case "bottomRight":
          bottomLeft.y(anchorY);
          topRight.x(anchorX);
          break;
        case "bottomLeft":
          bottomRight.y(anchorY);
          topLeft.x(anchorX);
          break;
      }
      // image.position(topLeft.position());
      let width = topRight.getX() - topLeft.getX();
      let height = bottomLeft.getY() - topLeft.getY();
      if (width && height) {
        image.width(width);
        image.height(height);
      }
    }
    function addAnchor(group, x, y, name) {
      let anchor = new Konva.Circle({
        x: x,
        y: y,
        stroke: "#666",
        fill: "#ddd",
        strokeWidth: 2,
        radius: 8,
        name: name,
        draggable: true,
        dragOnTop: false
      });
      anchor.on("dragmove", function() {
        update(this);
        state.layer.draw();
      });
      anchor.on("mousedown touchstart", function() {
        group.draggable(false);
        this.moveToTop();
      });
      anchor.on("dragend", function(evt) {
        group.draggable(true);
        commit(SET_STAGE_DATA);
        dispatch(UPDATE_ANIMATION, state.selectedNode);
        state.layer.draw();
      });
      // add hover styling
      anchor.on("mouseover", function() {
        document.body.style.cursor = "pointer";
        this.strokeWidth(4);
        state.layer.draw();
      });
      anchor.on("mouseout", function() {
        document.body.style.cursor = "default";
        this.strokeWidth(2);
        state.layer.draw();
      });

      group.add(anchor);
    }
    state.layer.draw();
  },

when I scale to the left the image goes outside of the anchors on redraw. I know I can fix this visually as long as I don't redraw the image by doing image.position(topLeft.position()); as you can see is commented out but it always acts as if positioning isn't set if you drag from the left. If I scale from the bottom right everything is fine. it acts as though it goes back to the previous position of the images left top corrner but from what I understand using stage.find() will give you the current stage. I'm at a totally loss. See in the image how it does not stay in the box. Any help here would be great thanks. [1]: https://i.sstatic.net/HEfHP.jpg


Solution

  • Update the SET_STAGE_DATA function to look like this

     [SET_STAGE_DATA](state){
        const isStage = state.stage;
        const isEditorMode = state.editorMode;
        if(isStage && !isEditorMode){
        const groups = state.stage.find("Group");
        const stageData = [];
        for (let x = 0; x < groups.length; x++) {
          let g = groups[x];
          let i = g.findOne("Image").getAttrs();
          let group = {x: g.getX() + i.x, y: g.getY() + i.y };
          let image = i;
          let obj = { image, group };
          stageData.push(obj);
        }
        state.stageData = stageData;
      } else {
        state.stageData = null;
      }
      }
    

    group.x and y don't update on scale but image.x and y do. On move image x and y are 0 and if you add them to the group.x and y it will put the anchors where you need them to be.