3

In my KonvaJS project, I'm adding images aka "stickers" onto an uploaded image, and with KonvaJS, I'm adding "Anchors" to rotate and resize the images.

enter image description here

When the mouse is off the main image, (or maybe when you click on the sticker, it toggles edit mode?), I'd like to remove the anchors and lines.

How is this possible?

function centreRectShape(shape) {
  shape.x((stage.getWidth() - shape.getWidth()) / 2);
  shape.y((stage.getHeight() - shape.getHeight()) / 2);
}

var stage = new Konva.Stage({
  container: 'canvas-container',
  width: 650,
  height: 300
});

var layer = new Konva.Layer();
stage.add(layer);

var bgRect = new Konva.Rect({
  width: stage.getWidth(),
  height: stage.getHeight(),
  fill: 'gold',
  opacity: 0.1
});
layer.add(bgRect);

var uploadedImage = new Konva.Image({
  draggable: false
});

layer.add(uploadedImage);

imgObj = new Image();

imgObj.onload = function() {

  uploadedImage.image(imgObj);

  var padding = 20;
  var w = imgObj.width;
  var h = imgObj.height;

  var targetW = stage.getWidth() - (2 * padding);
  var targetH = stage.getHeight() - (2 * padding);

  var widthFit = targetW / w;
  var heightFit = targetH / h;
  var scale = (widthFit > heightFit) ? heightFit : widthFit;

  w = parseInt(w * scale, 10);
  h = parseInt(h * scale, 10);

  uploadedImage.size({
    width: w,
    height: h
  });
  centreRectShape(uploadedImage);
  layer.draw();
}

imgObj.src = 'https://images.pexels.com/photos/787961/pexels-photo-787961.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260';

$('.sticker').on('click', function() {
  addSticker($(this).attr('src'));
});

function addSticker(imgUrl) {
  stickerObj = new Konva.Image({
    x: 240,
    y: 20,
    width: 93,
    height: 104,
    draggable: true
  });
  layer.add(stickerObj);
  var stickerImage = new Image();
  stickerImage.onload = function() {
    stickerObj.image(stickerImage);
    layer.draw();
  };
  stickerImage.src = imgUrl;
  var imgRotator = new Konva.Transformer({
    node: stickerObj,
    keepRatio: true,
    enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right']
  });
  layer.add(imgRotator);
}
html,
* {
  margin: 0;
  padding: 0;
}

body {
  background: #eee;
}

#image-editor {
  background: #fff;
  border-radius: 3px;
  border: 1px solid #d8d8d8;
  width: 650px;
  margin: 0 auto;
  margin-top: 20px;
  box-shadow: 0 3px 5px rgba(0, 0, 0, .2);
}

.stickers {
  padding: 10px 5px;
  background: #eee;
}

.stickers>img {
  margin-right: 10px;
}
<script src="https://unpkg.com/konva@2.4.1/konva.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="image-editor">
  <div id="canvas-container"></div>
  <div class="stickers">
    <div>
      click sticer below and then rotate
    </div>
    <img class="sticker" src="https://craftblock.me/koa/fb-upload-clone/stickers/sticker%20(1).png" alt="Sticker" width="62px">

  </div>
</div>
kinx
  • 463
  • 5
  • 12
  • 31

1 Answers1

2

The most simple way is to add to the addSticker() function event handlers for both mouseover and mouseout. You can attach them, e. g., to the stage object.

stage.on('mouseover', function() {
  imgRotator.attachTo(stickerObj);
  layer.draw();
});

stage.on('mouseout', function() {
  imgRotator.detach();
  layer.draw();
});

They do two things:

  • attach / detach the Transformer object to / from the selected node.
  • refresh the layer.

From here on you can improve it. You could make the addSticker() function to return an object with the attachTo and detach methods to be able to call them from somewhere else. Something like:

function addSticker(imgUrl) {
  ...
  return {
    showTransformer: function() {
      imgRotator.attachTo(stickerObj);
      layer.draw();
    },
    hideTransformer: function() {
      imgRotator.detach();
      layer.draw();
    }
  };
}

Also, if you mean to have more than one sticker, you should implement some logic to track to which sticker should the events apply.

Edit:

You have made two comments to this answer:

  1. "I think it's best if the mouseout is for that specific sticker."

The anchors to resize and rotate the image are actually outside the image. If you hide them after the mouseout event... well, your users will never reach them ;)

You have two options to avoid it:

  • Create a custom hit region which includes the anchors.
  • Replace the event with a click to toggle the transformer object.

I use the second approach in the updated snippet.

  1. "What if there's multiple added stickers?"

You should use a closure to keep the references to the different objects in the scope of that functions.

Combining the click to toggle and the closure we have:

stickerObj.on('click', (function(transformer, sticker) {
  return function() {
    transformer.getNode() ? transformer.detach() : transformer.attachTo(sticker);
    layer.draw();
  };
})(imgRotator, stickerObj));

Extra tips:

Even after detaching the transformer, the user will be still able to move the image around. That's because draggable is a property of the image (inherited from node), not of the transformer. So maybe you also want to toggle node.draggable(boolean);.

If for some reasons you don't want to detach/attach the transformer, you can achieve a similar effect toggling borderEnabled(), resizeEnabled() and rotateEnabled().

function centreRectShape(shape) {
  shape.x((stage.getWidth() - shape.getWidth()) / 2);
  shape.y((stage.getHeight() - shape.getHeight()) / 2);
}

var stage = new Konva.Stage({
  container: 'canvas-container',
  width: 650,
  height: 300
});

var layer = new Konva.Layer();
stage.add(layer);

var bgRect = new Konva.Rect({
  width: stage.getWidth(),
  height: stage.getHeight(),
  fill: 'gold',
  opacity: 0.1
});
layer.add(bgRect);

var uploadedImage = new Konva.Image({
  draggable: false
});

layer.add(uploadedImage);

imgObj = new Image();

imgObj.onload = function() {

  uploadedImage.image(imgObj);

  var padding = 20;
  var w = imgObj.width;
  var h = imgObj.height;

  var targetW = stage.getWidth() - (2 * padding);
  var targetH = stage.getHeight() - (2 * padding);

  var widthFit = targetW / w;
  var heightFit = targetH / h;
  var scale = (widthFit > heightFit) ? heightFit : widthFit;

  w = parseInt(w * scale, 10);
  h = parseInt(h * scale, 10);

  uploadedImage.size({
    width: w,
    height: h
  });
  centreRectShape(uploadedImage);
  layer.draw();
}

imgObj.src = 'https://images.pexels.com/photos/787961/pexels-photo-787961.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260';

$('.sticker').on('click', function() {
  addSticker($(this).attr('src'));
});

function addSticker(imgUrl) {
  stickerObj = new Konva.Image({
    x: 240,
    y: 20,
    width: 93,
    height: 104,
    draggable: true
  });
  layer.add(stickerObj);
  var stickerImage = new Image();
  stickerImage.onload = function() {
    stickerObj.image(stickerImage);
    layer.draw();
  };
  stickerImage.src = imgUrl;
  var imgRotator = new Konva.Transformer({
    node: stickerObj,
    keepRatio: true,
    enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right']
  });
  layer.add(imgRotator);
  
  stickerObj.on('click', (function(transformer, sticker) {
    return function() {
      transformer.getNode() ? transformer.detach() : transformer.attachTo(sticker);
      layer.draw();
    };
  })(imgRotator, stickerObj));
}
html,
* {
  margin: 0;
  padding: 0;
}

body {
  background: #eee;
}

#image-editor {
  background: #fff;
  border-radius: 3px;
  border: 1px solid #d8d8d8;
  width: 650px;
  margin: 0 auto;
  margin-top: 20px;
  box-shadow: 0 3px 5px rgba(0, 0, 0, .2);
}

.stickers { pitón reticulada 
  padding: 10px 5px;
  background: #eee;
}

.stickers>img {
  margin-right: 10px;
}
<script src="https://unpkg.com/konva@2.4.1/konva.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="image-editor">
  <div id="canvas-container"></div>
  <div class="stickers">
    <div>
      click sticer below and then rotate
    </div>
    <img class="sticker" src="https://craftblock.me/koa/fb-upload-clone/stickers/sticker%20(1).png" alt="Sticker" width="62px">

  </div>
</div>
David
  • 6,695
  • 3
  • 29
  • 46
  • Thanks David. This works great, but I think it's best if the "mouseoff" is for that specific sticker. What if there's multiple added stickers? How can I make it so wheneer I hover that sticker, the transform is there? – kinx Oct 13 '18 at 03:03
  • @kinx - I have updated my answer (including the snippet). – David Oct 14 '18 at 23:42