How to resize shape on canvas without changing its stroke size?
Remember, that Konva.Transformer is changing scaleX and scaleY properties of a node.
By default, if you are transforming a shape, its stroke will be scaled too. In some cases that is not a good behavior.
There are two ways to prevent stroke scaling:
- Reset scale of a shape
- Use
shape.strokeScaleEnabled(false)andtransformer.ignoreStroke(true)
Instructions: there are two rectangles to resize. The green one will reset its scale. The red one will just disable stroke scaling.
- Vanilla
- React
- Vue
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
// first way - reset scale on transform end
const rect1 = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: '#00ff00',
stroke: 'black',
strokeWidth: 5,
draggable: true,
});
layer.add(rect1);
const tr1 = new Konva.Transformer({
nodes: [rect1],
});
layer.add(tr1);
rect1.on('transformend', () => {
const scaleX = rect1.scaleX();
const scaleY = rect1.scaleY();
// apply the scale to the size, then reset it
rect1.scaleX(1);
rect1.scaleY(1);
rect1.width(Math.max(5, rect1.width() * scaleX));
rect1.height(Math.max(5, rect1.height() * scaleY));
});
// second way - disable stroke scaling
const rect2 = new Konva.Rect({
x: 200,
y: 50,
width: 100,
height: 100,
fill: '#ff0000',
stroke: 'black',
strokeWidth: 5,
draggable: true,
strokeScaleEnabled: false,
});
layer.add(rect2);
const tr2 = new Konva.Transformer({
nodes: [rect2],
ignoreStroke: true,
});
layer.add(tr2);
import { Stage, Layer, Rect, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [rect1Attrs, setRect1Attrs] = useState({
x: 50,
y: 50,
width: 100,
height: 100,
});
const [rect2Position, setRect2Position] = useState({ x: 200, y: 50 });
const rect1Ref = useRef();
const rect2Ref = useRef();
const tr1Ref = useRef();
const tr2Ref = useRef();
useEffect(() => {
tr1Ref.current.nodes([rect1Ref.current]);
tr2Ref.current.nodes([rect2Ref.current]);
}, []);
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Rect
x={rect1Attrs.x}
y={rect1Attrs.y}
width={rect1Attrs.width}
height={rect1Attrs.height}
fill="#00ff00"
stroke="black"
strokeWidth={5}
draggable
ref={rect1Ref}
onDragEnd={(e) => {
setRect1Attrs((attrs) => ({
...attrs,
...e.target.position(),
}));
}}
onTransformEnd={(e) => {
const node = rect1Ref.current;
const nextAttrs = {
x: node.x(),
y: node.y(),
width: node.width() * node.scaleX(),
height: node.height() * node.scaleY(),
};
node.scaleX(1);
node.scaleY(1);
setRect1Attrs(nextAttrs);
}}
/>
<Transformer ref={tr1Ref} />
<Rect
x={rect2Position.x}
y={rect2Position.y}
width={100}
height={100}
fill="#ff0000"
stroke="black"
strokeWidth={5}
draggable
strokeScaleEnabled={false}
ref={rect2Ref}
onDragEnd={(e) => setRect2Position(e.target.position())}
onTransformEnd={(e) => setRect2Position(e.target.position())}
/>
<Transformer ref={tr2Ref} ignoreStroke={true} />
</Layer>
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageSize">
<v-layer>
<v-rect
:config="rect1Config"
@transformend="handleTransformEnd"
ref="rect1Ref"
/>
<v-transformer
:config="tr1Config"
ref="tr1Ref"
/>
<v-rect
:config="rect2Config"
ref="rect2Ref"
/>
<v-transformer
:config="tr2Config"
ref="tr2Ref"
/>
</v-layer>
</v-stage>
</template>
<script setup>
import { computed, ref, onMounted } from 'vue';
const stageSize = {
width: window.innerWidth,
height: window.innerHeight
};
const rect1Size = ref({
width: 100,
height: 100
});
const rect1Config = computed(() => ({
x: 50,
y: 50,
width: rect1Size.value.width,
height: rect1Size.value.height,
fill: '#00ff00',
stroke: 'black',
strokeWidth: 5,
draggable: true
}));
const rect2Config = {
x: 200,
y: 50,
width: 100,
height: 100,
fill: '#ff0000',
stroke: 'black',
strokeWidth: 5,
draggable: true,
strokeScaleEnabled: false
};
const tr1Config = {};
const tr2Config = {
ignoreStroke: true
};
const rect1Ref = ref(null);
const rect2Ref = ref(null);
const tr1Ref = ref(null);
const tr2Ref = ref(null);
const handleTransformEnd = () => {
const node = rect1Ref.value.getNode();
rect1Size.value = {
width: node.width() * node.scaleX(),
height: node.height() * node.scaleY()
};
node.scaleX(1);
node.scaleY(1);
};
onMounted(() => {
tr1Ref.value.getNode().nodes([rect1Ref.value.getNode()]);
tr2Ref.value.getNode().nodes([rect2Ref.value.getNode()]);
});
</script>