css-animation-blocks

1.0.3 • Public • Published

CSS Animation Blocks

A JavaScript library for managing and applying CSS animations with the following features:

  1. Animation Blocks: Uses JavaScript objects to manage animation blocks in timeline format. Blocks can be nested, allowing complex animations by combining small, easily-maintained blocks.

  2. Additive Animations: Multiple CSS Animations can be added to Dom elements in sequence, and will not cancel out running animations, as long as the added animations don't target the same attributes.

  3. Multiple Transform Animations: To allow multiple sequential transform animations, CSS Animation Blocks creates nested wrapper elements around your elements. Each transform animation is applied to its own wrapper element. Without wrapper elements, transforms would cancel each other out.

Installation

There are two npm options for adding CSS Animation Blocks to your JS project. First run npm install css-animation-blocks, then include in your project using 'require' or 'import'.

  • const { AnimationBlock } = require 'css-animation-blocks';
  • import AnimationBlock from 'css-animation-blocks';

You can also include css-animation-blocks directly in the head of an html page:

  • <script src="https://unpkg.com/css-animation-block"></script>

Documentation

Instantiate

To create an AnimationBlock, require 'css-animation-blocks' and call new AnimationBlock() with a block timeline object and an optional configuration object.

const { AnimationBlock } = require('css-animation-blocks');
 
const mainBlock = new AnimationBlock({
  '00:00.000': {}
}, {
  Defaults: {},
  Loop: {}
});
 
mainBlock.start();

AnimationBlock timeline objects

Within a block timeline object, the main object keys are timecode strings, in the format 'minutes:seconds.milliseconds (00:00.000)'. This represents the moment in time your defined animations will start.

Each timecode key holds an object with an 'animations' array. This is where you'll call your 'cssAnimation' and 'cssTransform' keyframes on a Dom 'elementSelector' in an html page.

  • 'cssAnimation' is an array of CSS Animations.
  • 'cssTransform' is an object. The keys are the CSS Transform type, and the values are CSS Animations.
  • 'elementSelector' is a string value for any valid Dom selector that works with document.querySelectorAll.

Use standard CSS animation shorthand syntax. See the Mozilla docs for detailed info on CSS Animation shorthand, and also for more info about the CSS transform property.

const { AnimationBlock } = require('css-animation-blocks');
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease normal forwards'
        ],
        cssTransform: {
          rotate: 'rotate 2s ease normal forwards',
        },
        elementSelector: 'h1'
      }
    ]
  }
}, {
  defaults: {},
  loop: {}
});
 
mainBlock.start();

The above would add the 'cssAnimation' 'fade-in 1s ease normal forwards' and 'cssTransform' animation 'rotate 2s ease normal forwards 1' to all 'h1' elements.

In order for this to work, you must define CSS keyframes 'fade-in' and 'rotate' in an external CSS stylesheet included in you html page. That CSS might look like this:

h1 {
  opacity: 0;
}
 
@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
 
@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

You'll also need an HTML page that includes a h1 element:

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- include the CSS mentioned above -->
    <link href="style.css" rel="stylesheet">
    <script src="https://unpkg.com/css-animation-block"></script> 
    <script>
      /* JavaScript goes here */
    </script> 
  </head>
 
  <h1>CSS Animation Blocks</h1>
</html>

groupOffset

When applying animations to multiple objects, it's possible to add a delay between each one. 'groupOffset' is an object that contains a 'delayTime' or 'delayRange' element.

  • 'delayTime' is the number of milliseconds to wait between applying animations to the each of the chosen elementSelectors.
  • 'delayRange' is an array containing two 'delayTime' numbers representing the min/max time to delay between applying animations to the each of the chosen elementSelectors. At runtime, a number will be randomly selected using the 'delayRange' values as the min/max to choose from.
const { AnimationBlock } = require('css-animation-blocks');
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease normal forwards'
        ],
        cssTransform: {
          rotate: 'rotate 2s ease normal forwards',
        },
        elementSelector: 'h1',
        groupOffset: {
          delayTime: 300
        }
      }
    ]
  }
}, {
  defaults: {},
  loop: {}
});
 
mainBlock.start();

The above adds a 'groupOffset' of 300 milliseconds, so each 'h1' element's animation will start with a 300 millisecond delay.

Nested AnimationBlocks

Another element you can add to timecode objects is a 'blocks' array. Animations can be split into smaller blocks combined into another AnimationBlock.

In the previous example, there's a mainBlock with animations applied to an 'h1' elementSelector. Let's move that animation into its own 'h1Block' and include that in the mainBlock's 'blocks' array.

const { AnimationBlock } = require('css-animation-blocks');
 
const h1Block = new AnimationBlock({
  '00:00.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease normal forwards'
        ],
        cssTransform: {
          rotate: 'rotate 2s ease normal forwards',
        },
        elementSelector: 'h1',
        groupOffset: {
          delayTime: 300
        }
      }
    ]
  }
});
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [h1Block]
  }
}, {
  defaults: {},
  loop: {}
});
 
mainBlock.start();

AnimationBlock config object

The optional config object can be used to apply settings to an entire AnimationBlock.

Defaults object

To apply default 'elementSelector' and 'groupOffset' settings to an entire AnimationBlock, use the defaults object. When a default value is set, that value applies to all animations in the AnimationBlock, unless that value is already defined.

const { AnimationBlock } = require('css-animation-blocks');
 
const h1Block = new AnimationBlock({
  '00:00.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease normal forwards'
        ],
        cssTransform: {
          rotate: 'rotate 2s ease normal forwards',
        },
      }
    ]
  },
  '00:03.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease reverse forwards'
        ],
      }
    ]
  }
}, {
  defaults: {
    elementSelector: 'h1',
    groupOffset: {
      delayTime: 300
    }
  }
});
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [h1Block]
  }
}, {
  defaults: {},
  loop: {}
});
 
mainBlock.start();

The above 'defaults' settings applies all animations to the 'h1' 'elementSelector', and sets a 300 millisecond 'groupOffset' 'delayTime' before applying the animation to each 'h1' element on the page.

Loop object

Use the 'loop' object to make an AnimationBlock play multiple times.

  • 'count' is a number representing how many times to repeat the AnimationBlock.
  • 'infinite' is a boolean (true/false). If set to true, the AnimationBlock will repeat continuously.
  • 'endTime' this is a timecode string (00:00.000) indicating when to end the current loop and start the next loop.
const { AnimationBlock } = require('css-animation-blocks');
 
const h1Block = new AnimationBlock({
  '00:00.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease normal forwards'
        ],
        cssTransform: {
          rotate: 'rotate 2s ease normal forwards',
        },
      }
    ]
  },
  '00:03.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease reverse forwards'
        ],
      }
    ]
  }
}, {
  defaults: {
    elementSelector: 'h1',
    groupOffset: {
      delayTime: 300
    }
  },
  loop: {}
});
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [h1Block]
  }
}, {
  defaults: {},
  loop: {
    count: 2,
    endTime: '00:05.000'
  }
});
 
mainBlock.start();

Basic Tutorial

This short tutorial should provide a basic understanding of CSS Animation Blocks by building a simple animation.

HTML

Here's an html page with a ".container div", ".box div" and an "h1" .

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>CSS Animation Blocks</title>
  <link href="style.css" rel="stylesheet">
  <script src="index.js" type="module"></script> 
</head>
<body>
 
  <div class="container">
    <div class="box">
      <h1>CSS Animation Blocks</h1>
    </div>
  </div>
 
</body>
</html>

It also includes two external files, "style.css" and "index.js". That's where we'll place code to animate the "box" element.

CSS

In the "style.css" file, add styles to set the "box" element's initial state, and create animation keyframes that can be applied with Animation Blocks.

The following styles create a square red box with "opacity" set to 0 (invisible), and keyframes "fade-in", "move-down", "background-red" and "rotate".

NOTE: These examples only include the @keyframes syntax. You may require @-webkit-keyframes and other vendor prefixes for cross-browser compatibility.

.container {
  display: flex;
  flex-direction: row;
  justify-content: center;
  flex-wrap: wrap;
  font-size: 1.4rem;
  text-transform: lowercase;
  font-family: sans-serif;
  padding: 0;
  margin: 0;
}
 
.box {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 240px;
  height: 180px;
  background: black;
  opacity: 0;
  position: relative;
  border-radius: 10px;
  padding: 0;
  margin: 1em;
  overflow: hidden;
}
 
h1 {
  width: 100%;
  text-align: center;
  color: #fff;
  opacity: 0;
  padding: 0;
  margin: 0;
}
 
@keyframes fade-in {
  to { opacity: 1; }    
}
 
@keyframes move-down {
  0% { transform: translateY(0%); }
  100% { transform: translateY(20%); }
}
 
@keyframes background-red {
  to { background: red; }
}
 
@keyframes rotate {
  to { transform: rotate(360deg); }
}

Opening the html file in a browser now would show an empty page, because our elements have '0' opacity. Create an Animation Block to apply the "fade-in", "move-down" and "background-red" keyframes to the "box" element.

JavaScript

In the "index.js" file, import the AnimationBlock library and create a new AnimationBlock:

const { AnimationBlock } = require('css-animation-blocks');
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    animations: [
      {
        elementSelector: '.box',
        cssAnimation: [
          'fade-in 1s ease normal forwards',
          'background-red 1s steps(1) normal forwards',
        ],
        cssTransform: {
          translateY: 'move-down 2s ease normal forwards',
        },
      },
    ]
  }
},{});
 
mainBlock.start();

At time "00:00.000" we have an animations array containing animation objects. Each animation object:

  • Must have one "elementSelector". The "elementSelector" above targets ".box" (all elements with the class "box").
  • Can (optionally) have one "cssAnimation" array containing CSS animation shorthand strings.
  • Can (optionally) have one "cssTransform" object. Object keys should match the transform type, and the values are CSS animation shorthand strings.

The animation block above applies three animations, "fade-in", "background-red" and "move-down". Load the page in a browser and you should see the box fade in, move down and turn red.

NOTE: Animations are written in standard CSS animation shorthand. For more info, visit the MDN web docs

Nested Blocks

One benefit of CSS Animation Blocks is that you can include blocks in other blocks. To illustrate let's extend the previous example by moving the ".box" animation into its own block, and include that block in the "mainBlock".

const { AnimationBlock } = require('css-animation-blocks');
 
const boxBlock = new AnimationBlock({
  '00:00.000': {
    animations: [
      {
        elementSelector: '.box',
        cssAnimation: [
          'fade-in 1s ease normal forwards',
          'background-red 1.5s steps(1) normal forwards',
        ],
        cssTransform: {
          translateY: 'move-down 2s ease normal forwards',
        },
      },
    ]
  }
},{});
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [boxBlock]
  }
},{});
 
mainBlock.start();

We created a new Animation Block named "boxBlock". To nest that block in our "mainBlock", we added a "blocks" array containing the new block.

Nested blocks can also contain other nested blocks. Here, we'll add a new block to animate the "h1" element in our ".box" element, and we'll nest the new block in the "boxBlock".

const { AnimationBlock } = require('css-animation-blocks');
 
const titleBlock = new AnimationBlock({
  '00:00.000': {
    animations: [
      {
        elementSelector: 'h1',
        cssAnimation: [
          'fade-in 1s steps(1) normal forwards',
        ],
        cssTransform: {
          scale: 'scale-down 0s ease normal forwards',
        },
      }
    ]
  },
  '00:02.500': {
    animations: [
      {
        elementSelector: 'h1',
        cssTransform: {
          scale: 'scale-down 3s linear reverse forwards',
          rotate: 'rotate 2s ease normal forwards 1',
        },
      }
    ]
  },
},{});
 
const boxBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [titleBlock],
    animations: [
      {
        elementSelector: '.box',
        cssAnimation: [
          'fade-in 1s ease normal forwards',
          'background-red 1s steps(1) normal forwards',
        ],
        cssTransform: {
          translateY: 'move-down 2s ease normal forwards',
        },
      },
    ]
  },
  '00:03.000': {
    animations: [
      {
        elementSelector: '.box',
        cssTransform: {
          rotate: 'rotate 2s ease normal forwards 1',
        },
      }
    ]
  },
  '00:05.000': {
    animations: [
      {
        elementSelector: '.box',
        cssAnimation: [
          'fade-in 1s ease reverse forwards',
        ],
      }
    ]
  },
},{});
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [boxBlock]
  }
},{});
 
mainBlock.start();

Group Offset

So far, we've looked at animations on single elements, one ".box" and one "h1", but those selectors could apply to multiple elements. Let's duplicate the existing ".box" element and see how the animation is applied. Change the ".container" div in "index.html" to match this:

<div class="container">
  <div class="box">
    <h1>CSS</h1>
  </div>
  <div class="box">
    <h1>Animation</h1>
  </div>
  <div class="box">
    <h1>Blocks</h1>
  </div>
</div>

Now, if you reload the page, you should see three boxes all animating at the same time. This is fine, but what if you wanted to run the same animation on those elements, but delay the start of each element to create a "stepped" animation?

Each element in an "animations" array can include a "groupOffset" object with a "delayTime" key with value a in milliseconds. If the animation applies to multiple elements, each element will be delayed by the "groupOffset.delayTime" amount. Let's update the first animation in the "boxBlock" and add a "groupOffset":

'00:00.000': {
  blocks: [titleBlock],
  animations: [
    {
      elementSelector: '.box',
      cssAnimation: [
        'fade-in 1s ease normal forwards',
        'background-red 1s steps(1) normal forwards',
      ],
      cssTransform: {
        translateY: 'move-down 2s ease normal forwards',
      },
      groupOffset: {
        delayTime: 300
      }
    },
  ]
},

Reload the page, and each block should have a slight pause before fading in. You can add a different groupOffset for each object in an "animations" array. Trying copy the "groupOffset" from the ".box" element and adding it to the first animation in the "titleBlock". That should make the "h1" elements appear one-at-a-time, as each ".box" turns red.

What if you want all animations in a block to have the same groupOffset? It's possible to set a default offset for an entire block in the configuration settings.

Configuration

Animation Blocks have a second optional parameter for setting configurations. The options are "defaults" and "loop".

For example, you can see the config object here:

const { AnimationBlock } = require('css-animation-blocks');
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [boxBlock]
  }
},{
  defaults: {},
  loop: {}
});

Defaults

When an entire Animation Block targets a single "elementSelector" and/or "groupOffset", it can be tedious to enter the same "elementSelector" and/or "groupOffset" in each time entry. That's where the "defaults" config settings can help.

Any settings you add to the "defaults" config will apply to all objects in the "animations" array that do not already have those settings applied.

Default Element Selector

The "boxBlock" Animation Block we've been working applies animations to ".box" elements, and each animation time key includes the same "elementSelector" for ".box". We can simplify our code, and save some future typing, by applying a Default Element Selector to this Animation Block.

Here's the same code as before, but using a Default Element Selector:

const { AnimationBlock } = require('css-animation-blocks');
 
const boxBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [titleBlock],
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease normal forwards',
          'background-red 1s steps(1) normal forwards',
        ],
        cssTransform: {
          translateY: 'move-down 2s ease normal forwards',
        },
        groupOffset: {
          delayTime: 300
        }
      },
    ]
  },
  '00:03.000': {
    animations: [
      {
        cssTransform: {
          rotate: 'rotate 2s ease normal forwards 1',
        },
      }
    ]
  },
  '00:05.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease reverse forwards',
        ],
      }
    ]
  },
},{
  defaults: {
    elementSelector: '.box',
  }
});

As you can see, all "elementSelector" keys were removed from individual "animations" array objects, and a "defaults.elementSelector" setting was added to the config object. If you reload the page, the animation should continue working as before.

Default Group Offset

You can also set a default "groupOffset" that will apply to all animations in the current Animation Block. Here's the "boxBlock" Animation Block with a Default Group Offset config setting added.

const { AnimationBlock } = require('css-animation-blocks');
 
const boxBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [titleBlock],
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease normal forwards',
          'background-red 1s steps(1) normal forwards',
        ],
        cssTransform: {
          translateY: 'move-down 2s ease normal forwards',
        },
      },
    ]
  },
  '00:03.000': {
    animations: [
      {
        cssTransform: {
          rotate: 'rotate 2s ease normal forwards 1',
        },
      }
    ]
  },
  '00:05.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s ease reverse forwards',
        ],
      }
    ]
  },
},{
  defaults: {
    elementSelector: '.box',
    groupOffset: {
      delayTime: 300
    }
  }
});

Loop

Another config setting is "Loop". You can use this to run an Animation Block more than once. The possible settings are "count", "infinite", and "endTime".

The settings "count" and "infinite" are both ways to set the number of times an animation will play." A loop "count" of "2" means the animation will play twice, or setting "infinite" to "true" means the animation will play continuously. You only need one of these settings, either "count" or "infinite".

The "endTime" setting is the time the current animation loop ends and the next loop can begin. The "endTime" can be higher than the highest time in the Animation Block, adding a gap/pause at the end of the loop. The "endTime" can also be lower than the highest time in an Animation Block, causing only part of the animation to play.

Here we add the "loop" config to the "mainBlock" Animation Block. The animation will play twice and each loop will run for 7.5 seconds.

const { AnimationBlock } = require('css-animation-blocks');
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [boxBlock]
  }
},{
  loop: {
    count: 2,
    endTime: '00:07.500',
  },
});

Here's the same animation with "loop.infinite". This animation will run continuously.

const { AnimationBlock } = require('css-animation-blocks');
 
const mainBlock = new AnimationBlock({
  '00:00.000': {
    blocks: [boxBlock]
  }
},{
  loop: {
    infinte: true,
    endTime: '00:07.500',
  },
});

Nested blocks can be looped independently from the main block. The only limitation, is that the number of loops in nested blocks can't exceed the time length of the main block. So, if you created an infinite loop in a nested block, it would only run until is hit the "endTime" of the main block. In the example above, that would be 7.5 seconds.

To illustrate, let's set a short, infinite loop on our "titleBock". As you'll see, the animation will only run until it hits the end of the main block.

const { AnimationBlock } = require('css-animation-blocks');
 
const titleBlock = new AnimationBlock({
  '00:00.000': {
    animations: [
      {
        cssAnimation: [
          'fade-in 1s steps(1) normal forwards',
        ],
        cssTransform: {
          scale: 'scale-down 0s ease normal forwards',
        },
      }
    ]
  },
  '00:02.500': {
    animations: [
      {
        cssTransform: {
          scale: 'scale-down 3s linear reverse forwards',
          rotate: 'rotate 2s ease normal forwards 1',
        },
      }
    ]
  },
},{
  loop: {
    endTime: '00:02.000',
    infinite: true
  },
  defaults: {
    elementSelector: 'h1',
    groupOffset: {
      delayTime: 300
    }
  }
});

Package Sidebar

Install

npm i css-animation-blocks

Weekly Downloads

1

Version

1.0.3

License

MIT

Unpacked Size

106 kB

Total Files

9

Last publish

Collaborators

  • scrawlon