With Ferp

If you don't want to roll your own state management
Ferp effects are perfect for running the declarativas render method.
<!doctype html>
<html>
<body>
<canvas></canvas>
<script type="module">
import { app, effects } from 'https://unpkg.com/[email protected]^2.0.0-beta?module=1';
import { render, c } from 'https://unpkg.com/[email protected]^0.0.1-beta?module=1';
​
const getDelta = (state, timestamp) => state.lastRender > 0
? ((timestamp - state.lastRender) / 1000)
: 0;
const move = (value, change, multiplier) => value + (change * multiplier);
​
const scheduleFx = (action) => effects.defer((resolve) => {
requestAnimationFrame((timestamp) => {
resolve(effects.act(action(timestamp)));
});
});
​
const drawFx = (state, nextFx) => effects.thunk(() => {
render(state.context2d, state.sceneFn(state));
return nextFx
});
​
const drawAction = timestamp => state => {
const nextState = {
...state,
angle: move(state.angle, 30, getDelta(state, timestamp)),
lastRender: timestamp
};
return [
nextState,
drawFx(
nextState,
scheduleFx(drawAction),
),
];
};
​
const sceneFn = state => [
c('save'),
c('clearRect', {
x: 0,
y: 0,
width: state.context2d.canvas.width,
height: state.context2d.canvas.height,
}),
c('translate', { x: state.context2d.canvas.width / 2, y: state.context2d.canvas.height / 2 }),
c('rotate', { value: state.angle * Math.PI / 180 }),
c('fillStyle', { value: '#f0f' }),
c('fillRect', {
x: -50,
y: -50,
width: 100,
height: 100,
}),
c('restore'),
];
​
app({
init: [
{
sceneFn: sceneFn,
angle: 0,
lastRender: 0,
context2d: document.querySelector('canvas').getContext('2d'),
},
scheduleFx(drawAction),
]
})
​
</script>
</body>
</html>
​