Billiards simulation with MobX and canvas
We’re building a small game. You have 11 glass balls – marbles, if you will. Grab one, throw it at the others, see how they bounce around. There is no point to the game, but it looks cool, and it’s fun to build.
We’re using React and Konva to render our 11 marbles on an HTML5 Canvas element, MobX to drive the animation loop, and D3 to help with collision detection. Because of our declarative approach to animation, we can split this example into two parts:
- Part 1: Rendering the marbles
- Part 2: Building the physics
You can see the finished code on Github and play around with a hosted version of the code you’re about to build.
I know this example comes late in the book, and you’re feeling like you know all there is to React and visualizations. You can think of this example as practice. It’s a good way to learn the basics of MobX.
Decorators
Before we begin, let me tell you about decorators. MobX embraces them to make its API easier to use. You can use MobX without decorators, but decorators make it better. I promise.
About two years ago, decorators got very close to becoming an official spec, then were held back. I don’t know why, but they’re a great feature whose syntax is unlikely to change. So even if MobX has to change its implementation when decorators do land in the spec, you’re not likely to have to change anything.
You can think of decorators as function wrappers. Instead of something like this:
inject('store', ({ store }) => <div>A thing with {store.value}</div>);
You can write something like this:
@inject('store')({ store }) => <div>A thing with {store.value}</div>
I know, that’s not much of a difference. It becomes better looking when you work with classes or combine multiple decorators. That’s when they shine. No more })))}))
at the end of your function declarations.
By the way, inject
is to MobX much like connect
is to Redux. We’ll talk more about that later.
Part 0: Some setup
Because decorators aren’t in the JavaScript spec, we have to tweak how we start our project. We can still use create-react-app
, but there’s an additional step.
You should start a new project like this:
$ create-react-app billiards-game --scripts-version custom-react-scripts
This command creates a new directory with a full setup for React. Just like you’re used to.
The addition of --scripts-version custom-react-scripts
employs @kitze’s custom-react-scripts project to give us more configuration options. Namely the ability to enable decorators.
We enable them in the .env
file. Add this line:
// billiards-game/.env// ...REACT_APP_DECORATORS=true
No installation necessary. I think custom-react-scripts
uses the transform-decorators-legacy
Babel plugin behind the scenes. It’s pre-installed, and we just enabled it with that .env
change.
Before we begin, you should install the other dependencies as well:
$ npm install --save konva react-konva mobx mobx-react d3-timer d3-scale d3-quadtree
This installs Konva, MobX, and the parts of D3 that we need. You’re now ready to build the billiards game.
A quick MobX primer
Explaining MobX in detail is beyond the scope of this book. I bet you can learn it by osmosis as you follow the code in our billiards example.
That said, here’s a quick rundown of the concepts we’re using.
MobX implements the ideas of reactive programming. There are values that are observable and functions that react when those values change. MobX ensures only the minimal possible set of observers is triggered on every change.
So, we have:
@observable
– a property whose changes observers subscribe to
@observer
– a component whose render()
method observes values
@computed
– a method whose value can be fully derived from obsevables
@action
– a method that changes state, analogous to a Redux reducer
@inject
– a decorator that injects global stores into a ...