I recently gave myself a challenge of creating a rotating hero banner using only sass.

But why?

There are loads of javascript plugins, why not use one of them? A few reasons.

Sass Mixin

Below is the Sass mixin, its quite long and complicated but it works. There’s probably a shorter way to do this, I will go through it sometime to try and optimise it.

@mixin banner($num, $holdSecs, $moveSecs, $transition) {	
	$fullRotation: (($holdSecs + $moveSecs) * $num);
	$singleRotationSeconds: $holdSecs + $moveSecs;
	$singleRotationPercent: 100% / $num;
	$holdPercent: ($holdSecs / $singleRotationSeconds) * $singleRotationPercent;
	$movePercent: $singleRotationPercent - $holdPercent;
	$firstMoveBackStart: $singleRotationPercent + $holdPercent;
	$firstMoveBackEnd: $firstMoveBackStart + $movePercent;
	$firstFinalMoveIn: 100% - $movePercent;
	.banner-1 {
		animation: move-1 $fullRotation+s infinite;
	}
	@if $transition == move {
		@keyframes move-1 {
			0% {
				left: 0;
			}
			#{$holdPercent} {
				left: 0;
			}
			#{$singleRotationPercent} {
				left: -100%;
			}
			#{$firstMoveBackStart} {
				left: -100%;
			}
			#{$firstMoveBackEnd} {
				left: 100%;
			}
			#{$firstFinalMoveIn} {
				left: 100%;
			}
			100% {
				left: 0;
			}
		}
		@for $i from 2 through $num {
			.banner-#{$i} {
				animation: move-#{$i} $fullRotation+s infinite;
			}
			$moveInStart: ($singleRotationPercent * ($i - 1)) - $movePercent;
			$moveInEnd: $moveInStart + $movePercent;
			$moveOutStart: $moveInEnd + $holdPercent;
			$moveOutEnd: $moveOutStart + $movePercent;
			@keyframes move-#{$i} {
				0% {
					left: 100%;
				}
				#{$moveInStart} {
					left: 100%;
				}
				#{$moveInEnd} {
					left: 0%;[]
				}
				#{$moveOutStart} {
					left: 0%;
				}
				#{$moveOutEnd} {
					left: -100%;
				}
				100% {
					left: -100%;
				}
			}
		}
	} @else if $transition == fade {
		@keyframes move-1 {
			0% {
				opacity: 1;
			}
			#{$holdPercent} {
				opacity: 1;
			}
			#{$singleRotationPercent} {
				opacity: 0;
			}
			#{$firstFinalMoveIn} {
				opacity: 0;
			}
			100% {
				opacity: 1;
			}
		}
		@for $i from 2 through $num {
			.banner-#{$i} {
				animation: move-#{$i} $fullRotation+s infinite;
			}
			$moveInStart: ($singleRotationPercent * ($i - 1)) - $movePercent;
			$moveInEnd: $moveInStart + $movePercent;
			$moveOutStart: $moveInEnd + $holdPercent;
			$moveOutEnd: $moveOutStart + $movePercent;
			@keyframes move-#{$i} {
				0% {
					opacity: 0;
				}
				#{$moveInStart} {
					opacity: 0;
				}
				#{$moveInEnd} {
					opacity: 1;
				}
				#{$moveOutStart} {
					opacity: 1;
				}
				#{$moveOutEnd} {
					opacity: 0;
				}
				100% {
					opacity: 0;
				}
			}
		}
	} 
}

Usage

To use this, write up your html as follows:

<div class="wrapper">
	<ul>
		<li class="banner_item banner-1">One</li>
		<li class="banner_item banner-2">Two</li>
		<li class="banner_item banner-3">Three</li>
	</ul>
</div>

The surrounding div needs to have a width assigned and the following css:

.wrapper {
	width: 100%;
	position: relative;	
	overflow: hidden;
}

Include the mixin using 4 arguments:

@include banner(4, 4, 2, fade);

The arguments in order are:

  1. Number of items
  2. Time in seconds each item will remain static after the transition.
  3. Time in seconds for the transition to take place
  4. The type of transition, currently accepts ‘move’ or ‘fade’

How it works

There are currently 2 options for transitioning between items, ‘move’ and ‘fade’.

For ‘move’ the item’s will transition from being in the viewport of the surrounding div to moving out to the right. The first item is absolutely placed in the div so it is visible whereas the other items are placed at -100% left and so won’t be visible. The mixin basically calculates the keyframes required for the number of items and populates the keyframes css.

‘fade’ basically has all items stacked in the viewport and changes the opacity of each item in turn, showing the one below.

Demo

You can also see an example of this being used on www.jin-long.co.uk