konvajs / react-konva

React + Canvas = Love. JavaScript library for drawing complex canvas graphics using React.
https://konvajs.github.io/docs/react/
MIT License
5.72k stars 254 forks source link

Drawing a shape using click, mouse move, and click #164

Closed navono closed 6 years ago

navono commented 6 years ago

Konva is a great canvas framework and react-konva also nice. Thanks for the cool things!

I want to dynamic create shape on layers, and I tried add shapes into a list, then Layer show the children, but it's doesn't draw the shape as I move mouse. Here is a reproduce demo.

The problem seems like can't change the shape's props after update it's width and height with setState , but don't know why. Am I miss something or use wrong solution?

Thanks for reply.

ejarzo commented 6 years ago

I think I can help, but I am not sure exactly what you are trying to achieve. Are you trying to make it so that you click and drag such that the more you drag, the bigger the shape (and it's size is set once you release)? Or did you just want to just change the dimensions of the shape whenever the mouse moves?

navono commented 6 years ago

@ejarzo Thank you for your reply. Two goals.

First, I want to dynamic generate shapes(e.g. a rect) on layer by mouse click(get the x and y position), then move mouse(dynamic update rect width and heigh, the rect also dynamic display in the layer), then mouse click again(determine the final width and height ), done.

Second, dynamic adjust the dimensions of already exist shape. This can be easier like this demo, change the circle radius, make the circle bigger or smaller.

I hope this can explain clearly.

navono commented 6 years ago

@ejarzo I move the creation of rect into handleClick. Pick the x and y position in first mouse click and create rect in second mouse click and push it to children, then it works.

But among the mouse movements, the rect doesn't show. My intention is want to show it in among the mouse movements.

lavrton commented 6 years ago

I think you should not create React elements in your "handle" function. Store in this.state only real state of your app (like coordinates of Rects). Use render function to create elements.

As this is not a bug or feature I will close the issue. Please use StackOverflow for questions.

ejarzo commented 6 years ago

I realize this isn't a bug/issue but just to follow up - @navono Instead of creating React elements in your handleClick function, try only setting the state there, and letting render handle the rest. React is declarative, meaning that you describe your app in state, and render automatically reflects any changes you make to state.

If you represent your shapes as an array of objects (each having { x, y, width, height }), and then display that array in render, any time you change values in the array, the shapes will change on the screen. The dimension values will be props for a ColoredRect, so ColoredRect will not need to manage its own dimensions - it can simply render the props that it receives.

I made a demo here: https://codesandbox.io/s/wwpyr3j737

navono commented 6 years ago

@ejarzo Sorry about this not a bug/issue. I should ask in SO.

Big thank you about the demo.

physicsai commented 5 years ago

@ejarzo's answer is very instructive, but generally speaking the code presented at https://codesandbox.io/s/wwpyr3j737 is highly inefficient as it re-renders every square while drawing out a new square. Try drawing 20+ square to see how the browser slows down. Consider using konva's to() method to animate the individual square that is being drawn.

ejarzo commented 5 years ago

@physicsai Very true. Using to() could definitely work but depending on the situation, it may not be desirable because the state of your application would not be in sync with what is being drawn on the canvas. To keep the same declarative approach without ruining the performance, we could optimize the ColoredRect component using shouldComponentUpdate() or, similar to your suggestion, we could separate the shape that is currently being drawn from the rest so that only that one is re-rendered on drag. Then once it is completed, we add it to the list of static shapes.