batcher.mjs 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { MotionGlobalConfig } from 'motion-utils';
  2. import { stepsOrder } from './order.mjs';
  3. import { createRenderStep } from './render-step.mjs';
  4. const maxElapsed = 40;
  5. function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
  6. let runNextFrame = false;
  7. let useDefaultElapsed = true;
  8. const state = {
  9. delta: 0.0,
  10. timestamp: 0.0,
  11. isProcessing: false,
  12. };
  13. const flagRunNextFrame = () => (runNextFrame = true);
  14. const steps = stepsOrder.reduce((acc, key) => {
  15. acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined);
  16. return acc;
  17. }, {});
  18. const { read, resolveKeyframes, update, preRender, render, postRender } = steps;
  19. const processBatch = () => {
  20. const timestamp = MotionGlobalConfig.useManualTiming
  21. ? state.timestamp
  22. : performance.now();
  23. runNextFrame = false;
  24. if (!MotionGlobalConfig.useManualTiming) {
  25. state.delta = useDefaultElapsed
  26. ? 1000 / 60
  27. : Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1);
  28. }
  29. state.timestamp = timestamp;
  30. state.isProcessing = true;
  31. // Unrolled render loop for better per-frame performance
  32. read.process(state);
  33. resolveKeyframes.process(state);
  34. update.process(state);
  35. preRender.process(state);
  36. render.process(state);
  37. postRender.process(state);
  38. state.isProcessing = false;
  39. if (runNextFrame && allowKeepAlive) {
  40. useDefaultElapsed = false;
  41. scheduleNextBatch(processBatch);
  42. }
  43. };
  44. const wake = () => {
  45. runNextFrame = true;
  46. useDefaultElapsed = true;
  47. if (!state.isProcessing) {
  48. scheduleNextBatch(processBatch);
  49. }
  50. };
  51. const schedule = stepsOrder.reduce((acc, key) => {
  52. const step = steps[key];
  53. acc[key] = (process, keepAlive = false, immediate = false) => {
  54. if (!runNextFrame)
  55. wake();
  56. return step.schedule(process, keepAlive, immediate);
  57. };
  58. return acc;
  59. }, {});
  60. const cancel = (process) => {
  61. for (let i = 0; i < stepsOrder.length; i++) {
  62. steps[stepsOrder[i]].cancel(process);
  63. }
  64. };
  65. return { schedule, cancel, state, steps };
  66. }
  67. export { createRenderBatcher };