Curve
A Curve is a collection of keyframes and modifiers for the purpose of creating any simple or complex curve that can be evaluated at any time. It also contains static methods that facilitate the creation of simple curves.
Generic Class
Curve class is generic, meaning it takes a type argument (when using TypeScript) that will declare the type of the value returned by evaluating the curve at any time. Throughout the documentation, this type will be referred to as T.Constructor
new Curve<T> (
keys: Keyframe<T>[] = [],
modifiers: CurveModifier<T>[] = [],
endBehavior: EndBehavior = EndBehavior.Clamp,
smoothing = 0.25
)
keys: The list of Keyframe used to define the curve.modifiers: The list of CurveModifier that will be applied to the curve.endBehavior: The EndBehavior used to define the curve’s behavior beyond its first and last keyframes.smoothing: The degree to which different easing functions are smoothly transitioned into each other between keyframes.\
Properties
startTime
The time of the first keyframe of the curve.
endTime
The time of the last keyframe of the curve.
duration
The difference in time between the first and last keyframes of the curve.
smoothing
The degree to which different easing functions are smoothly transitioned into each other between keyframes. This value is initally set in the constructor but can be modified at any time.
endBehavior
The EndBehavior used to define the curve’s behavior beyond its first and last keyframes. This value is initally set in the constructor but can be modified at any time.
Evaluating the curve
evaluate (time: number, modifierStop = -1): T
The evaluate method returns the value of the curve at any given time. This value is calculated by interpolating between the two keyframes before and after the time value.
The modifierStop parameter is optional and defaults to -1. It indicates how many modifiers to apply to the curve. A negative modifierStop value indicates that each modifier should be applied. The modifierStop parameter is mainly for use in curve modifiers that need to evaluate the curve in order to prevent endless recursive evaluation loops.
import { Curve, NumberKeyframe } from 'curves';
const curve: Curve<number> = new Curve<number>(
[
new NumberKeyframe(0, 0),
new NumberKeyframe(1, 2),
new NumberKeyframe(5, 1),
], // Keyframes
);
// Expected output:
// time: 0, value: 0
// time: 1, value: 2
// time: 2, value: 1.9375
// time: 3, value: 1.5
// time: 4, value: 1.0625
// time: 5, value: 1
for (let i = 0; i < 5; i += 1) {
console.log(`time: ${i}, value: ${curve.evaluate(i)}`)
}
Adding & Removing Keyframes
There are multiple methods available for adding and removing keyframes from a curve. There are also some protocols that must be observed if you choose to manually add or remove keyframes.
Curve.keys
The keys property of a Curve object is an array that stores the keyframes of the curve. Each keyframe must extend the Keyframe class, with the same generic type argument as the Curve. For example, a Curve<number> must have keyframes that extend Keyframe<number>.
import { Curve, NumberKeyframe } from 'curves';
// Define the curve
const curve: Curve<number> = new Curve<number>(
[
new NumberKeyframe(0, 0),
new NumberKeyframe(1, 2),
new NumberKeyframe(2, 1),
], // Keyframes
);
// Expected output:
// time: 0, value: 0
// time: 1, value: 2
// time: 2, value: 1
curve.keys.forEach((key) => console.log(`time: ${key.time}, value: ${key.value}`));
addKeyframe and removeKeyframe methods, Update must be called before the next evaluate for the curve to function properly. The same is true if any of the keyframes stored in the array are directly modified.addKeyframe
addKeyframe (keyframe: Keyframe<T>): void
The addKeyframe method adds a keyframe to Curve.keys and updates the curve.
import { Curve, NumberKeyframe } from 'curves';
// Define the curve
const curve: Curve<number> = new Curve<number>(
[
new NumberKeyframe(0, 0),
new NumberKeyframe(1, 1),
], // Keyframes
);
// Add a keyframe
curve.addKeyframe(new NumberKeyframe(0.5, 2));
// Expected output:
// time: 0, value: 0
// time: 0.5, value: 2
// time: 1, value: 1
curve.keys.forEach((key) => console.log(`time: ${key.time}, value: ${key.value}`));
removeKeyframe
removeKeyframe (keyframe: Keyframe<T> | { time: number, value: T }): void
The removeKeyframe method takes a keyframe or any value with a time property and a value property as its only argument. It finds the index of a keyframe with a matching time and value and removes it from Curve.keys and updates the curve.
import { Curve, NumberKeyframe } from 'curves';
// Define the curve
const curve: Curve<number> = new Curve<number>(
[
new NumberKeyframe(0, 0),
new NumberKeyframe(1, 2),
new NumberKeyframe(2, 1),
], // Keyframes
);
// Remove a keyframe
curve.removeKeyframe({time: 2, value: 1});
// Expected output:
// time: 0, value: 0
// time: 1, value: 2
curve.keys.forEach((key) => console.log(`time: ${key.time}, value: ${key.value}`));
removeKeyframeByTime
removeKeyframeByTime (time: number): void
The removeKeyframe method takes a number representing the time of the keyframe to remove as its only argument. It finds the index of a keyframe with a matching time and removes it from Curve.keys and updates the curve.
import { Curve, NumberKeyframe } from 'curves';
// Define the curve
const curve: Curve<number> = new Curve<number>(
[
new NumberKeyframe(0, 0),
new NumberKeyframe(1, 2),
new NumberKeyframe(2, 1),
], // Keyframes
);
// Remove a keyframe
curve.removeKeyframeByTime(2);
// Expected output:
// time: 0, value: 0
// time: 1, value: 2
curve.keys.forEach((key) => console.log(`time: ${key.time}, value: ${key.value}`));
removeKeyframeByIndex
removeKeyframeByIndex (index: number): void
The removeKeyframe method takes a number representing the index of the keyframe in Curve.keys as its only argument. It finds the index of a keyframe with a matching time and value and removes it from Curve.keys and updates the curve. Each time the curve updates, the keyframes are sorted in order of their time.
import { Curve, NumberKeyframe } from 'curves';
// Define the curve
const curve: Curve<number> = new Curve<number>(
[
new NumberKeyframe(0, 0),
new NumberKeyframe(1, 2),
new NumberKeyframe(2, 1),
], // Keyframes
);
// Remove a keyframe
curve.removeKeyframeByIndex(0);
// Expected output:
// time: 1, value: 2
// time: 2, value: 1
curve.keys.forEach((key) => console.log(`time: ${key.time}, value: ${key.value}`));
Managing Modifiers
Modifiers are used to apply transformations and effects to a curve after it is evaluated. The modifiers are applied in the order of the list that stores them.
modifiers
The modifiers property of a Curve object is an array that stores the modifiers of the curve. Each modifier must extend the CurveModifier class, with the same generic type argument as the Curve. For example, a Curve<number> must have modifiers that extend CurveModifier<number>.
import { Curve, Modifiers } from 'curves';
// Define the curve
const curve: Curve<number> = new Curve<number>(
[
new NumberKeyframe(0, 0),
new NumberKeyframe(5, 2)
], // Keyframes
[
new Modifiers.Number.Noise(0.1, 0.5),
new Modifiers.Number.Noise(0.025, 2) // Layered noise
] // Modifiers
);
// Expected output: [ Noise, Noise ]
console.log(curve.modifiers);
addModifier, Update must be called before the next evaluate for the curve to function properly. The same is true if any of the modifiers stored in the array are directly modified.addModifier
addModifier (modifier: CurveModifier<T>): void
The addModifier method adds a modifier to the end of the modifier list and subsequently updates the list.
import { Curve, Modifiers } from 'curves';
// Define the curve
const curve: Curve<number> = new Curve<number>(
[
new NumberKeyframe(0, 0),
new NumberKeyframe(5, 2)
], // Keyframes
[
new Modifiers.Number.Noise(0.1, 0.5),
new Modifiers.Number.Noise(0.025, 2) // Layered noise
] // Modifiers
);
// Add a modifier
curve.addModifier(new Modifiers.Number.Clamp(0, 5));
// Expected output: [ Noise, Noise, Clamp ]
console.log(curve.modifiers);
Updating the curve
{{ < lead > }} update (): void {{ < /lead > }}
After manually modifying Curve.keys or Curve.modifiers manually, call update before evaluating the curve. It is used to calculate Curve.startTime, Curve.endTIme, and Curve.duration, as well as configuring keyframes and modifiers.
import { Curve, NumberKeyframe } from 'curves';
// Define the curve
const curve: Curve<number> = new Curve<number>(
[
new NumberKeyframe(0, 0),
new NumberKeyframe(1, 1),
], // Keyframes
);
// Add a keyframe
curve.keyframes.push(new NumberKeyframe(5, 2));
// Expected output: 1
console.log(curve.endTime);
// Update curve
curve.update();
// Expected output: 5
console.log(curve.endTime);