Load Simple HTML5 Canvas Stage from JSON Tutorial
To load a simple stage from JSON with Konva, we can use the Konva.Node.create() method.
The create() method accepts a JSON string and container id as arguments.
- Vanilla
- React
- Vue
import Konva from 'konva';
// JSON string from a previous save
const json = '{"attrs":{"width":400,"height":400},"className":"Stage","children":[{"attrs":{},"className":"Layer","children":[{"attrs":{"x":100,"y":100,"radius":50,"fill":"red","stroke":"black","strokeWidth":3},"className":"Circle"}]}]}';
// create node using json string
const stage = Konva.Node.create(json, 'container');
// you can keep adding events, etc
const circle = stage.findOne('Circle');
circle.on('click', () => {
circle.fill(Konva.Util.getRandomColor());
});
Note: Using Konva.Node.create() directly in React or Vue is an anti-pattern. In these frameworks, we should manage state (data) separately from the view (components). Instead of serializing and loading entire node structures, we should save and load the data that defines our shapes, then let the framework components handle rendering. This approach is more aligned with React and Vue's declarative, state-driven patterns and provides better control over component lifecycle and events.
import { Stage, Layer, Circle } from 'react-konva';
import { useState, useEffect } from 'react';
import Konva from 'konva';
const App = () => {
// In React, we store shape data as state instead of using Konva.Node.create()
const [shapeData, setShapeData] = useState(null);
useEffect(() => {
// Simulating loading JSON data from storage or API
const savedShapeData = {
circle: {
x: 100,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 3
},
// We could have more shapes here
};
// In a real app, this might be:
// fetch('/api/shapes').then(response => response.json()).then(setShapeData)
setShapeData(savedShapeData);
}, []);
const handleCircleClick = () => {
setShapeData({
...shapeData,
circle: {
...shapeData.circle,
fill: Konva.Util.getRandomColor()
}
});
};
// Don't render until we have data
if (!shapeData) return <div>Loading...</div>;
return (
<Stage width={400} height={400}>
<Layer>
<Circle
{...shapeData.circle}
onClick={handleCircleClick}
/>
</Layer>
</Stage>
);
};
export default App;
Note: Using Konva.Node.create() directly in React or Vue is an anti-pattern. In these frameworks, we should manage state (data) separately from the view (components). Instead of serializing and loading entire node structures, we should save and load the data that defines our shapes, then let the framework components handle rendering. This approach is more aligned with React and Vue's declarative, state-driven patterns and provides better control over component lifecycle and events.
<template>
<v-stage :config="stageSize" v-if="shapeData">
<v-layer>
<v-circle
:config="shapeData.circle"
@click="handleCircleClick"
/>
</v-layer>
</v-stage>
<div v-else>Loading...</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Konva from 'konva';
const stageSize = {
width: 400,
height: 400
};
// In Vue, we store shape data as reactive state
const shapeData = ref(null);
onMounted(() => {
// Simulating loading JSON data from storage or API
const savedShapeData = {
circle: {
x: 100,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 3
}
// We could have more shapes here
};
// In a real app, this might be:
// fetch('/api/shapes').then(response => response.json()).then(data => shapeData.value = data)
shapeData.value = savedShapeData;
});
const handleCircleClick = () => {
if (shapeData.value) {
shapeData.value = {
...shapeData.value,
circle: {
...shapeData.value.circle,
fill: Konva.Util.getRandomColor()
}
};
}
};
</script>