chromium/chrome/browser/resources/compose/animations/animator.ts

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import {assert} from '//resources/js/assert.js';

export const STANDARD_EASING = 'cubic-bezier(0.2, 0.0, 0, 1.0)';

export const EMPHASIZED_DECELERATE = 'cubic-bezier(0.05, 0.7, 0.1, 1.0)';

/**
 * Generic animator class that has common animations and util methods.
 */
export class Animator {
  private root_: HTMLElement;
  private animationsEnabled_: boolean;

  constructor(root: HTMLElement, animationsEnabled: boolean) {
    this.root_ = root;
    this.animationsEnabled_ = animationsEnabled;
  }

  getElement(selector: string): HTMLElement {
    const element = this.root_.shadowRoot!.querySelector(selector);
    assert(element);
    return element as HTMLElement;
  }

  animate(
      selector: string, keyframes: Keyframe[],
      options: KeyframeAnimationOptions,
      meetsCondition: boolean = true): Animation[] {
    if (!this.animationsEnabled_ || !meetsCondition) {
      return [];
    }

    const elements = Array.from(
        this.root_.shadowRoot!.querySelectorAll<HTMLElement>(selector));
    assert(elements.length > 0);
    return elements.map(element => {
      return element.animate(
          keyframes, Object.assign({fill: 'backwards'}, options));
    });
  }

  fadeIn(selector: string, options: KeyframeAnimationOptions): Animation[] {
    return this.animate(
        selector,
        [
          {opacity: 0},
          {opacity: 1},
        ],
        Object.assign({easing: 'linear'}, options));
  }

  fadeOut(selector: string, options: KeyframeAnimationOptions): Animation[] {
    return this.animate(
        selector,
        [
          {opacity: 1},
          {opacity: 0},
        ],
        Object.assign({easing: 'linear'}, options));
  }

  /* Fades out an element and then sets the 'display' of the element to 'none'.
   * This is useful for animations that result in an element and its children
   * becoming [hidden]. */
  fadeOutAndHide(
      selector: string, beforeDisplay: string,
      options: KeyframeAnimationOptions): Animation[] {
    return this.animate(
        selector,
        [
          {display: beforeDisplay, opacity: 1},
          {display: 'none', opacity: 0},
        ],
        Object.assign({easing: 'linear'}, options));
  }

  /* Maintains a style for a duration on an element. This is useful for
   * animations that require a fixed state (such as fixed heights). */
  maintainStyles(
      selector: string, styles: Keyframe, options: KeyframeAnimationOptions) {
    return this.animate(selector, [styles, styles], options);
  }

  scaleIn(selector: string, options: KeyframeAnimationOptions): Animation[] {
    return this.animate(
        selector,
        [
          {transform: 'scale(0)'},
          {transform: 'scale(1)'},
        ],
        Object.assign({easing: STANDARD_EASING}, options));
  }

  slideIn(
      selector: string, startDistance: number,
      options: KeyframeAnimationOptions): Animation[] {
    return this.animate(
        selector,
        [
          {transform: `translateY(${startDistance}px)`},
          {transform: `translateY(0)`},
        ],
        Object.assign({easing: STANDARD_EASING}, options));
  }

  slideOut(
      selector: string, endDistance: number,
      options: KeyframeAnimationOptions): Animation[] {
    return this.animate(
        selector,
        [
          {transform: `translateY(0)`},
          {transform: `translateY(${endDistance}px)`},
        ],
        Object.assign({easing: STANDARD_EASING}, options));
  }
}