Frakcool
Frakcool

Reputation: 11153

How to add a Konva component using React?

I'm relatively new to React and I am getting stuck when trying to integrate KonvaJS into React. I'm trying to follow the first example shown in the Konva Overview:

// first we need to create a stage
var stage = new Konva.Stage({
  container: 'container',   // id of container <div>
  width: 500,
  height: 500
});

// then create layer
var layer = new Konva.Layer();

// create our shape
var circle = new Konva.Circle({
  x: stage.width() / 2,
  y: stage.height() / 2,
  radius: 70,
  fill: 'red',
  stroke: 'black',
  strokeWidth: 4
});

// add the shape to the layer
layer.add(circle);

// add the layer to the stage
stage.add(layer);

// draw the image
layer.draw();

So I created my own file KonvaDrawer.js which encapsulates the above code:

import Konva from 'konva';

function KonvaDrawer() {
    // first we need to create a stage
    var stage = new Konva.Stage({
        container: 'container',   // id of container <div>
        width: 500,
        height: 500
    });
    
    // then create layer
    var layer = new Konva.Layer();
    
    // create our shape
    var circle = new Konva.Circle({
        x: stage.width() / 2,
        y: stage.height() / 2,
        radius: 70,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 4
    });
    
    // add the shape to the layer
    layer.add(circle);
    
    // add the layer to the stage
    stage.add(layer);
    
    // draw the image
    layer.draw();
}

export default KonvaDrawer;

Then in my App.js I create the div with an id="container" as the code above mentions it needs to have a div with that id (or that's what I understand)

import React from 'react';
import './App.css';

function App() {
  return (
    <div id="container"></div>
  );
}

export default App;

And finally the index.js where I call the KonvaDrawer function:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import KonvaDrawer from './KonvaDrawer';

ReactDOM.render(
  <React.StrictMode>
    <App />
    {KonvaDrawer()}
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
serviceWorker.unregister();

However when I do npm start I get this error:

Stage.js:105 Uncaught Can not find container in document with id container

So, I'm not sure why this is happening,

All the examples that I've found online create the Konva components like this:

<Stage>
    <Layers>
        <Shapes>

For example here and here but I'm curious about if the way that I'm trying to do it is possible and why it's not working currently.

So, if I follow the above structure and do this:

import React from 'react';
import './App.css';
import {Stage, Layer, Circle} from 'react-konva';

function App() {
  return (
    <Stage width={window.innerWidth} height={window.innerHeight}>
      <Layer>
        <Circle x={window.innerWidth / 2} 
                y={window.innerHeight / 2} 
                radius={70}
                fill={'red'}
                stroke={'black'}
                strokeWidth={4}
        />
      </Layer>
    </Stage>
  );
}

export default App;

It correctly shows a red circle, but if I wanted to automate the creation of layers of something with for-loops, or create elements with Javascript, then how could I do it?

Upvotes: 1

Views: 2688

Answers (1)

lavrton
lavrton

Reputation: 20308

Your KonvaDrawer is just a function that creates Konva nodes directly. It can't be used as a React component (https://reactjs.org/tutorial/tutorial.html#overview)

So react components do not create elements (such as Konva nodes or DOM elements) directly. They just define how the page (or canvas element) should look at the end.

If you want to create elements with for-loop or other ways you just need to define your "state" of the app and the generate components from the state.

const App = () => {
  const [circle, setCircles] = React.useState([{x: 10, y: 10}, {x: 100, y: 100}]);

  return (
    <Stage width={window.innerWidth} height={window.innerHeight}>
      <Layer>
        {circles.map(circle => (
          <Circle x={circle.x} 
                y={circle.y} 
                radius={70}
                fill={'red'}
                stroke={'black'}
                strokeWidth={4}
          />
        ))}
      </Layer>
    </Stage>
  );
}

Upvotes: 2

Related Questions