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/ferp@^2.0.0-beta?module=1';
import { render, c } from 'https://unpkg.com/declarativas@^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>

Last updated