Search code examples
javascriptcanvaskonvajskonva

Adding transformer to dynamically generated shapes?


I have the button which add new group which have square, to layer when clicked very simple code I guess no need to post. But my question is that how can I add transformer to it when on clicked?, I have done it with this mouseleave and mouseenter functions.

  group.on('mouseenter', () => {
        transformer.borderEnabled(true);
        transformer.resizeEnabled(true);
        layer.draw();
    });

    group.on('mouseleave', () => {
        transformer.borderEnabled(false);
        transformer.resizeEnabled(false);
        layer.draw();
    });

It is in loop which creates new group named "group", It works fine but in circle when I hover it the transformer appears but then when I go to transformer's boxes to resize it consider as it is mouseleave but this is doing only in circle not in line text.
So can I have solution for active transformer on element which is clicked or for considering hover on transformer boxes as a hover on node? Thanks


Solution

  • The mouseleave() will always fire because the pointer must leave the group to use the transformer handles or spinner.

    An alternative approach would be

    • click to enable the transformer,
    • leave the transformer in place even when the mouse moves away
    • wait for a click on some other shape to know you can hide the transformer.

    That is the standard GUI approach I believe.

    If you need to show hover focus then stick a transparent rectangle the size of the groups clientrect into the group and change its stroke from transparent to some colour in the mouseenter and back in the mouseleave. You will also maybe want to set the rect.listening to false so as it coes not interfere with mouse events on the shapes in the group, but then again it might help in dragging.

    Demo below.

    // Set up the canvas and shapes
    let stage = new Konva.Stage({container: 'container1', width: 300, height: 200});
    let layer = new Konva.Layer({draggable: false});
    stage.add(layer);
    
    // Add a transformer.
    let transFormer1 = new Konva.Transformer();
    layer.add(transFormer1);
    
    // Create a sample group 
    let group1 = new Konva.Group();
    layer.add(group1);
    group1.add(new Konva.Circle({x: 20, y: 30, radius: 15, fill: 'magenta', stroke: 'black'}))  
    group1.add(new Konva.Circle({x: 60, y: 40, radius: 15, fill: 'magenta', stroke: 'black'}))  
    group1.add(new Konva.Rect({x: 90, y: 60, width: 25, height: 25, fill: 'magenta', stroke: 'black'}));
    let pos = group1.getClientRect();
    let boundRect1 = new Konva.Rect({name: 'boundRect', x: pos.x, y: pos.y, width: pos.width, height: pos.height, fill: 'transparent', stroke: 'transparent'});
    group1.add(boundRect1);  
    
    // When mouse enters the group show a border
    group1.on('mouseenter', function(){
      let boundRect = this.find('.boundRect');
      boundRect[0].stroke('red');
      layer.draw();
    })
    
    // and remove border when mouse leaves
    group1.on('mouseleave', function(){
      let boundRect = this.find('.boundRect');
      boundRect[0].stroke('transparent');
      layer.draw();
    })
    
    
    // If the group is clicked, enable the transformer on that group.
    group1.on('click', function(){
      transFormer1.attachTo(this)
      layer.batchDraw();
    })
    
    // For a more pleasing demo let us have 2 groups.
    // Make a copy of group1, offset new group, and change fill on its child shapes except the bound rect 
    let group2 = group1.clone(); 
    layer.add(group2)
    group2.position({x: 120, y: 30});
    for (let i = 0, shapes = group2.getChildren(); i < shapes.length; i = i + 1){
      shapes[i].fill(shapes[i].fill() !== 'transparent' ? 'cyan' : 'transparent');
    }
    
    stage.draw();
    <script src="https://unpkg.com/konva@^3/konva.min.js"></script>
    <p>Move mouse over the shapes to see the group borders, click a group to apply the transformer.
    </p>
    <div id='container1' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>