Optional ReadonlydefaultObject containing default configuration options that are appropriate for the effect. These options can be overwritten while calling the clip factory function. This makes it convenient to rely on some preset behaviors that make sense without having to set them in a clip factory function.
When defining preset effects, the configuration options that you are allowed to set here may be limited by the AnimClip
value for AnimClip.categoryImmutableConfig (which you cannot modify). For example, for exit animation clips,
ExitClip.categoryImmutableConfig sets a value for commitsStyles, so you cannot set a value for commitsStyles in
defaultConfig for any entries in the exit effects bank.
const clipFactories = webchalk.createAnimationClipFactories({
additionalEntranceEffectBank: {
// Element fades in, starting from 0 opacity.
fadeIn: {
buildFrameGenerators() {
return {
keyframesGenerator_play: () => {
return [ {opacity: '0'}, {} ];
},
} as const;
},
},
fadeIn_default: {
buildFrameGenerators() {
return {keyframesGenerator_play: () => {
return [ {opacity: '0'}, {} ];
},
} as const;
},
defaultConfig: {
duration: 2000,
},
},
}
});
// select element from DOM
const element = document.querySelector('.some-element');
const ent1 = clipFactories.Entrance(element, 'fadeIn', [], {});
// ↑ duration will be set to whatever the default duration is for all
// EntranceClip objects
const ent2 = clipFactories.Entrance(element, 'fadeIn_default', [], {});
// ↑ duration will be set to 2000 because that is what was specified in
// the 'fadeIn_default' effect definition
const ent3 = clipFactories.Entrance(element, 'fadeIn_default', [], {duration: 1000});
// ↑ duration will be set to 1000 because configuration settings set in the
// clip factory function call will overwrite any default settings
Optional ReadonlyimmutableObject containing immutable configuration options for the effect that are appropriate for the effect. These options cannot be overwritten while calling the clip factory function). This makes it possible to set in stone some expected behaviors of clips that use certain effects.
When creating custom effects, the configuration options that you are allowed to set here may be limited by the AnimClip
value for AnimClip.categoryImmutableConfig (which you cannot modify). For example, for exit animation clips,
ExitClip.categoryImmutableConfig sets a value for commitsStyles, so you cannot set a value for commitsStyles in
immutableConfig for any entries in the exit effects bank.
const clipFactories = webchalk.createAnimationClipFactories({
additionalEntranceEffectBank: {
appear: {
buildFrameGenerators() {
return {
keyframesGenerator_play: () => {
return [];
},
};
},
},
appear_immutable: {
buildFrameGenerators() {
return {
keyframesGenerator_play: () => {
return [];
},
};
},
immutableConfig: {
duration: 0,
easing: 'linear',
composite: 'replace',
},
},
}
});
// select element from DOM
const element = document.querySelector('.some-element');
const ent1 = clipFactories.Entrance(element, 'appear', [], {duration: 1000});
// ↑ no issues
const ent2 = clipFactories.Entrance(element, 'appear_immutable', [], {endDelay: 1000});
// ↑ no issues (there is no immutable setting on endDelay for 'appear_immutable')
const ent3 = clipFactories.Entrance(element, 'appear_immutable', [], {duration: 1000});
// ↑ TypeScript compiler error will be thrown because duration is not allowed to be set
// when using the 'appear_immutable' effect. When running the code, this duration will
// simply be ignored in favor of the immutable duration setting.
Optional ReadonlyhowString that determines how frequently buildFrameGenerators will be run. SUGGESTION: Read the documentation for buildFrameGenerators first.
on-first-play-only, buildFrameGenerators will run the first time
play() is called and never again. The first-made EffectFrameGeneratorSet object's functions and the closure created during the first call to
buildFrameGenerators will be used for the clip's entire lifetime.
on-first-play-only when code in the closure of buildFrameGenerators
only needs to (or perhaps must only) run once for the returned generators to be correct.on-every-play, buildFrameGenerators will run every time
the clip is about to play forward rather than just the first time,
thus creating a new closure and returning a new EffectFrameGeneratorSet each time.// global variable that will be used in the fadeOut_exclusive effect.
let usedFadeOutEx = false;
const clipFactories = webchalk.createAnimationClipFactories({
additionalExitEffectBank: {
// A preset effect you wrote for fading an element out.
// Here, it makes no difference what howOftenBuildGenerators is set to.
//
// - If set to 'on-first-play-only', then buildFrameGenerators() will run only once
// (on the first play()). Thus, keyframesGenerator_play() is defined
// only once and is set to return [{}, {opacity: 0}].
//
// - If set to 'on-every-play', then EVERY time the clip
// plays, buildFrameGenerators() plays. Thus, keyframesGenerator_play()
// will keep being redefined and set to be a function that
// returns [{}, {opacity: 0}]. It made no difference because
// the body of keyframesGenerator_play() remains the same.
//
// Thus, it makes no difference what howOftenBuildGenerators is set to.
// For the sake of optimization, you decide to set it to 'on-first-play-only'
// (which is the default value anyway, but it adds more clarity).
fadeOut: {
buildFrameGenerators() {
return {
keyframesGenerator_play: () => {
return [{}, {opacity: 0}];
},
};
},
howOftenBuildGenerators: 'on-first-play-only',
},
// A preset animation effect you made that can only be used by one animation clip
// (Why you would ever do something this is unclear, but the reason does not matter.)
// Here, howOftenBuildGenerators must be set to 'on-first-play-only'.
//
// - If set to 'on-first-play-only', then the global variable usedFadeOutEx is
// checked for truthiness and then set to true on the first (and only) running of
// buildFrameGenerators(). On subsequent calls to play(), buildFrameGenerators() does not re-run, so
// the if-condition is not run again. However, any OTHER clip that uses the fadeOut_exclusive
// effect will fail on their first play() because they need to run buildFrameGenerators() for
// the first time and will throw the error (because usedFadeOutEx is already set to true).
// This is the desired behavior.
//
// - If set to 'on-every-play', then buildFrameGenerators() will run on every play(). Thus,
// playing the same clip twice will always cause an error because it will run into
// the if-conditional again after usedFadeOutEx is already set to true, which is
// NOT the desired behavior.
//
// The difference is that 'on-first-play-only' causes the if-conditional to run
// only once, while 'on-every-play' causes it to be encountered a second time.
fadeOut_exclusive: {
buildFrameGenerators() {
if (usedFadeOutEx) {
throw new Error(`Only one clip is allowed to use the 'fadeOut_exclusive' effect.`);
}
usedFadeOutEx = true;
return {
keyframesGenerator_play: () => {
return [ {}, {opacity: 0} ];
},
};
},
howOftenBuildGenerators: 'on-first-play-only',
},
// A preset animation effect you made for flying out to the left side of the screen.
// Here, it makes no difference what howOftenBuildGenerators is set to.
//
// - If set to 'on-first-play-only', then buildFrameGenerators() will run only once. Thus,
// keyframesGenerator_play() is defined only once, and the closure containing
// computeTranslationStr() will also only be made once. On every play(),
// keyframesGenerator_play() uses computeTranslationStr() to compute
// the translation, so the translation will always be recomputed.
// This is the desired behavior.
//
// - If set to 'on-every-play', then every time play() is called to play the clip,
// buildFrameGenerators() is called again, creating a new closure containing a function
// called computeTranslationStr() and returning a new keyframesGenerator_play()
// that uses computeTranslationStr() to compute the translation. It makes no
// difference since the bodies of computeTranslationStr() and
// keyframesGenerator_play() remain the same, so this is functionally the
// same as the previous paragraph.
// This is the desired behavior.
//
// Thus, it makes no difference what howOftenBuildGenerators is set to.
// For the sake of optimization, you decide to set it to 'on-first-play-only'.
flyOutLeft1: {
buildFrameGenerators() {
const computeTranslationStr = () => {
// compute distance between right side of element and left side of viewport
const orthogonalDistance = -(this.domElem.getBoundingClientRect().right);
// create translation string
const translationString = `${orthogonalDistance}px 0px`;
return translationString;
}
return {
keyframesGenerator_play: () => {
return [
{translate: computeTranslationStr()}
];
},
// keyframesGenerator_rewind could have been omitted, but for ease of
// visual understanding, they are kept for the flyOut effects
keyframesGenerator_rewind: () => {
return [
{translate: computeTranslationStr()},
{translate: `0 0`}
];
}
};
},
immutableConfig: {
composite: 'accumulate',
},
howOftenBuildGenerators: 'on-first-play-only',
},
// A preset animation effect for flying out either left or right (random).
// Here, howOftenBuildGenerators must be set to 'on-every-play'.
//
// - If set to 'on-first-play-only', then leftOrRight is defined only once. Thus,
// once the clip plays for the first time, leftOrRight will be permanently set
// to 'go left' or 'go right' within the closure created by buildFrameGenerators(),
// so the element's direction will not be randomized each time.
// This is NOT the desired effect.
//
// - If set to 'on-every-play', then every time play() is called to play the clip,
// buildFrameGenerators() is called again. The variable leftOrRight is thus recomputed, so
// the result of computeTranslationStr() will be randomly left or right every time
// the clip is played.
// This is the desired behavior.
//
// The difference is that 'on-every-play' causes the effect to use a fresh
// leftOrRight on each play, while 'on-first-play-only' does not.
flyOutRandom: {
buildFrameGenerators() {
// 50% change of going left or right
const leftOrRight = Math.random() < 0.5 ? 'go left' : 'go right';
const computeTranslationStr = () => {
// compute distance between right side of element and left side of viewport
const distGoingLeft = -(this.domElem.getBoundingClientRect().right);
// compute distance between left side of element and right side of viewport
const distGoingRight = window.innerWidth - this.domElem.getBoundingClientRect().left;
// choose distance based on leftOrRight
const orthogonalDistance = leftOrRight === 'go left' ? distGoingLeft : distGoingRight;
// create translation string
const translationString = `${orthogonalDistance}px 0px`;
return translationString;
}
return {
keyframesGenerator_play: () => {
return [
{translate: computeTranslationStr()}
];
},
keyframesGenerator_rewind: () => {
return [
{translate: computeTranslationStr()},
{translate: `0 0`}
];
}
};
},
immutableConfig: {
composite: 'accumulate',
},
howOftenBuildGenerators: 'on-every-play',
},
}
});
ReadonlybuildFunction that runs when the clip is played. Returns a EffectFrameGeneratorSet object, which contains callback functions that will produce the effects for both playing and rewinding the animation.
A subset of properties of the AnimClip storing the effect at runtime
An array containing parameters used to set the behavior for the specific animation effect when calling the clip factory function.
An object containing 4 possible callback functions that return Keyframes and/or Mutator.
Overview
Whenever buildFrameGenerators runs (how often it runs depends on
howOftenBuildGenerators), it returns a new EffectFrameGeneratorSet containing
callback functions, which we can refer to as "effect generators". The clip will call these effect generators to generate the
keyframes/mutators for the animation as soon as the animation needs to be executed.
Naturally, the generators have access to the closure created by the call to buildFrameGenerators
(in other words, its scope), which is useful for storing stateful data and helper functions that you can
use within the generators.
For the sake of code clarity, it is recommended that you keep a final return statement at the bottom of
buildFrameGenerators (as opposed to several possible return statements scattered throughout).
Special this
For both convenience and utility, using this inside the scope of buildFrameGenerators
gives access to a subset of useful properties and methods of the clip.
Forward Keyframes Generator
In a typical case, you will return a EffectFrameGeneratorSet containing the callback function EffectFrameGeneratorSet.keyframesGenerator_play.
When the clip is played, the callback function will be called to produce the keyframes for the animation to play. When the clip
is rewound, the same callback function will be called again to produce keyframes for the animation to play, but the direction will
be reversed. This means that every time playback is initiated (playing or rewinding), a new set of keyframes is produced.
When writing your keyframes, you must always define the full course of the effect. For example, from the forward keyframes generator,
do not return [{}, {backgroundColor: 'blue'}, {backgroundColor: 'red', opacity: '0.5'}].
Instead, store the original styles—something like const initialStyles = this.getStyles(['backgroundColor', 'opacity']);, and return
[{...initialStyles}, {backgroundColor: 'blue'}, {backgroundColor: 'red', opacity: 0.5}] (taking advantage of the fact that
initialStyles will still be accessible even after the EffectFrameGeneratorSet is returned). This ensures
that the initial styles can be restored when the clip is rewound. The helper method AnimClip.getStyles is a convenient way
to get the current style properties of an element.
Forward Mutator Generator
You can also animate JavaScript values using EffectFrameGeneratorSet.mutatorGenerator_play.
Like the keyframes generator, it is a callback function that will be called when the clip is played, and when the clip is rewound,
it will be called again with its effect being reversed this time. The difference is that instead of returning
a Keyframes, it will return a Mutator—a function that will be repeatedly called at the device's framerate.
If some JavaScript value is changed within the mutator with respect to the clip's progress, then the result is the illusion of a
smooth animation that couldn't be achieved using normal CSS-based animations (since CSS cannot animate JavaScript values).
(Under the hood, requestAnimationFrame loops are being used.)
See the documentation of EffectFrameGeneratorSet for details on how to use mutator generators.
Backward Effect Generators
In addition to the forward keyframes/mutator generators, you may also define EffectFrameGeneratorSet.keyframesGenerator_rewind and/or
EffectFrameGeneratorSet.mutatorGenerator_rewind, giving you the ability to define more complex effects. When the clip is played,
the full EffectFrameGeneratorSet will be produced. Only the forward keyframes/mutator generators will be called at first since they will be used for
playing a clip (just as before). When the clip is eventually rewound, then the backward keyframes/mutator generators will be called and
used for the animation (instead of reusing the forward generators). When the clip is eventually played again, the forward generators
will be called to produce the effect again. Then when the clip is eventually rewound again, the backward generators will once again
be called to produce the effect—so on and so forth.
Generator Rebuild Frequency
By default, buildFrameGenerators only runs the first time the clip is played, so the
resulting generators will be reused for the lifetime of the clip. To allow buildFrameGenerators
to rerun and remake the EffectFrameGeneratorSet, set PresetEffectDefinition.howOftenBuildGenerators to 'on-every-play'.
Caution with composite
Be mindful of how the value of AnimClipConfig.composite
('replace', 'add' or 'accumulate') may affect the effect when rewinding. This will less often be an issue
for entrance and exit effects since changes resulting from effects in these
categories are never committed (meaning composite-related bugs are less likely), but for motion and
emphasis effects, you should be especially cognizant of potential logic errors.
// EXAMPLES WHERE BACKWARD GENERATORS CAN BE OMITTED
const clipFactories = webchalk.createAnimationClipFactories({
additionalEmphasisEffectBank: {
// -----------------------------------------------------------------
// ----------------------------EXAMPLE 1----------------------------
// -------------------------transparencyHalf------------------------
// -----------------------------------------------------------------
transparencyHalf: {
buildFrameGenerators() {
const initialOpacity = this.getStyles('opacity');
// return EffectFrameGeneratorSet
return {
keyframesGenerator_play: () => {
// return Keyframes (Keyframe[])
return [{opacity: initialOpacity}, {opacity: 0.5}];
},
// Notice how the backward generator would be equivalent to running the forward generator
// and reversing the effect of the keyframes. That means that the forward keyframes
// generator is invertible, and the backward generator can be omitted.
keyframesGenerator_rewind: () => {
// return Keyframes (Keyframe[])
return [{opacity: 0.5}, {opacity: initialOpacity}];
},
};
},
},
// Exactly equivalent to transparencyHalf because the keyframe generator
// is invertible
transparencyHalf_shortcut: {
buildFrameGenerators() {
const initialOpacity = this.getStyles('opacity');
// return EffectFrameGeneratorSet
return {
keyframesGenerator_play: () => {
// return Keyframes (Keyframe[])
return [{opacity: initialOpacity}, {opacity: 0.5}];
},
};
},
},
},
additionalEntranceEffectBank: {
// -----------------------------------------------------------------
// ----------------------------EXAMPLE 2----------------------------
// ------------------------------shyIn------------------------------
// -----------------------------------------------------------------
// Element shyly enters, hesitantly fading and scaling in and out until it
// reaches full opacity and scale
shyIn: {
buildFrameGenerators() {
// return EffectFrameGeneratorSet
return {
keyframesGenerator_play: () => {
// return Keyframes (PropertyIndexedKeyframes)
return {
opacity: [0, 0.5, 0.1, 0.7, 0, 1],
scale: [0, 0.5, 0.1, 0.7, 0, 1],
};
},
// Notice how the backward generator would be equivalent to running the forward generator
// and reversing the effect of the keyframes. That means that the forward keyframes
// generator is invertible.
keyframesGenerator_rewind: () => {
// return Keyframes (PropertyIndexedKeyframes)
return {
opacity: [1, 0, 0.7, 0.1, 0.5, 0],
scale: [1, 0, 0.7, 0.1, 0.5, 0],
};
},
};
},
},
// Exactly equivalent to shyIn because the keyframes generator is invertible.
shyIn_shortcut: {
buildFrameGenerators() {
// return EffectFrameGeneratorSet
return {
keyframesGenerator_play: () => {
// return Keyframes (PropertyIndexedKeyframes)
return {
opacity: [0, 0.5, 0.1, 0.7, 0, 1],
scale: [0, 0.5, 0.1, 0.7, 0, 1],
};
},
};
},
},
// -----------------------------------------------------------------
// ----------------------------EXAMPLE 3----------------------------
// -----------------------riseUp and sinkDown-----------------------
// -----------------------------------------------------------------
// Replicates PowerPoint's Rise Up animation.
// Element flies in from the bottom of the screen and ends up
// slightly too high, then settles down to its final position.
riseUp: {
buildFrameGenerators() {
const belowViewportDist = () => {
return window.innerHeight - this.domElem.getBoundingClientRect().top;
};
// return frame generator set
return {
keyframesGenerator_play: () => {
// return Keyframes (Keyframe[])
return [
{
opacity: 0,
composite: 'replace'
},
{
translate: `0 ${belowViewportDist()}px`,
offset: 0,
easing: useEasing('power2-out')
},
{
translate: `0 -25px`,
offset: 0.83333
},
{
translate: `0 -25px`,
offset: 0.86,
easing: useEasing('power1-in')
},
{translate: `0 0`},
];
},
// It would be a pain to figure out what the backward keyframes should look like
// for rewinding this effect. Fortunately, the forward generator is invertible,
// (trust me—it is true) so keyframesGenerator_rewind() can be omitted.
// ---------------------------------------------------------------------------------------
// keyframesGenerator_rewind: () => {
// // return Keyframes (Keyframe[])
// return [] // ??????
// },
};
},
defaultConfig: {
composite: 'accumulate',
},
immutableConfig: {},
},
},
additionalExitEffectBank: {
// Replicates PowerPoint's Sink Down animation, which is the opposite of Rise Up.
// Element floats up slightly and then accelerates to the bottom of the screen.
sinkDown: {
buildFrameGenerators() {
const belowViewportDist = () => {
return window.innerHeight - this.domElem.getBoundingClientRect().top;
};
// return frame generator set
return {
// Most of the time, when you write your own preset entrance/exit effect, you will want
// to write the corresponding exit/entrance effect. If you write flyIn, you'll probably
// write flyOut; if you write slideOut, you'll probably write slideIn; if you write riseUp,
// you'll probably write sinkDown. The beauty is that if riseUp and sinkDown are opposites,
// then we know that playing riseUp should be the same as rewinding sinkDown. Therefore,
// we can copy-paste the logic from riseUp's keyframesGenerator_play() and simply set
// reverseKeyframesEffect to true. Once again, we have gotten
// away with just figuring out what the forward keyframes look like without having
// to figure out what the other set looks like.
// ---------------------------------------------------------------------------------------
reverseKeyframesEffect: true,
keyframesGenerator_play: () => {
// return Keyframes (Keyframe[])
return [
{
opacity: 0,
composite: 'replace'
},
{
translate: `0 ${belowViewportDist()}px`,
offset: 0,
easing: useEasing('power2-out'),
},
{
translate: `0 -25px`,
offset: 0.83333
},
{
translate: `0 -25px`,
offset: 0.86,
easing: useEasing('power1-in')
},
{translate: `0 0`},
];
},
// keyframesGenerator_rewind: () => {
// // return Keyframes (Keyframe[])
// return [] // ??????
// },
};
},
defaultConfig: {
composite: 'accumulate',
},
immutableConfig: {},
},
// -----------------------------------------------------------------
// ----------------------------EXAMPLE 4----------------------------
// ----------------------------flyOutLeft---------------------------
// -----------------------------------------------------------------
// a preset animation effect for flying out to the left side of the screen
// while displaying the percentage progress in the element's text content
flyOutLeft: {
buildFrameGenerators() {
const computeTranslationStr = () => {
const orthogonalDistance = -(this.domElem.getBoundingClientRect().right);
const translationString = `${orthogonalDistance}px 0px`;
return translationString;
}
// return EffectFrameGeneratorSet
return {
keyframesGenerator_play: () => {
// return Keyframes (Keyframe[])
return [
{translate: computeTranslationStr()}
];
},
// Notice how the backward generator would be equivalent to running the forward generator
// and reversing the effect of the keyframes (even though the composite value is
// 'accumulate', it's still invertible because exit effects' changes are never committed).
// That means that the forward keyframes generator is invertible.
// --------------------------------------------------------------------------------------
keyframesGenerator_rewind: () => {
// return Keyframes (Keyframe[])
return [
{translate: computeTranslationStr()},
{translate: `0 0`}
];
},
mutatorGenerator_play: () => {
// return Mutator
return () => {
this.domElem.textContent = `${this.computeTween(0, 100)}%`;
};
},
// Notice how the backward generator would be equivalent to running the forward generator
// and reversing the effect of the mutator. That means that the mutator generator is
// invertible. (Note that it may not always be the case that BOTH the keyframes
// generators and the forward mutator generator are invertible).
// --------------------------------------------------------------------------------------
mutatorGenerator_rewind: () => {
// return Mutator
return () => {
this.domElem.textContent = `${this.computeTween(100, 0)}%`;
};
},
};
},
defaultConfig: {
duration: 1000,
easing: "ease-in",
},
immutableConfig: {
// this means that the translation is added onto the element's position
// instead of replacing it
composite: 'accumulate',
},
},
// Exactly equivalent to flyOutLeft
flyOutLeft_shortcut: {
buildFrameGenerators() {
const computeTranslationStr = () => {
const orthogonalDistance = -(this.domElem.getBoundingClientRect().right);
const translationString = `${orthogonalDistance}px 0px`;
return translationString;
}
// return EffectFrameGeneratorSet
return {
keyframesGenerator_play: () => {
// return Keyframes (Keyframe[])
return [
{translate: computeTranslationStr()}
];
},
mutatorGenerator_play: () => {
// return Mutator
return () => {
this.domElem.textContent = `${this.computeTween(0, 100)}%`;
};
},
};
},
defaultConfig: {
duration: 1000,
easing: "ease-in",
},
immutableConfig: {
composite: 'accumulate',
},
},
},
});
// EXAMPLES WHERE BACKWARD GENERATORS CANNOT BE OMITTED
const clipFactories = webchalk.createAnimationClipFactories({
additionalMotionEffectBank: {
// a preset animation effect for translating a certain number of pixels to the right
translateRight: {
buildFrameGenerators(numPixels: number) {
// a helper function you wrote that will exist within a closure scoped to buildFrameGenerators()
const createTranslationString = () => {
if (numPixels <= 0) { throw RangeError(`Number of pixels must exceed 0.`) }
const translationString = `${numPixels}px`;
return translationString;
}
// return EffectFrameGeneratorSet
return {
keyframesGenerator_play: () => {
// return Keyframes (Keyframe][])
return [
{translate: createTranslationString()} // Keyframe
];
},
// keyframesGenerator_rewind() must be specified because reversing the keyframes produced
// by keyframesGenerator_play() would not have the intended effect (due to
// {composite: 'accumulate'}, trying to simply use the reversal of
// {translate: createTranslationString()} from keyframesGenerator_play() would actually
// cause the target element to jump an additional numPixels pixels to the right
// before sliding left, which is not the intended rewinding effect).
keyframesGenerator_rewind: () => {
// return Keyframes (Keyframe[])
return [
{translate: '-'+createTranslationString()}, // Keyframe
];
}
};
},
immutableConfig: {
// this means that the translation is added onto the element's position
// instead of replacing it
composite: 'accumulate',
},
},
// a preset animation effect for scrolling to a specific point on the page.
scrollTo: {
buildFrameGenerators(yPosition: number) {
const initialPosition = this.domElem.scrollTop;
// return EffectFrameGeneratorSet
return {
// The mutation is to use the scrollTo() method on the element.
// Thanks to computeTween(), there will be a smooth scroll
// from initialPosition to yPosition
mutatorGenerator_play: () => {
// return Mutator
return () => {
this.domElem.scrollTo({
top: this.computeTween(initialPosition, yPosition),
behavior: 'instant'
});
};
},
// The forward mutation loop is not invertible because reversing it requires
// re-computing the element's scroll position at the time of rewinding
// (which may have since changed for any number of reasons, including user
// scrolling, size changes, etc.). So we must define mutatorGenerator_rewind()
// to do exactly that.
mutatorGenerator_rewind: () => {
// return Mutator
return () => {
const currentPosition = this.domElem.scrollTop;
this.domElem.scrollTo({
top: this.computeTween(currentPosition, initialPosition),
behavior: 'instant'
});
};
}
};
},
},
}
});
ReadonlylengthReadonlynameReturns the name of the function. Function names are read-only and can not be changed.
Determines whether the given value inherits from this function if this function was used as a constructor function.
A constructor function can control which objects are recognized as its instances by 'instanceof' by overriding this method.
Calls the function, substituting the specified object for the this value of the function, and the specified array for the arguments of the function.
The object to be used as the this object.
OptionalargArray: anyA set of arguments to be passed to the function.
For a given function, creates a bound function that has the same body as the original function. The this object of the bound function is associated with the specified object, and has the specified initial parameters.
An object to which the this keyword can refer inside the new function.
A list of arguments to be passed to the new function.
Calls a method of an object, substituting another object for the current object.
The object to be used as the current object.
A list of arguments to be passed to the method.
Returns a string representation of a function.
Object representing an entry in a PresetEffectBank. It contains