How to resize and rotate canvas shapes with Vue and Konva?
Currently, there is no pure declarative "Vue-way" to use the Transformer tool. However, you can still use it effectively by manually attaching it to Konva nodes.
The idea is to create a v-transformer component and manually attach it to the required node when selected.
Instructions: Click on a shape to select it. You can then resize and rotate it using the transformer handles. The shape will change color after transformation.
<template>
<v-stage
ref="stage"
:config="stageSize"
@mousedown="handleStageMouseDown"
@touchstart="handleStageMouseDown"
>
<v-layer ref="layer">
<v-rect
v-for="item in rectangles"
:key="item.id"
:config="item"
@transformend="handleTransformEnd"
/>
<v-transformer ref="transformer" />
</v-layer>
</v-stage>
</template>
<script setup>
import { ref } from 'vue';
import Konva from 'konva';
const stageSize = {
width: window.innerWidth,
height: window.innerHeight
};
const rectangles = ref([
{
rotation: 0,
x: 10,
y: 10,
width: 100,
height: 100,
scaleX: 1,
scaleY: 1,
fill: 'red',
name: 'rect1',
draggable: true
},
{
rotation: 0,
x: 150,
y: 150,
width: 100,
height: 100,
scaleX: 1,
scaleY: 1,
fill: 'green',
name: 'rect2',
draggable: true
}
]);
const selectedShapeName = ref('');
const transformer = ref(null);
const handleTransformEnd = (e) => {
// find element in our state
const rect = rectangles.value.find(
(r) => r.name === selectedShapeName.value
);
if (!rect) return;
// update the state with new properties
rect.x = e.target.x();
rect.y = e.target.y();
rect.rotation = e.target.rotation();
rect.scaleX = e.target.scaleX();
rect.scaleY = e.target.scaleY();
// change fill color randomly
rect.fill = Konva.Util.getRandomColor();
};
const updateTransformer = () => {
const transformerNode = transformer.value.getNode();
const stage = transformerNode.getStage();
const selected = selectedShapeName.value;
const selectedNode = stage.findOne('.' + selected);
// do nothing if selected node is already attached
if (selectedNode === transformerNode.nodes()[0]) {
return;
}
if (selectedNode) {
// attach to selected node
transformerNode.nodes([selectedNode]);
} else {
// remove transformer
transformerNode.nodes([]);
}
};
const handleStageMouseDown = (e) => {
// clicked on stage - clear selection
if (e.target === e.target.getStage()) {
selectedShapeName.value = '';
updateTransformer();
return;
}
// clicked on transformer - do nothing
const clickedOnTransformer =
e.target.getParent().className === 'Transformer';
if (clickedOnTransformer) {
return;
}
// find clicked rect by its name
const name = e.target.name();
const rect = rectangles.value.find((r) => r.name === name);
if (rect) {
selectedShapeName.value = name;
} else {
selectedShapeName.value = '';
}
updateTransformer();
};
</script>