Simple drawing app
# install
bun i
# dev server
npm run vite package/appstate
The state is hold in a redux flavored global store (see reducer.ts)
Some data are passed as props to components (see how StatefulControlPanel.tsx reads the animationRunning from the store)
Other piece of data update the app outside react reconciliation. This pattern is useful to avoid wasting react update on event that occur often (such as animation) (see how Canvas.tsx subscribe to the store to 1\ detect when the canvas should be redraw 2\ redraw on the next animation frame )
The philosophy here is to have the store handle the most of the app logic. For example the canvas component is only concern about reporting user click on a specific pixel of the canvas. The store is then responsible of 1\ projecting this coordinates into the world space 2\ resolving collision 3\ reacting to a click on a rectangle.
viewport
The viewport (ie what part of the infinite canvas is on display) is part of the global state. Which is convenient since we need to it to randomize new rectangle position in a visible partition.
It is derived from the canvas size on screen, it is implemented in a way where it should not be hard to let the user control the viewport (to zoom / pan )
components
The components are split between two category:
- presentation only ("dumb" components), which receive everything they need from their props and have little logic.
- stateful ("smart" components) which manipulate the state from the data source and delegate the presentation to the former components.
There is an attempt to have a system with reusable UI components (inside the atomic folder)
performance consideration
This implementation favors readability over performance. Noticeably it creates a lot of js object ( every rectangle is cloned on every frame while animated ), this is likely not great when the number of rectangle explode. We can discuss how to improve on that.