Rate
A common pattern in interactivity and motion is changing values over time. For example, maybe we want an object to appear to float, slowly sinking over time.
A common approach to achieve this is to change a value by a fixed amount every time an update loop happens:
let state = { value: 0}
function update() { state = { ...state, value: value + 0.01 }}
Although this is mostly fine, there are three challenges:
- The amount of change (in the above case, 0.01) is tied to the speed of
update()
being called. For example, if you end up callingupdate()
less often, you’ll need to increase the number to get a similar rate of change. - Even if you try to call
update()
at a fixed rate (eg. usingsetInterval()
) timing inconsistencies mean that the change amount can be applied unevenly - Values might seem less readable than talking about change per second, per minute etc.
ixfx has two functions which compute a slice of an amount over time.
- Sources.perSecond : calculate part of an amount spread over one second
- Sources.perMinute : calculate part of an amount spread over one minute
In its basic usage, call perSecond
with the amount you want to correspond to one second of elapsed time. You get back a function which yields a number.
import { Sources} from 'ixfx/modulation.js';// Create the function onceconst gain = Sources.perSecond(100);
// ...and then call the returned function whenever// we want the amount that has elapsedconst value = gain();
Every time you call the returned function, you get a proportion of the original amount depending on how much time has elapsed. For example, if we had 100 as the amount:
- Calling twice within 1 second will each yield 50 (totalling 100 over 1sec)
- Calling once after 2 seconds will yield 200
When you call the function the accumulated value resets.
If we give the clamp
option, it will not let the return value exceed the original amount
// Even if we call increaser() after several seconds, it will// at most yield 100.const increaser = Sources.perSecond(100, { clamp: true });
Example
In the demo sketch layout, you might want to use perSecond
as part of state computation.
Here’s an example of that, as used in the demo below.
import { Sources } from 'ixfx/modulation.js';import { resolveFields } from 'ixfx/data.js';
const settings = { // How quickly to deflate deflationRate: 0.02};
let state = { // Amount of inflation on 0..1 scale inflation: 1, // Function that returns amount to deflate per second deflator: Sources.perSecond(settings.deflationRate)};
async function update() { // 'resolve fields', which will automatically run 'deflator' function for us // and return back an object with plain values: let { inflation, deflator } = await resolveFields(state);
// Compute new inflation value inflation = clamp(inflation - deflator);
// Save new state saveState({ inflation }); window.requestAnimationFrame(update);}
// ...other parts of code omitted...