Description Animation component
Required Script <script async custom-element="amp-animation" src="https://cdn.ampproject.org/v0/amp-animation-0.1.js"></script>
Supported Layouts nodisplay

Overview

AMP Animations rely on Web Animations API to define and run animations in AMP documents.

Format

An amp-animation element defines such an animation as a JSON structure.

Top-level animation specification

The top-level object defines an overall animation process which consists of an arbitrary number of animation components defined as an animations array:

<amp-animation layout="nodisplay">
<script type="application/json">
{
  // Timing properties
  ...
  "animations": [
    {
      // Animation 1
    },
    ...
    {
      // Animation N
    }
  ]
}
</script>
</amp-animation>

Placement in DOM

Initially, <amp-animation> is only allowed to be placed as a direct child of <body> element. This restriction will be removed in the near future.

Animation component

Each animation component is a keyframes effect and is comprised of:

  • Target element(s) referenced by a selector
  • Conditions: media query and supports condition
  • Timing properties
  • Keyframes
{
  "selector": "#target-id",
  // Conditions
  // Variables
  // Timing properties
  // Subtargets
  ...
  "keyframes": []
}

Conditions

Conditions can specify whether this animation component is included in the final animation. Currently, only media expression is supported.

Media query

Media query can be specified using the media property. This property can contain any expression allowed for Window.matchMedia API and corresponds to @media CSS rule.

If value is specified for an animation component, the animation component will only be included if the media query will match the current environment.

Supports condition

Supports condition can be specified using the supports property. This property can contain any expression allowed for CSS.supports API and corresponds to @supports CSS rule.

If value is specified for an animation component, the animation component will only be included if the supports condition will match the current environment.

Animation switch statement

In some cases it's convenient to combine multiple conditional animations with an optional default into a single animation. This can be done using switch animation statement in this format:

{
  // Optional selector, vars, timing
  ...
  "switch": [
    {
      "media": "(min-width: 320px)",
      "keyframes": {...},
    },
    {
      "supports": "offset-distance: 0",
      "keyframes": {...},
    },
    {
      // Optional default: no conditionals
    }
  ]
}

In switch animation, the candidates are evaluated in the defined order and the first animation that matches conditional statements is executed and the rest are ignored.

For instance, this animation runs motion-path animation if supported and falls back to transform:

{
  "selector": "#target1",
  "duration": "1s",
  "switch": [
    {
      "supports": "offset-distance: 0",
      "keyframes": {
        "offsetDistance": [0, '300px']
      }
    },
    {
      "keyframes": {
        "transform": [0, '300px']
      }
    }
  ]
}

Variables

An animation component can declare CSS variables that will be used for timing and keyframes values via var() expressions. var() expressions are evaluated using the current target context. The CSS variables specified in animation components are propagated to nested animations, applied to animation targets and thus override CSS variables used in final animations.

For instance:

<amp-animation layout="nodisplay">
<script type="application/json">
{
  "--delay": "0.5s",
  "--x": "100px",
  "animations": [
    {
      "selector": "#target1",
      "delay": "var(--delay)",
      "--x": "150px",
      "keyframes": {"transform": "translate(var(--x), var(--y, 0px)"}
    },
    ...
  ]
}
</script>
</amp-animation>

In this sample:

  • --delay is propagated into nested animations and used as a delay of #target1 animation.
  • --x is propagated into nested animations but overriden by the #target1 animation and later used for transform property.
  • --y is not specified anywhere in the <amp-animation> and thus will be queried on the #target1 element. It defaults to 0px if not defined in CSS either.

For more information on var(), see the var() and calc() section.

Timing properties

Top-level animation and animation components may contain timing properties. These properties are defined in detail in the AnimationEffectTimingProperties of the Web Animation spec. The set of properties allowed here includes:

Property Type Default Description
duration time 0 The animation duration. Either a numeric value in milliseconds or a CSS time value, e.g. `2s`.
delay time 0 The delay before animation starts executing. Either a numeric value in milliseconds or a CSS time value, e.g. `2s`.
endDelay time 0 The delay after the animation completes and before it's actually considered to be complete. Either a numeric value in milliseconds or a CSS time value, e.g. `2s`.
iterations number or
"Infinity" or
"infinite"
1 The number of times the animation effect repeats.
iterationStart number/CSS 0 The time offset at which the effect begins animating.
easing string "linear" The timing function used to scale the time to produce easing effects.
direction string "normal" One of "normal", "reverse", "alternate" or "alternate-reverse".
fill string "none" One of "none", "forwards", "backwards", "both", "auto".

All timing properties allow either a direct numeric/string values or CSS values. For instance, "duration" can be specified as 1000 or 1s or 1000ms. In addition, calc() and var() and other CSS expressions are also allowed.

An example of timing properties in JSON:

{
  ...
  "duration": "1s",
  "delay": 100,
  "endDelay": "var(--end-delay, 10ms)",
  "easing": "ease-in",
  "fill": "both"
  ...
}

Animation components inherit timing properties specified for the top-level animation.

Subtargets

Everywhere where selector can be specified, it's possible to also specify subtargets: []. Subtargets can override timing properties or variables defined in the animation for specific subtargets indicated via either an index or a CSS selector.

For instance:

{
  "selector": ".target",
  "delay": 100,
  "--y": "100px",
  "subtargets": [
    {
      "index": 0,
      "delay": 200,
    },
    {
      "selector": ":nth-child(2n+1)",
      "--y": "200px"
    }
  ]
}

In this example, by default all targets matched by the ".target" have delay of 100ms and "--y" of 100px. However, the first target (index: 0) is overriden to have delay of 200ms; and odd targets are overriden to have "--y" of 200px.

Notice, that multiple subtargets can match one target element.

Keyframes

Keyframes can be specified in numerous ways described in the keyframes section of the Web Animations spec or as a string refering to the @keyframes name in the CSS.

Some typical examples of keyframes definitions are below.

Shorthand object-form "to" format specifies the final state at 100%:

{
  "keyframes": {"opacity": 0, "transform": "scale(2)"}
}

Shorthand object-form "from-to" format specifies the starting and final states at 0 and 100%:

{
  "keyframes": {
    "opacity": [1, 0],
    "transform": ["scale(1)", "scale(2)"]
  }
}

Shorthand object-form "value-array" format specifies multiple values for starting, final states and multiple (equal-spaced) offsets:

{
  "keyframes": {
    "opacity": [1, 0.1, 0],
    "transform": ["scale(1)", "scale(1.1)", "scale(2)"]
  }
}

The array-form specifies keyframes. Offsets are assigned automatically at 0, 100% and spaced evenly in-between:

{
  "keyframes": [
    {"opacity": 1, "transform": "scale(1)"},
    {"opacity": 0, "transform": "scale(2)"}
  ]
}

The array-form can also include "offset" explicitly:

{
  "keyframes": [
    {"opacity": 1, "transform": "scale(1)"},
    {"offset": 0.1, "opacity": 0.1, "transform": "scale(2)"},
    {"opacity": 0, "transform": "scale(3)"}
  ]
}

The array-form can also include "easing":

{
  "keyframes": [
    {"easing": "ease-out", "opacity": 1, "transform": "scale(1)"},
    {"opacity": 0, "transform": "scale(2)"}
  ]
}

For additional keyframes formats refer to Web Animations spec.

The property values allow any valid CSS values, including calc(), var() and other CSS expressions.

Keyframes from CSS

Another way to specify keyframes is in the document's stylesheet (<style> tag) as @keyframes CSS rule. For instance:

<style amp-custom>
  @keyframes keyframes1 {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
</style>

<amp-animation layout="nodisplay">
<script type="application/json">
{
  "duration": "1s",
  "keyframes": "keyframes1"
}
</script>
</amp-animation>

CSS @keyframes are mostly equivalent to inlining keyframes definition in the JSON per Web Animations spec. However, there are some nuances:

  • For broad-platform support, vendor prefixes, e.g. @-ms-keyframes {} or -moz-transform may be needed. Vendor prefixes are not needed and not allowed in the JSON format, but in CSS they could be necessary.
  • Platforms that do not support calc() and var() will not be able to take advantage of amp-animation polyfills when keyframes are specified in CSS. It's thus recommended to always include fallback values in CSS.
  • CSS extensions such as width(), height(), num(), rand() and index() cannot be used in CSS.

Whitelisted properties for keyframes

Not all CSS properties can be used in keyframes. Only CSS properties that modern browsers can optimize and animate quickly are whitelisted. This list will grow as more properties are confirmed to provide good performance. Currently the list contains:

  • opacity
  • transform
  • visibility
  • 'offsetDistance'

Notice that the use of vendor prefixed CSS properties is neither needed nor allowed.

Abbreviated forms of animation configuration

If the animation only involves a single element and a single keyframes effect is sufficient, the configuration can be reduced to this one animation component only. For instance:

<amp-animation layout="nodisplay">
<script type="application/json">
{
  "selector": "#target-id",
  "duration": "1s",
  "keyframes": {"opacity": 1}
}
</script>
</amp-animation>

If the animation is comprised of a list of components, but doesn't have top-level animation, the configuration can be reduced to an array of components. For instance:

<amp-animation layout="nodisplay">
<script type="application/json">
[
  {
    "selector": ".target-class",
    "duration": 1000,
    "keyframes": {"opacity": 1}
  },
  {
    "selector": ".target-class",
    "duration": 600,
    "delay": 400,
    "keyframes": {"transform": "scale(2)"}
  }
]
</script>
</amp-animation>

Animation composition

Animations can reference other animations thus combining several amp-animation declarations into a single final animation. Referencing an animation from another animation is mostly the same as nesting. The reason why one would want to split animations into different elements would be to reuse the same animation from several places or to simply make each animation declaration smaller and more manageable.

For instance:

<amp-animation id="anim1" layout="nodisplay">
<script type="application/json">
{
  "animation": "anim2",
  "duration": 1000,
  "--scale": 2
}
</script>
</amp-animation>

<amp-animation id="anim2" layout="nodisplay">
<script type="application/json">
{
  "selector": ".target-class",
  "keyframes": {"transform": "scale(var(--scale))"}
}
</script>
</amp-animation>

This sample animation, will combine "anim2" animation as part of "anim1". The "anim2" is included without a target (selector). In such case, the included animation is expected to reference its own target.

Another form allows the including animation to provide the target or multiple targets. In that case, the included animation is executed for each matched target. For instance:

<amp-animation id="anim1" layout="nodisplay">
<script type="application/json">
{
  "selector": ".target-class",
  "animation": "anim2",
  "duration": 1000,
  "--scale": 2
}
</script>
</amp-animation>

<amp-animation id="anim2" layout="nodisplay">
<script type="application/json">
{
  "keyframes": {"transform": "scale(var(--scale))"}
}
</script>
</amp-animation>

Here, whether the ".target-class" matches one element, several or none - the "anim2" is executed for each matched target.

The variables and timing properties specified in the caller animation are passed to the included animation as well.

var() and calc() expressions

amp-animation allows use of var() and calc() expressions for timing and keyframes values.

For instance:

<amp-animation layout="nodisplay">
<script type="application/json">
[
  {
    "selector": ".target-class",
    "duration": "4s",
    "delay": "var(--delay)",
    "--y": "var(--other-y, 100px)",
    "keyframes": {"transform": "translate(calc(100vh + 20px), var(--y))"}
  }
]
</script>
</amp-animation>

Both var() and calc() polyfilled on platforms that do not directly support them. var() properties are extracted from the corresponding target elements. However, it's unfortunately impossible to fully polyfill var(). Thus, where compatibility is important, it's strongly recommended to include default values in the var() expressions. For instance:

<amp-animation layout="nodisplay">
<script type="application/json">
[
  {
    "selector": ".target-class",
    "duration": "4s",
    "delay": "var(--delay, 100ms)",
  }
]
</script>
</amp-animation>

Animation components can specify their own variables as --var-name fields. These variables are propagated into nested animations and override variables of target elements specified via stylesheet (<style> tag). var() expressions first try to resolve variable values specified in the animations and then by querying target styles.

CSS extensions

amp-animation provides several CSS extensions for typical animations needs: rand(), num(), width(), and height(). These functions can be used everywhere where CSS values can be used within amp-animation, including timing and keyframes values.

CSS index() extension

The index() function returns an index of the current target element in the animation effect. This is most relevant when multiple targets are animated with the same effect using selector property. The first target matched by the selector will have index 0, the second will have index 1 and so on.

Among other things, this property can be combined with calc() expressions and be used to create staggered effect. For instance:

{
  "selector": ".class-x",
  "delay": "calc(200ms * index())"
}

CSS rand() extension

The rand() function returns a random CSS value. There are two forms.

The form without arguments simply returns the random number between 0 and 1.

{
  "delay": "calc(10s * rand())"
}

The second form has two arguments and returns the random value between these two arguments.

{
  "delay": "rand(5s, 10s)"
}

CSS width() and height() extensions

The width() and height() extensions return the width/height of the animated element or the element specified by the selector. The returned value is in pixels, e.g. 100px.

The following forms are supported:

  • width() and height() - width/height of the animated element.
  • width('.selector') and height('.selector') - width/height of the element specified by the selector. Any CSS selector can be used. For instance, width('#container > li').
  • width(closest('.selector')) and height(closest('.selector')) - width/height of the element specified by the closest selector.

The width() and height() are epsecially useful for transforms. The left, top and similar CSS properties that can use % values to express animations proportional to container size. However, transform property interpretes % values differently - as a percent of the selected element. Thus, the width() and height() can be used to express transform animations in terms of container elements and similar.

These functions can be combined with calc(), var() and other CSS expressions. For instance:

{
  "transform": "translateX(calc(width('#container') + 10px))"
}

CSS num() extension

The num() function returns a number representation of a CSS value. For instance:

  • num(11px) yields 11;
  • num(110ms) yields 110;
  • etc.

For instance, the following expression calculates the delay in seconds proportional to the element's width:

{
  "delay": "calc(1s * num(width()) / 100)"
}

SVG animations

SVGs are awesome and we certainly recommend their use for animations!

SVG animations are supported via the same CSS properties described in Whitelisted properties for keyframes with some nuances:

  • IE/Edge SVG elements do not support CSS transform properties. The transform animation itself is polyfilled. However, initial state defined in a stylesheet is not applied. If the initial transformed state is important on IE/Edge, it's recommended to duplicate it via SVG transform attribute.
  • While transform CSS is polyfilled for IE/Edge, unfortunately, it's impossible to polyfill transform-origin. Thus, where compatibility with IE/Edge is desired, it's recommended to only use the default transform-origin.
  • Most of the browsers currently have issues interpreting transform-origin CSS correctly. See issues for Chrome, Safari and Firefox. Most of this confusion should be resolved once CSS transform-box is implemented. Where transform-origin is important, it's recommended to also include the desired transform-box CSS for future compatibility.

Triggering animation

The animation can be triggered via a trigger attribute or an on action.

trigger attribute

Currently, visibility is the only available value for the trigger attribute. The visibility triggers when the underlying document or embed are visible (in viewport).

For instance:

<amp-animation id="anim1" layout="nodisplay"
    trigger="visibility">
  ...
</amp-animation>

Triggering via on action

For instance:

<amp-animation id="anim1" layout="nodisplay">
  ...
</amp-animation>
<button on="tap:anim1.start">Animate</button>

on actions

amp-animation element exports the following actions:

  • start - Starts the animation is it's not running already. Timing properties and variables can be specified as action arguments. E.g. anim1.start(delay=-100, --scale=2).

  • restart - Starts the animation or restarts the currently running one. Timing properties and variables can be specified as action arguments. E.g. anim1.start(delay=-100, --scale=2).

  • pause - Pauses the currently running animation.

  • resume - Resumes the currently running animation.
  • togglePause - Toggles pause/resume actions.
  • seekTo - Pauses the animation and seeks to the point of time specified by the time argument in milliseconds or percent argument as a percentage point in the timeline.
  • reverse - Reverses the animation.
  • finish - Finishes the animation.
  • cancel - Cancels the animation.