Cascading sequences

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

- 4 min read

Movement can show us where to look and can overtake all forms of visual information we process, more so than colour, line, or shape. Something that moves immediately draws our attention as our eyes track the movement. As the demo reveals your eyes move left and right, motion can be difficult to ignore, unless it’s a flash banner advertisement.

When we add motion, we are essentially imitating life - real world objects and their movement inspire how their digital counterparts behave, or how they can exaggerate behaviour to affect tone.

Often objects cascade, they behave in a way in which one object follows the object before it. The movement of pistons in a car, a flocks of birds, droplets of water, their individual movement defines their relationship to the group.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Changing individual timing

Timing is one way we can make this one to many relationship stand out rather than appearing as a bulk mass. Various factors affect how we time our objects such as placement, size, and the order in which it appears in the sequence - all these determine the individuals relationship to the group.

A grid shows us an example of how placement can matter, if we animate a number objects one after the other in a sequence with 2 rows - the placement of the second row of objects is affected by the timing of the first row.

Depending on how we're animating the objects, how close something is to the left side of the screen and the right side of the screen is also a consideration for timing. Try resizing the browser and running the animation at different sizes where the grid is shaped differently.

A quick primer on CSS animation

If you're familiar with Flash or After Effects or other animation programs but not familiar with CSS Animation expect to be confused by the bizarre choices in how animation is applied. The most confusing part of CSS Animation is by far how the timing is applied.

						

	/*
		In CSS Animation you must first
		tell the browser what animation you wish to do
		the below animation will be start from invisible
		then become visible halfway through the animation
	*/

	@keyframes sampleAnimation {

		0% {

			opacity: 0;

		}

		50% {

			opacity: 1;

		}

	}

	/*
		But where's the timing?
		Let's say you have a circle you want to appear
	*/

	.circle {

		animation: cascadeIn 1s 1s ease-in-out forwards;

	}

						
					

If the code above was in a visual editor you might see a tween from 0s to 0.5s where the circle was fading in, but in CSS animation, rather than define these tweens we define the entire animation and apply different timing to the overall animation. Working out where things are actually being applied require a calculator to understand.

An example of a timeline in the popular motion graphics software, Adobe After Effects

This approach to how CSS applies animation is both its strength and its weakness - it allows us to reuse the animation with different timings, but at the cost of overall control of what actually happens within the animation. This design choice gives us reuse over quality, which is completely counterintuitive to how I'd want to be using animations for interface elements in CSS.

What's worse is that transforms like Scale, and translate (moving) exist on the same track - so you can't apply these seperately, unless you apply a different transform to a parent element.

Applying animation in sequence

This cascading experiment shows how we might achieve this sequencing behaviour in CSS using the :Nth-child and :Nth-of-type selectors in combination with animation-delay. We have a number of objects in a grid, which are revealed one-by-one.

						

	/*
	   each item is given a slightly different animation delay
	   this is how each item is fired off individually
	*/

	.d-cascade li:nth-of-type(1) {

	  animation-delay:75ms;


	}
	/* each list item is incrementing by a delay of 75ms */
	.d-cascade li:nth-of-type(2) {

	  animation-delay:150ms;

	}

	/*
		A very simple cascade animation,
		from invisible and 70% of it's size
		to 100% and visible
	*/

	@keyframes cascadeInSimple {

	  0% {

	    opacity:0;
	    transform:scale(0.7);

	  }

	  100% {

	    opacity:1;
	    transform:scale(1);

	  }
	}

	/*
		Apply the animation to every list item
	*/
	.d-cascade-simple li {

	  animation: cascadeInSimple 1s 1s ease-in-out forwards alternate infinite;


	}

						

					

Individual actors

In order to create more independent behaviour we need to change the way we approach the animation

If you want to code individual behaviour beyond simply changing the delay - you need to set an initial state to be different on each object, and use the animation to apply the uniform result. This is not the only way to make this happen, the alternative is to apply 2 seperate keyframe animations to the same elements and alter the animation timing, this can get really messy.

						
	/*
		This is just the first 2 list items,
		by selecting by type we can add different delays
		and different properties from their start positions
	*/

	.d-cascade li:nth-of-type(1) {

	  animation-delay:75ms;
	  transform:scale(0.1) translateX(-200%);

	}

	/* Each list item is given a slightly different starting point */

	.d-cascade li:nth-of-type(2) {

	  animation-delay:150ms;
	  transform:scale(0.2) translateX(-180%);

	}
					
				

One way to approach setting the initial state is to remove the 0% and add a later value like 50% / 100%, then you can control the initial states in your styles. This will allow you you to go beyond uniform starting points that you would be restricted to using if you made the animation from 0%.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

The animation then uniformly moves each actor back to their original position, it doesn't require us to set 0%, by doing this we can individually style the starting point of each actor and apply the animation after the fact.

						


	@keyframes cascadeInOut {

	  50% {

	     opacity:1;

	  }

	  100% {


	    transform:scale(1) translateX(0);

	  }
	}

	.d-cascade-continuous li {

	   animation: cascadeInOut 1.2s 1s ease-in-out forwards infinite;

	}
						
					

Some considerations

Fallbacks

If you're using CSS to animate a piece of interface that's integral to the experience, a library like Modernizr can help you detect if the browser has the CSS animations feature, if it's not present - serve the content without anything from the default state that can't be animated. For example, if my initial state has opacity set to 0 and Internet Explorer 9 hits the browser it's going to be completely usable since IE9 supports opacity, but doesn't support animations.

Fill-mode, a friend to features

The animation fill mode, where you see 'forwards', in the shorthand animation style essentially says that when this animation is complete apply this style forever, if I were to put backwards it will apply the styles of 0% to the list items, and if I put 'both' it will apply the animation styles both ways (before and after).

This can be useful when trying to create fallbacks for the CSS animation as you can have the animation apply styles both ways, and if animations aren't present - then it just ignores this style application.

Responsive animation

As I stated earlier, considerations like size and placement can affect timing - this is fairly important when we're talking about responsive animation. Squishing our animations from a wide screen size to a small screen size just doesn't seem to cut it. Objects are smaller, their placement can change - it no longer works the way it did before.

This isn't the only consideration, the other major consideration is animation performance - small devices and even some of the mid sized portable devices don't have the power that our desktops and laptops have behind them - they can be incredibly janky. In my testing experience, even the most performant animation properties, transforms and opacity, can appear to be janky on portable devices - so it's worth trying to keep animations as subtle as possible on small screen devices.