Konva JS Event Handling

As per Konva doc –
Konva is an HTML5 Canvas JavaScript framework that extends the 2d context
by enabling canvas interactivity for desktop and mobile applications.

Every thing starts from Konva.Stage that contains several user’s layers (Konva.Layer). Each layer can contain shapes, groups of shapes, or groups of other groups.

Event handling is a very common requirement which all applications require. Konva JS also support event handling and we can add events on any shapes.
To bind an event on Konva Shape, we use on() method. On method requires an event type and a function type as a parameter.
Konva supports mouseover, mouseout, mouseenter, mouseleave, mousemove, mousedown, mouseup, wheel, click, dblclick, dragstart, dragmove, and dragend desktop events. We can also bind event on layers and spaces as well with shapes.

Read Konva Doc to binding event on shape, layer and stage.

__________________________________________________


*Cases which we are going to cover in this Blog*


CASE 1: We have to implement Event handling at one place instead of binding event listening on all the shapes. If we bind events on each and every konva shape then it is very complicated to handle listening everywhere. So the safe place is the ‘Stage’ where we can perform event binding.


CASE 2: We have to catch all the shapes where the user clicked. If multiple shapes are overlapped then get all the shapes.


CASE 3: Users should not be able to click on the evacuated place of the Canvas.


CASE 4: Code should work in this way like if there is more than one stage so by using a config ‘disableEvent: true’, we can stop event binding on that stage.


CASE 5: We could also use ‘disableEvent: true’ for layers as well.


CASE 6: In case if we don’t need to apply click on any shape so we can do that as well.


CASE 7: Once clicked on a shape, we could also get targeted shaped data.

konva JS


HTML TEMPLATE FILE

<!DOCTYPE html>
<html>

<head>
  <script src="https://unpkg.com/konva@4.2.0/konva.min.js"></script>
  <link rel="stylesheet" href="./konva.css" />

  <meta charset="utf-8" />

</head>

<body>
  <div id="maincontainer" class="main-div">
    <div id="container" class="track-container"></div>
    <div id="propContainer" class="prop-container"></div>
  </div>
</body>
<script src="konva-service.js"></script>

</html>


JS FILE

// Call draw method to draw konva shapes
draw();

var finalShapes = [];

// Create stage with using params stageConfig and add click event.
// If disableEvent is true for a stage - don't bind listener on that stage
function createStage(stageConfig) {
    let stage = new Konva.Stage(stageConfig);
    if (!stageConfig.disableEvent) {
        addEventOnKonvaStage(stage);
    }
    return stage;
}

// Bind Event Listener on stage
function addEventOnKonvaStage(stage) {
    stage.on('click', ($event) => {
        finalShapes = [];
        console.clear()
        const evt = $event.evt;
        console.log(evt.offsetX, evt.offsetY, $event.target.attrs);
        if ($event.target.getClassName() === 'Stage') {
            return;
        }
        readAllLayersOfStage(stage, evt);
        if (finalShapes) {
            if (finalShapes.length) {
                alert(finalShapes.map((item) => item.id).join(',  '));
            }
            console.log('Clicked Shapes: ', finalShapes.map((item) => item))
        }
    });

}

// Based on passed stage in param we read all layer of that stage.
// Get all shapes for each layer whose disableEvent is not set
function readAllLayersOfStage(stage, evt) {
    var layers = stage.getLayers();
    layers.forEach(element => {
        if (!element.attrs.disableEvent) {
            element.children.each((item) => {
                getOverlappedShapes(item, evt);
            });
        }

    });
}

// Where user clicked - get all shapes on that particular points
function getOverlappedShapes(item, evt) {
    const attrs = item.attrs;
    if (attrs.data) {
        const hitStrokeWidth = attrs.hitStrokeWidth / 2;
        let x, y;
        switch (item.getClassName()) {
            case 'Line':
                if (Math.abs(attrs.points[1] - evt.offsetY) <= hitStrokeWidth) {
                    finalShapes.push(attrs)
                }
                break;
            case 'Circle':
                x = evt.offsetX >= (attrs.x - hitStrokeWidth - attrs.radius) && evt.offsetX <= (attrs.x + hitStrokeWidth + attrs.radius);
                y = evt.offsetY >= (attrs.y - hitStrokeWidth - attrs.radius) && evt.offsetY <= (attrs.y + hitStrokeWidth + attrs.radius);
                if (x && y) {
                    finalShapes.push(attrs);
                }
                break;
            default:
                x = evt.offsetX >= (attrs.x - hitStrokeWidth) && evt.offsetX <= (attrs.x + hitStrokeWidth + attrs.width);
                y = evt.offsetY >= (attrs.y - hitStrokeWidth) && evt.offsetY <= (attrs.y + hitStrokeWidth + attrs.height);

                if (x && y) {
                    finalShapes.push(attrs);
                }
                break;

        }
    }
}

// Draw layer and shapes 
function draw() {
    var container = document.getElementById('maincontainer');

    var trackStage = createStage({
        name: 'trackStage',
        container: 'container',
        width: container.offsetWidth,
        height: container.offsetHeight * 80 / 100
    });

    var propStage = createStage({
        name: 'propStage',
        container: 'propContainer',
        width: container.offsetWidth,
        height: container.offsetHeight * 20 / 100,
        disableEvent: true
    });

    var assetLayer = new Konva.Layer({
        name: 'assetLayer'
    });
    var lineLayer = new Konva.Layer({
        name: 'lineLayer'
    });
    var geoPositionLayer = new Konva.Layer({
        name: 'geoLayer',
        disableEvent: true
    });

    var propLayer = new Konva.Layer({
        name: 'propLayer'
    });

    var line1 = new Konva.Line({
        id: 'line1',
        points: [10, 80, 100, 80, 900, 80],
        stroke: 'black',
        strokeWidth: 1,
        hitStrokeWidth: 6,
        data: {
            id: 'line1',
            text: 'this is line1'
        }
    })

    var line2 = new Konva.Line({
        id: 'line2',
        points: [280, 130, 700, 130],
        stroke: 'black',
        strokeWidth: 1,
        hitStrokeWidth: 6,
        data: {
            id: 'line2',
            text: 'this is line2'
        }
    })

    var circle1 = new Konva.Circle({
        id: 'circle1',
        x: 90,
        y: 70,
        radius: 24,
        fill: 'green',
        stroke: 'black',
        strokeWidth: 1,
        hitStrokeWidth: 6,
        data: {
            text: 'this is circle',
            a: 123,
            b: 34223
        },
        opacity: 1
    });

    var rect1 = new Konva.Rect({
        id: 'rect1',
        x: 90,
        y: 60,
        width: 400,
        height: 50,
        fill: 'orange',
        stroke: 'black',
        strokeWidth: 1,
        hitStrokeWidth: 6,
        opacity: 0.5,
        data: {
            r: 123,
            b: 34223
        },
    });

    var rect2 = new Konva.Rect({
        id: 'rect2',
        x: 440,
        y: 70,
        width: 120,
        height: 30,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 1,
        opacity: 0.5,
        hitStrokeWidth: 6,
        data: {
            id: 'rect2',
            a: 234,
            b: 2343244
        }
    });

    var rect3 = new Konva.Rect({
        id: 'rect3',
        x: 620,
        y: 70,
        width: 40,
        height: 50,
        fill: 'pink',
        stroke: 'black',
        strokeWidth: 1,
        opacity: 1,
        hitStrokeWidth: 6,
        data: {
            id: 'rect2',
            a: 234,
            b: 2343244
        }
    });

    var text1 = new Konva.Text({
        x: 10,
        y: 200,
        text: 'This is Text Shape of Geo Layer which is not clickable',
        fontSize: 15,
        fill: 'green'
    });

    var text2 = new Konva.Text({
        x: 200,
        y: 30,
        text: 'This is Text Shape of Asset Layer. (Cannot click on this shape because it does not have data property)',
        fontSize: 13,
        fill: 'blue'
    });

    var text3 = new Konva.Text({
        x: 10,
        y: 20,
        text: 'This is Text Shape of Prop Stage (We have disabled click on this stage)',
        fontSize: 15,
        fill: 'white'
    });

    // add the shape to the assetLayer
    lineLayer.add(line1);
    lineLayer.add(line2);

    assetLayer.add(circle1);
    assetLayer.add(rect2);
    assetLayer.add(rect1);
    assetLayer.add(rect3);
    assetLayer.add(text2);

    geoPositionLayer.add(text1);

    propLayer.add(text3);

    trackStage.add(assetLayer);
    trackStage.add(lineLayer);
    trackStage.add(geoPositionLayer);
    propStage.add(propLayer);
}


CSS FILE

body {
    padding: 0;
    font-size: 16px;
    overflow: hidden;
    margin: 8px;
}

.main-div {
    height: 98vh;
    border: 1px solid wheat;
}

.track-container {
    background-color: yellow;

}

.prop-container {
    background-color: black;
}


event handling konva js

Konva JS Event Handling

Read Konva here https://konvajs.org/docs/
Learn more about Canvas here: https://www.jsmount.com/html-5-canvas-top-interview-questions-and-answer/

  1. Hi, my loved one! I want to say that this article is awesome, great written, and comes with approximately all important Infos. I would like to look at extra posts like this .|

  2. I just like the helpful information you supply to your articles.
    I will bookmark your weblog and test again here regularly. I’m somewhat certain I will learn a lot of new stuff proper right here! Good luck for the following!

  3. Keto Advanced Weight Loss

    Greetings I am so glad I found your site, I really found you by mistake, while I was researching on Aol for something else,
    Anyways I am here now and would just like to
    say kudos for a tremendous post and a all round thrilling blog (I also love the theme/design),
    I don’t have time to read through it all at the minute but I have bookmarked it and also added in your RSS feeds,
    so when I have time I will be back to read more, Please
    do keep up the awesome work.

  4. It抯 exhausting to search out educated folks on this subject, but you sound like you understand what you抮e talking about! Thanks

  5. Youre so cool! I dont suppose Ive read something like this before. So nice to find any person with some original thoughts on this subject. realy thank you for starting this up. this website is something that’s needed on the web, somebody with a bit originality. useful job for bringing one thing new to the internet!

  6. Every weekend i used to pay a visit this website, for the reason that i wish for enjoyment, as this
    this site conations really pleasant funny stuff too.

  7. I’m gone to tell my little brother, that he should also visit this blog on regular basis to get updated from most up-to-date news update.

  8. Thank you for every other informative blog. Where else may just I am
    getting that type of information written in such a perfect method?
    I’ve a challenge that I’m just now operating on, and I’ve
    been on the look out for such info.

Leave a Reply

Your email address will not be published.