Load HTML5 Canvas Stage from JSON Tutorial
To load a complex stage that originally contained images and event bindings using Konva,
we need to create a stage node using Konva.Node.create(), and then set the
images and event handlers with the help of selectors using the find() method.
Images and event handlers must be manually set because they aren't serializable.
That methods works for small apps. For more complex cases take a look into Best Practices
- Vanilla
- React
- Vue
import Konva from 'konva';
// JSON string from a previous save
const json = '{"attrs":{"width":578,"height":200},"className":"Stage","children":[{"attrs":{},"className":"Layer","children":[{"attrs":{"x":100,"y":100,"sides":6,"radius":70,"fill":"red","stroke":"black","strokeWidth":4},"className":"RegularPolygon"}]}]}';
// create node using json string
const stage = Konva.Node.create(json, 'container');
// get reference to the hexagon
const hexagon = stage.findOne('RegularPolygon');
// bind events
hexagon.on('click', () => {
hexagon.fill(Konva.Util.getRandomColor());
});
Note: Using Konva.Node.create() directly in React is an anti-pattern. In React applications, we should manage state separately from the view. Instead of deserializing entire node structures, we should load the data that defines our shapes and let React components handle the rendering. The example below demonstrates how to load shape data as state in React:
import { Stage, Layer, RegularPolygon } from 'react-konva';
import { useState, useEffect } from 'react';
import Konva from 'konva';
const App = () => {
const [shapeData, setShapeData] = useState(null);
useEffect(() => {
// Simulating loading JSON data from storage or API
const loadData = () => {
// This would typically come from localStorage, API, etc.
const jsonString = '{"hexagon":{"x":100,"y":100,"sides":6,"radius":70,"fill":"red","stroke":"black","strokeWidth":4}}';
try {
// Parse the JSON into a JavaScript object
const data = JSON.parse(jsonString);
setShapeData(data);
} catch (error) {
console.error('Error parsing JSON:', error);
}
};
loadData();
}, []);
const handleClick = () => {
if (shapeData) {
setShapeData({
...shapeData,
hexagon: {
...shapeData.hexagon,
fill: Konva.Util.getRandomColor()
}
});
}
};
// Don't render until we have data
if (!shapeData) return <div>Loading...</div>;
return (
<Stage width={578} height={200}>
<Layer>
<RegularPolygon
{...shapeData.hexagon}
onClick={handleClick}
/>
</Layer>
</Stage>
);
};
export default App;
Note: Using Konva.Node.create() directly in Vue is an anti-pattern. In Vue applications, we should manage state with reactive data separately from the view. Instead of deserializing entire node structures, we should load the data that defines our shapes and let Vue components handle the rendering. The example below demonstrates how to load shape data as reactive state in Vue:
<template>
<div>
<v-stage :config="stageSize" v-if="shapeData">
<v-layer>
<v-regular-polygon
:config="shapeData.hexagon"
@click="handleClick"
/>
</v-layer>
</v-stage>
<div v-else>Loading...</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Konva from 'konva';
const stageSize = {
width: 578,
height: 200
};
const shapeData = ref(null);
onMounted(() => {
// Simulating loading JSON data from storage or API
const loadData = () => {
// This would typically come from localStorage, API, etc.
const jsonString = '{"hexagon":{"x":100,"y":100,"sides":6,"radius":70,"fill":"red","stroke":"black","strokeWidth":4}}';
try {
// Parse the JSON into a JavaScript object
const data = JSON.parse(jsonString);
shapeData.value = data;
} catch (error) {
console.error('Error parsing JSON:', error);
}
};
loadData();
});
const handleClick = () => {
if (shapeData.value) {
shapeData.value = {
...shapeData.value,
hexagon: {
...shapeData.value.hexagon,
fill: Konva.Util.getRandomColor()
}
};
}
};
</script>