'How to solve a special "select and transform: resize text" problem?

1. Background:

I use konvajs to create a "table" component:

structure:

       Stage
         |     
       Layer       
         |            
   +-----+--------------+    
   |                    |
Group(tableGroup) Group(tableGroup)
                        |
         +--------------+--------+
         |                       |
      Group(cellGroup)     Group(cellGroup)
         |
    +----+----+
    |         |
Shape(Rect)  Shape(Text)

image:

table component

2. Target:

I want every text shape to be width adaptive. I found it in the official documents of konvajs:

How to change width of the text with transforming tool? https://konvajs.org/docs/select_and_transform/Resize_Text.html

Scenario 1: add all "cellGroup" to "transformer"

// My code
tableGroup.on('click', function (e) { // Click the selected table
   tr.nodes([])
   tableGroup.getChildren().map(function(item) {
       tr.nodes(tr.nodes().concat(item)); // Add all cellGroups to transformer
       item.off('transform')
       item.on('transform', (e) => {
           item.setAttrs({
               width: item.width() * item.scaleX(),
               height: item.height() * item.scaleY(),
               scaleX: 1,
               scaleY: 1,
           });
           item.getChildren().map(function(child){
               child.setAttrs({
                    width: item.width() * item.scaleX(),
                    height: item.height() * item.scaleY(),
                    scaleX: 1,
                    scaleY: 1,
               });
           })
       })
    })
});

Scheme 2: add "tablegroup" to "transformer"

// My code
tableGroup.on('click', function (e) { // Click the selected table
   tr.nodes([tableGroup]) // Add all tableGroup to transformer
   tableGroup.on('transform', (e) => {
       tableGroup.getChildren().map(function(item) {
           item.setAttrs({
               width: item.width() * item.scaleX(),
               height: item.height() * item.scaleY(),
               scaleX: 1,
               scaleY: 1,
           });
           item.getChildren().map(function(child){
               child.setAttrs({
                    width: item.width() * item.scaleX(),
                    height: item.height() * item.scaleY(),
                    scaleX: 1,
                    scaleY: 1,
               });
           })
        })
     })
});

conclusion: Scheme 1 is feasible, but scheme 2 is not.My requirement is to add "tableGroup" to transformer and realize text width adaptation.Find a solution, thank you very much.

3. Other:

Q: Why must "tableGroup" be added to "transformer"?

A: Because when moving a "Group" or "Shape" with "Transformer", the coordinates (x, y) of the "Group" or "Shape" will be changed. I don't want to change the coordinates of "cellGroup", I want to change the coordinates of "tableGroup" (x, y). Or you have a better solution.Find a solution, thank you very much.



Solution 1:[1]

Here is a solution using two groups - one for the table outline and a second to contain the cells. The cell group has its attrs set to follow the table group as it is transformed - excluding the scale!

This is not a perfect solution that you can cut & paste as a usable component but should show you a potential alternative.

/*
* From here onwards we set up the stage and its contents.
*/
const stage = new Konva.Stage({
        container: 'container',
        width: window.innerWidth,
        height: window.innerHeight 
      }),
      layer = new Konva.Layer(),
      
      tblGroup = new Konva.Group({draggable: true}), 
      cellGroup = new Konva.Group(),
      cellRect = new Konva.Rect({strokeWidth: 1, stroke: 'black', name: 'cellRect'}),
      cellText = new Konva.Text({fontName: "Arial", fontSize: 20, fill: 'black', name: 'cellText'}),
      tr = new Konva.Transformer();
      
stage.add(layer);
layer.add(tr);

// Using this plain JS objet to define the table and relative cell positions.
const tblData = {
  position: { x: 100, y: 100, width: 202, height: 82},
  cells: [
    {x: 1, y: 1, width: 100, height: 40, text: 'Cell 1-1'},
    {x: 101, y: 1, width: 100, height: 40, text: 'Cell 1-2'},
    {x: 1, y: 41, width: 100, height: 40, text: 'Cell 2-1'},
    {x: 101, y: 41, width: 100, height: 40, text: 'Cell 2-2'},    
  ]
}
const tableGroup = tblGroup.clone({x: tblData.position.x, y: tblData.position.y}),
      tblPosGroup = tableGroup.clone(), // position exactly as tableGroup.
      tblRect = cellRect.clone({x:0, y: 0, width: tblData.position.width, height: tblData.position.height});
tblRect.stroke('red');
tableGroup.add(tblRect);

// add the cells.
for (let i = 0; i < tblData.cells.length; i++){
  const
      cell = cellGroup.clone({}),
      rect = cellRect.clone(tblData.cells[i]),
      text = cellText.clone(tblData.cells[i]);
  
  // Note we stach the positioning data into the Konva shape instances in a custom attr - used in the transform event
  rect.setAttrs({posData: tblData.cells[i]});
  text.setAttrs({posData: tblData.cells[i]});
  
  cell.add(rect,text);
  tblPosGroup.add(cell);
}

layer.add(tableGroup, tblPosGroup);

tableGroup.on('transform', (e) => {
  
  // make the cells group follow the tbl group as it is transformed
  tblPosGroup.setAttrs({position: tableGroup.position(), rotation: tableGroup.rotation()});


  // find all the objects we want to manager - using the shape name() attr which we search with a dot prefix.
  tblPosGroup.find('.cellRect, .cellText').map(function(item) {

    const cellPos = item.getAttr("posData");
    
    // set the position and size of the cells referring to original position & size data and applying scale from transformer.
    item.setAttrs({
      x: cellPos.x * tableGroup.scaleX(),
      y: cellPos.y * tableGroup.scaleY(),
      width: cellPos.width * tableGroup.scaleX(),
      height: cellPos.height * tableGroup.scaleY()
    });
  })
  
})
 
tblPosGroup.on('click', function (e) { // When the table is clicked - actually we listen on the cells group as this will get the click
  tr.nodes([tableGroup]); // Add all tableGroup to transformer
  e.cancelBubble = true;
})

  
stage.on('click', function (e) { 
  tr.nodes([]);   
})
body {
  margin: 20px;
  padding: 0;
  overflow: hidden;
  background-color: #f0f0f0;
}
<script src="https://unpkg.com/konva@8/konva.min.js"></script>
<p>A table-like construction. The aim was to keep cell contents unchanged in size and scale whilst allowing a transformer to be used.</p>
<p>Solution is two groups. Group#1 is the main table rect and group#2 is the cells. Group#2 attrs are set to follow group#1 attrs in the group#1.onTransform event listener.</p>

<div id="container"></div>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Vanquished Wombat