webchalk-animate - v0.31.0
    Preparing search index...

    Class TextEditorClip<TPresetEffectDefinition>

    Used to edit the text contents of an element.

    A "clip" is the smallest building block of a timeline. It is essentially a [DOM element, effect] pair, where a "DOM element" is some HTML element on the page and the effect is the animation effect that will be applied to it (asynchronously).

    The AnimClip class is abstract, meaning it cannot be instantiated. But it has several subclasses such as EntranceClip, MotionClip, TransitionClip, etc. Webchalk provides convenient factory functions that can be used to create such clips—the factory functions can be obtained from Webchalk.createAnimationClipFactories. Examples are shown below.

    Generally (with some exceptions), using a clip factory function follows this format: const clip = <factory func>(<some element>, <effect name>, [<effect options>], {<optional clip configuration>});

    // retrieve connector entrance clip factory function;
    const { TextEditor } = webchalk.createAnimationClipFactories();

    // select elements containing text from the DOM
    const textbox = document.querySelector('.some-text-box');
    const explanation = document.querySelector('.explanation');
    const paragraphs = document.querySelector('.details');
    const letters = document.querySelector('.letters');

    // A = element, B = effect name, C = effect options, D = configuration (optional)

    // create connector entrance clips using factory function
    // A B C D
    const clip1 = TextEditor(textbox, '~delete-text', [{match: /I hate this class!/, findAllMatches: true}], {durationOrRate: '400wpm'});
    // A B C D
    const clip2 = TextEditor(explanation, '~insert-text', ['Hello world!', {position: 'after'}], {durationOrRate: 2000});
    // A B C D
    const clip3 = TextEditor(paragraphs, '~replace-text', ['Bob', {match: 'Jimmy', findAllMatches: true}], {durationOrRateInsertion: '200wpm'});
    // A B C
    const clip4 = TextEditor(letters, '~replace-text', [['a', 'b', 'c'], {match: /Let's use (x), (y), and (z)./}]);

    // play clips (all will play at the same time because they are asynchronous)
    clip1.play(); // in textbox, finds and deletes all substrings that match string 'I hate this class!'
    clip2.play(); // in explanation, inserts string 'Hello world!' at the end of the text
    clip3.play(); // in paragraphs, finds and replaces all substrings that match 'Jimmy' with 'Bob'
    clip4.play(); // in letters, finds the substring that matches regex /Let's use x, y, and z/ and...
    // ... replaces 'x' with 'a', 'y' with 'b', and 'z' with 'c'

    Type Parameters

    Hierarchy (View Summary)

    Index

    Structure

    domElem: DOMElement

    The DOM element that is to be animated.

    • get root(): | AnimClip<
          Readonly<
              StripDuplicateMethodAutocompletion<
                  {
                      defaultConfig?: Partial<unknown> & object;
                      howOftenBuildGenerators?: "on-first-play-only" | "on-every-play";
                      immutableConfig?: Partial<unknown> & object;
                      buildFrameGenerators(
                          this: Readonly<
                              Pick<
                                  (
                                      AnimClip<Readonly<StripDuplicateMethodAutocompletion<{ defaultConfig?: (Partial<unknown> & object) | undefined; immutableConfig?: (Partial<unknown> & object) | undefined; howOftenBuildGenerators?: "on-first-play-only" | "on-every-play" | undefined; buildFrameGenerators(this: Readonly<Pick<AnimClip<Readonly<StripDuplicateMethodAutocompletion<any>>, AnimClipConfig>, "computeTween">>, ...effectOptions: unknown[]): EffectFrameGeneratorSet; }>>, AnimClipConfig>
                                  ),
                                  "computeTween",
                              >,
                          >,
                          ...effectOptions: unknown[],
                      ): EffectFrameGeneratorSet;
                  },
              >,
          >,
          AnimClipConfig,
      >
      | AnimSequence
      | AnimTimeline

      The highest level of this clip's lineage.

      • If the clip is nested within an AnimTimeline: that timeline,
      • Else, if the clip is within an AnimSequence: that sequence,
      • Else: the clip itself

      Returns
          | AnimClip<
              Readonly<
                  StripDuplicateMethodAutocompletion<
                      {
                          defaultConfig?: Partial<unknown> & object;
                          howOftenBuildGenerators?: "on-first-play-only" | "on-every-play";
                          immutableConfig?: Partial<unknown> & object;
                          buildFrameGenerators(
                              this: Readonly<
                                  Pick<
                                      (
                                          AnimClip<Readonly<StripDuplicateMethodAutocompletion<{ defaultConfig?: (Partial<unknown> & object) | undefined; immutableConfig?: (Partial<unknown> & object) | undefined; howOftenBuildGenerators?: "on-first-play-only" | "on-every-play" | undefined; buildFrameGenerators(this: Readonly<Pick<AnimClip<Readonly<StripDuplicateMethodAutocompletion<any>>, AnimClipConfig>, "computeTween">>, ...effectOptions: unknown[]): EffectFrameGeneratorSet; }>>, AnimClipConfig>
                                      ),
                                      "computeTween",
                                  >,
                              >,
                              ...effectOptions: unknown[],
                          ): EffectFrameGeneratorSet;
                      },
                  >,
              >,
              AnimClipConfig,
          >
          | AnimSequence
          | AnimTimeline

    Properties

    animation: WebchalkAnimation
    direction: "forward" | "backward" = 'forward'
    effectFrameGeneratorSetMetadata: {
        bFramesMirrored: boolean;
        bRafMirrored: boolean;
        noKeyframes: boolean;
        noRaf: boolean;
    } = ...
    effectName: string
    effectOptions: Parameters<TPresetEffectDefinition["buildFrameGenerators"]> = ...
    firstRun: boolean = true
    generateError: ClipErrorGenerator = ...
    id: number

    A number that uniquely identifies the clip from other clips. Automatically generated.

    inProgress: boolean = false
    isFinished: boolean = false
    isPaused: boolean = false
    isRunning: boolean = false
    nestedEffectFrameGeneratorSetMetadataArray: {
        bFramesMirrored: boolean;
        bRafMirrored: boolean;
        noKeyframes: boolean;
        noRaf: boolean;
    }[] = []
    presetEffectDefinition: TPresetEffectDefinition
    timescaleType: "duration" | "rate" = 'duration'
    MIN_DURATION: number = 0.01

    Property Getter Methods

    • Returns an object containing the specified style properties of the specified element.

      • Normal CSS properties must be written in camelCase (e.g., ['marginBottom', 'backgroundColor'], NOT ['margin-bottom', 'background-color']),
      • CSS variables should be written normally (e.g., ['--nav-edge-color', '--brand-red']).

      Parameters

      • element: Element

        The DOM element from which to read the styles.

      • styleProps: StyleProperty[]

        An array of strings representing camelCase CSS property names.

      Returns { [key: string]: string }

      An object where the keys are the specified camelCase strings and the values are the CSS property values.

    • Returns the string value of the specified CSS property name for the specified element.

      • Normal CSS properties must be written in camelCase (e.g., 'marginBottom', NOT 'margin-bottom'),
      • CSS variables should be written normally (e.g., '--brand-red').

      Parameters

      Returns string

      The string value of the specified camelCase CSS property name.

    • Returns an object containing the specified style properties of this clip's DOM element.

      • Normal CSS properties must be written in camelCase (e.g., ['marginBottom', 'backgroundColor'], NOT ['margin-bottom', 'background-color']),
      • CSS variables should be written normally (e.g., ['--nav-edge-color', '--brand-red']).

      Parameters

      • styleProps: StyleProperty[]

        An array of strings representing camelCase CSS property names.

      Returns { [key: string]: string }

      An object where the keys are the specified camelCase strings and the values are the CSS property values.

    • Returns the string value of the specified CSS property name for this clip's DOM element.

      • Normal CSS properties must be written in camelCase (e.g., 'marginBottom', NOT 'margin-bottom'),
      • CSS variables should be written normally (e.g., '--brand-red').

      Parameters

      Returns string

      The string value of the specified camelCase CSS property name.

    Accessors

    Playback Methods

    Timing Event Methods

    • Returns a Promise that is resolved when the animation clip reaches the specified time in the specified direction.

      Parameters

      • direction: "forward" | "backward"

        The direction the animation will be going when the Promise is resolved.

      • phase: "delayPhase" | "activePhase" | "endDelayPhase" | "whole"

        The phase of the animation where the Promise will be resolved.

      • timePosition: number | "end" | `${number}%` | "beginning"

        The time position within the phase when the Promise will be resolved.

      Returns Promise<void>

      A promise that is resolved at the specific time point of the animation.

      async function testFunc() {
      const { Entrance } = webchalk.createAnimationClipFactories();
      const square = document.querySelector('.square');
      const ent = Entrance(square, '~fade-in', []);
      // wait until ent is played and gets 1/5 of the way through the active phase of the animation
      await ent.generatePromise('forward', 'activePhase', '20%');
      console.log('1/5 done playing!');
      }

      testFunc();
      async function testFunc() {
      const { Entrance } = webchalk.createAnimationClipFactories();
      const square = document.querySelector('.square');
      const ent = Entrance(square, '~fade-in', []);
      // wait until ent is eventually rewound and gets 4/5 of the way through rewinding the active phase of the animation
      await ent.generatePromise('backward', 'activePhase', '20%');
      console.log('4/5 done rewinding!');
      }

      testFunc();
    • Pauses the animation clip when it reaches the specified time and performs the specified task, unpausing once the task is complete.

      • If the clip is part of a structure (like a sequence), the entire structure is paused as well.

      Parameters

      • phase: "delayPhase" | "activePhase" | "endDelayPhase" | "whole"

        The phase of the animation to place the blocks in.

      • timePosition: number | "end" | `${number}%` | "beginning"

        The time position within the phase when the task should be performed.

      • task: ScheduledTask

        An object that contains the functions that should be called when timePosition is reached.

      • schedulingOptions: { frequencyLimit?: number } = {}

        An options object defining the behavior of the scheduling.

        • OptionalfrequencyLimit?: number

          The maximum number of times the task can be performed.

          Infinity
          

      Returns string

      The string id (auto-generated) referring to the task and its spot in the schedule.

      async function wait(milliseconds: number) { // Promise-based timer
      return new Promise(resolve => setTimeout(resolve, milliseconds));
      }

      const square = document.querySelector('.square');
      const { Entrance } = webchalk.createAnimationClipFactories();
      const ent = Entrance(square, '~fade-in', [], {endDelay: 1500});

      // adds 1 task that will pause the clip for 2 seconds once the clip is 15% through the active phase
      ent.scheduleTask('activePhase', '15%', {onPlay: () => wait(2000)});
      // adds 1 more task at the same point that will pause the clip for 3 seconds.
      ent.scheduleTask('activePhase', '15%', {onPlay: () => wait(3000)});
      // adds 1 task at 40% into the endDelay phase that will...
      // ... log 'HELLO' if the clip is playing forward
      // ... log 'WORLD' if the clip is rewinding
      ent.scheduleTask('endDelayPhase', '40%', {
      onPlay: () => console.log('HELLO'),
      onRewind: () => console.log('WORLD')
      }, {frequencyLimit: 2});

      (async () => {
      // 1) First play
      await ent.play();
      // ↑
      // Once ent is 15% through the active phase, it will pause and handle its scheduled tasks.
      // -- "wait(2000)" resolves after 2 seconds.
      // -- "wait(3000)" resolves after 3 seconds.
      // There are no more tasks at this point, so playback is resumed.
      // Once ent is 40% through the endDelay phase, it will pause and handle its tasks
      // -- 'HELLO' is logged to the console
      // There are no more tasks at this point, so playback is resumed.

      // 2) First rewind
      await ent.rewind();
      // ↑
      // Once ent rewinds back to the 40% point of the endDelay phase, it will pause and...
      // ... handle its scheduled tasks
      // -- 'WORLD' is logged to the console
      // There are no more tasks at this point, so playback is resumed.

      // 3) Second play
      await ent.play();
      // ↑
      // Once ent is 15% through the active phase, it will pause and handle its scheduled tasks.
      // -- "wait(2000)" resolves after 2 seconds.
      // -- "wait(3000)" resolves after 3 seconds.
      // There are no more tasks at this point, so playback is resumed.
      // Once ent is 40% through the endDelay phase, it will pause and handle its tasks
      // -- 'HELLO' is logged to the console
      // -- -- Since the frequency limit was 2, this subtask is removed
      // There are no more tasks at this point, so playback is resumed.

      // 4) Second rewind
      await ent.rewind();
      // ↑
      // Once ent rewinds back to the 40% point of the endDelay phase, it will pause and...
      // ... handle its scheduled tasks
      // -- 'WORLD' is logged to the console
      // -- -- Since the frequency limit was 2, this subtask is removed
      // There are no more tasks at this point, so playback is resumed.

      // 5) Third play
      await ent.play();
      // ↑
      // Once ent is 15% through the active phase, it will pause and handle its scheduled tasks.
      // -- "wait(2000)" resolves after 2 seconds.
      // -- "wait(3000)" resolves after 3 seconds.
      // There are no more tasks at this point, so playback is resumed.

      // 6) Third rewind
      await ent.rewind();
      // ↑ No scheduled tasks, so playback runs uninterrupted
      })();

    Configuration

    config: TextEditorClipConfig = ...
    • get categoryDefaultConfig(): {
          commitsStyles: true;
          composite: "accumulate";
          cssClasses: {
              toAddOnFinish: [];
              toAddOnStart: [];
              toRemoveOnFinish: [];
              toRemoveOnStart: [];
          };
          delay: 0;
          duration: number;
          durationOrRate: "500wpm";
          durationOrRateDeletion: number;
          durationOrRateInsertion: "500wpm";
          easing: "linear";
          endDelay: 0;
          playbackRate: 1;
          startsNextClipToo: false;
          startsWithPrevious: false;
      }

      The default configuration for clips in a specific effect category, which includes any additional configuration options that are specific to the effect category.

      • This never changes, and it available mostly just for reference. Consider it a static property.
      • This does NOT include any default configuration from preset effect definitions or configurations passed in from clip factory functions.

      Returns {
          commitsStyles: true;
          composite: "accumulate";
          cssClasses: {
              toAddOnFinish: [];
              toAddOnStart: [];
              toRemoveOnFinish: [];
              toRemoveOnStart: [];
          };
          delay: 0;
          duration: number;
          durationOrRate: "500wpm";
          durationOrRateDeletion: number;
          durationOrRateInsertion: "500wpm";
          easing: "linear";
          endDelay: 0;
          playbackRate: 1;
          startsNextClipToo: false;
          startsWithPrevious: false;
      }

    • get categoryImmutableConfig(): {
          composite: "accumulate";
          duration: number;
          easing: "linear";
      }

      The unchangeable default configuration for clips in a specific effect category.

      • This never changes, and it is available mostly just for reference. Consider it a static property.
      • This does NOT include any immutable configuration from preset effect definitions.

      Returns { composite: "accumulate"; duration: number; easing: "linear" }

    • get immutableConfig(): this["categoryImmutableConfig"] & TPresetEffectDefinition["immutableConfig"]

      All the unchangeable default configuration settings for the clip (both category-specific immutable configurations and immutable configurations that come from the specific preset effect definition).

      Returns this["categoryImmutableConfig"] & TPresetEffectDefinition["immutableConfig"]

    • get baseDefaultConfig(): {
          commitsStyles: true;
          composite: "replace";
          cssClasses: {
              toAddOnFinish: [];
              toAddOnStart: [];
              toRemoveOnFinish: [];
              toRemoveOnStart: [];
          };
          delay: 0;
          duration: 500;
          easing: "linear";
          endDelay: 0;
          playbackRate: 1;
          startsNextClipToo: false;
          startsWithPrevious: false;
      }

      The base default configuration for any animation clip before any category-specific configuration, preset effect definition configuration, or configuration passed in through clip factory functions are applied.

      Returns {
          commitsStyles: true;
          composite: "replace";
          cssClasses: {
              toAddOnFinish: [];
              toAddOnStart: [];
              toRemoveOnFinish: [];
              toRemoveOnStart: [];
          };
          delay: 0;
          duration: 500;
          easing: "linear";
          endDelay: 0;
          playbackRate: 1;
          startsNextClipToo: false;
          startsWithPrevious: false;
      }

    Helper Methods

    • Calculates the value partway between two fixed numbers (an initial value and a final value) based on the progress of the animation.

      Parameters

      • initialVal: number

        The starting value.

      • finalVal: number

        The ending value.

      Returns number

      The number that is a percentage of the way between initialVal and finalVal based on the percentage of completion of the animation (playing or rewinding).

      const {Entrance} = webchalk.createAnimationClipFactories({
      additionalEntranceEffectBank: {
      rotate: {
      buildFrameGenerators(degrees: number) {
      return {
      // when playing, keep computing the value between 0 and 'degrees'
      mutatorGenerator_play: () => () => { this.domElem.style.rotate = this.computeTween(0, degrees)+'deg'; },
      // when rewinding, keep computing the value between 'degrees' and 0
      mutatorGenerator_rewind: () => () => { this.domElem.style.rotate = this.computeTween(degrees, 0)+'deg'; }
      };
      }
      }
      },
      });

      const someElement = document.querySelector('.some-element');

      (async () => {
      await Entrance(someElement, 'rotate', [360], {duration: 2000}).play();
      // ↑ At 1.5 seconds (or 1500ms), the animation is 1.5/2 = 75% done playing.
      // Thus, computeTween(0, 360) at that exactly moment would...
      // return the value 75% of the way between 0 and 360 (= 270).
      // Therefore, at 1.5 seconds of playing, someElement's rotation is set to "270deg".

      await Entrance(someElement, 'rotate', [360], {duration: 2000}).rewind();
      // ↑ At 0.5 seconds (or 500ms), the animation is 0.5/2 = 25% done rewinding.
      // Thus, computeTween(360, 0) at that exactly moment would...
      // return the value 25% of the way between 360 and 0 (= 270).
      // Therefore, at 0.5 seconds of rewinding, someElement's rotation is set to "270deg".
      })();
    • Returns Readonly<
          StripDuplicateMethodAutocompletion<
              {
                  defaultConfig?: Partial<unknown> & object;
                  howOftenBuildGenerators?: "on-first-play-only" | "on-every-play";
                  immutableConfig?: Partial<unknown> & object;
                  buildFrameGenerators(
                      this: Readonly<
                          Pick<
                              AnimClip<
                                  (
                                      Readonly<StripDuplicateMethodAutocompletion<{ defaultConfig?: (Partial<unknown> & object) | undefined; immutableConfig?: (Partial<unknown> & object) | undefined; howOftenBuildGenerators?: "on-first-play-only" | "on-every-play" | undefined; buildFrameGenerators(this: Readonly<Pick<AnimClip<Readonly<StripDuplicateMethodAutocompletion<any>>, AnimClipConfig>, "computeTween">>, ...effectOptions: unknown[]): EffectFrameGeneratorSet; }>>
                                  ),
                                  AnimClipConfig,
                              >,
                              "computeTween",
                          >,
                      >,
                      ...effectOptions: unknown[],
                  ): EffectFrameGeneratorSet;
              },
          >,
      >

      An effect definition with a function that returns empty arrays (so no actual keyframes).

      This static method is purely for convenience.

    Methods