2

I am quite new to javascript, and I have been following tutorials at https://konvajs.org/ to help me learn their library.

I am currently trying to make it so you can load and select local images to move, resize, and rotate them.

This is the code I have so far:

  <head>
    <!-- USE DEVELOPMENT VERSION -->
    <script src="https://unpkg.com/konva@7.0.0/konva.min.js"></script>
    <meta charset="utf-8" />
    <title>Konva Select and Transform Demo</title>
    <style>
      body {
        margin: 0;
        padding: 0;
        overflow: hidden;
      }
    </style>
  </head>

  <body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script        src="https://cdnjs.cloudflare.com/ajax/libs/konva/3.2.5/konva.min.js"></script>
<div>Render a local image without upload</div>
<div>
  <input type="file" id="file_input">
</div>
    <div id="container"></div>
    <script>
      var width = window.innerWidth;
      var height = window.innerHeight;

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

      var layer = new Konva.Layer();
      stage.add(layer);
        //add images
        // listen for the file input change event and load the image.
$("#file_input").change(function(e){

    var URL = window.webkitURL || window.URL;
    var url = URL.createObjectURL(e.target.files[0]);
    var img = new Image();
    img.src = url;


    img.onload = function() {

      var img_width = img.width;
      var img_height = img.height;

      // calculate dimensions to get max 300px
      var max = 300;
      var ratio = (img_width > img_height ? (img_width / max) : (img_height / max))

      // now load the Konva image
      var theImg = new Konva.Image({
        image: img,
        x: 50,
        y: 30,
        width: img_width/ratio,
        height: img_height/ratio,
        draggable: true,
        rotation: 0
      });

      layer.add(theImg);
      layer.draw();
    }


});
        


      var tr = new Konva.Transformer();
      layer.add(tr);

      // by default select all shapes
      

      // at this point basic demo is finished!!
      // we just have several transforming nodes
      layer.draw();

      // add a new feature, lets add ability to draw selection rectangle
      var selectionRectangle = new Konva.Rect({
        fill: 'rgba(0,0,255,0.5)',
      });
      layer.add(selectionRectangle);

      var x1, y1, x2, y2;
      stage.on('mousedown touchstart', (e) => {
        // do nothing if we mousedown on eny shape
        if (e.target !== stage) {
          return;
        }
        x1 = stage.getPointerPosition().x;
        y1 = stage.getPointerPosition().y;
        x2 = stage.getPointerPosition().x;
        y2 = stage.getPointerPosition().y;

        selectionRectangle.visible(true);
        selectionRectangle.width(0);
        selectionRectangle.height(0);
        layer.draw();
      });

      stage.on('mousemove touchmove', () => {
        // no nothing if we didn't start selection
        if (!selectionRectangle.visible()) {
          return;
        }
        x2 = stage.getPointerPosition().x;
        y2 = stage.getPointerPosition().y;

        selectionRectangle.setAttrs({
          x: Math.min(x1, x2),
          y: Math.min(y1, y2),
          width: Math.abs(x2 - x1),
          height: Math.abs(y2 - y1),
        });
        layer.batchDraw();
      });

      stage.on('mouseup touchend', () => {
        // no nothing if we didn't start selection
        if (!selectionRectangle.visible()) {
          return;
        }
        // update visibility in timeout, so we can check it in click event
        setTimeout(() => {
          selectionRectangle.visible(false);
          layer.batchDraw();
        });

        var shapes = stage.find('.rect').toArray();
        var box = selectionRectangle.getClientRect();
        var selected = shapes.filter((shape) =>
          Konva.Util.haveIntersection(box, shape.getClientRect())
        );
        tr.nodes(selected);
        layer.batchDraw();
      });

      // clicks should select/deselect shapes
      stage.on('click tap', function (e) {
        // if we are selecting with rect, do nothing
        if (selectionRectangle.visible()) {
          return;
        }

        // if click on empty area - remove all selections
        if (e.target === stage) {
          tr.nodes([]);
          layer.draw();
          return;
        }

        // do nothing if clicked NOT on our rectangles
        if (!e.target.hasName('rect')) {
          return;
        }

        // do we pressed shift or ctrl?
        const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
        const isSelected = tr.nodes().indexOf(e.target) >= 0;

        if (!metaPressed && !isSelected) {
          // if no key pressed and the node is not selected
          // select just one
          tr.nodes([e.target]);
        } else if (metaPressed && isSelected) {
          // if we pressed keys and node was selected
          // we need to remove it from selection:
          const nodes = tr.nodes().slice(); // use slice to have new copy of array
          // remove node from array
          nodes.splice(nodes.indexOf(e.target), 1);
          tr.nodes(nodes);
        } else if (metaPressed && !isSelected) {
          // add the node into selection
          const nodes = tr.nodes().concat([e.target]);
          tr.nodes(nodes);
        }
        layer.draw();
      });
    </script>

Demo: https://www.w3schools.com/code/tryit.asp?filename=GG5QCXFMLFXJ

I am unsure how to make the selecting work on images, rather than rectangles.

Thank you

moon
  • 21
  • 2
  • Hi and welcome Moon. I wanted to congratulate you on writing a perfect question, It is clear, concise and easy to understand what you are seeking. A great example! – Vanquished Wombat Jun 27 '20 at 08:40

1 Answers1

1

In the demo, you will see that "rect" names is used to detect selectable shapes. That is a useful approach, because you may have non-selectable shapes on the stage (like a background).

So to check a shape if it is selectable, we use:

e.target.hasName('rect')

So just remember to add "rect" name to new images on the stage:

// now load the Konva image
var theImg = new Konva.Image({
        name: 'rect',
        image: img,
        x: 50,
        y: 30,
        width: img_width/ratio,
        height: img_height/ratio,
        draggable: true,
        rotation: 0
});

The name itself is not very important. You can find a different way to detect "selectable" nodes.

https://www.w3schools.com/code/tryit.asp?filename=GG6XVLB39IKW

lavrton
  • 18,973
  • 4
  • 30
  • 63