'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:
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 |
