Using CSS transitions to animate the opacity of images is a great way to implement a web slideshow, but implementing an animation-based fallback for old browsers is a pain. Fortunately - yui includes a transition module that takes care of the fallback magic; which made it easy for me to code up an Android and iOS-friendly HTML replacement for a Flash .swf banner in a project I'm helping with.
The banner's markup leverages the absolute position in a relative position container trick:
div.banner {
position:relative;
height:230px;
}
div.banner img {
position: absolute;
opacity: 0;
}
div.banner img.logo { /* overlay logo on banner */
opacity:1;
bottom:25px;
right:0;
}
<div id="banner" data-anim-period-secs="5" class="yui3-u-1 banner">
<img src="/myrwa/resources/img/banner/lichen.jpg"/>
<img src="/myrwa/resources/img/banner/Herringrun.jpg"/>
<img src="/myrwa/resources/img/banner/TuftsSailingTeam.jpg"/>
<img src="/myrwa/resources/img/banner/canoe.jpg"/>
<img id="logo" class="logo" src="/myrwa/resources/img/myRWA_logo_2010.gif" />
</div>
The banner's javascript module implements a simple yui view that runs a setinterval loop (Y.later wraps setinterval) that applies an opacity transition to make the current image in the banner's slideshow opaque, and the last image transparent.
/*
* Copyright 2013 http://mysticriver.org
*
* The contents of this file are freely available subject to the
* terms of the Apache 2.0 open source license.
*/
/**
* see http://yuiblog.com/blog/2007/06/12/module-pattern/
* YUI doc comments: http://developer.yahoo.com/yui/yuidoc/
* YUI extension mechanism: http://developer.yahoo.com/yui/3/yui/#yuiadd
*
* @module myrwa-banner
* @namespace myrwa
*/
YUI.add( 'myrwa-banner', function(Y) {
Y.namespace('myrwa');
function log( msg, level ) {
level = level || 2;
Y.log( msg, level, "myrwa/banner.js" );
}
/**
* View abstraction for the header banner which animates with
* transitions between a set of gallery images.
* Initialize with container
* selector for markup initialized with banner images
* ready for progressive enhancement.
*
* @class BannerView
*/
var BannerView = Y.Base.create( 'bannerView', Y.View, [],
{
initializer:function(config) {
},
render:function() {
// do not setup the animation than once
if ( this.rendered ) return;
var container = this.get( "container" );
var logoNode = container.one( "img.logo" );
var imageNodes = container.all( "img"
).filter( function(img) { return ! Y.Node(img).hasClass( "logo" ); } );
Y.assert( "Found image nodes", imageNodes.size() > 0 );
// initialize the banner to show the first image
imageNodes.each( function(n) { n.setStyle( "opacity", 0 ); } );
imageNodes.item(0).setStyle( "opacity", 1 );
if ( imageNodes.size() == 1 ) return; // no need to animate
var animPeriod = this.get( "animPeriodSecs" );
if ( animPeriod < 2 ) {
// check for an attribute on the container if period not set in js code
var tmp = parseInt( container.getAttribute( "data-anim-period-secs" ) );
if ( tmp ) { animPeriod = tmp; }
}
if ( animPeriod < 2 ) animPeriod = 2;
var currentImageIndex = 0;
log( "Launching banner animation ..." );
//
// Every animPeriod seconds ease the current node out, and the new node in
//
Y.later( animPeriod * 1000, this,
function(){
var nextImageIndex = (currentImageIndex + 1) % imageNodes.size();
var inNode = imageNodes.item( nextImageIndex );
var outNode = imageNodes.item( currentImageIndex );
//log( "banner transition from " + currentImageIndex + " to " + nextImageIndex );
inNode.setStyle( "opacity", 0 );
outNode.setStyle( "opacity", 1 );
inNode.show();
outNode.show();
outNode.transition(
{
easing: 'ease-out',
duration: 0.75, // seconds
opacity: 0
},
function() { outNode.hide(); }
);
inNode.transition(
{
easing: 'ease-in',
duration: 0.75,
opacity: 1
}
);
currentImageIndex = nextImageIndex;
},
[], true
);
this.rendered = true;
}
},
{
ATTRS: {
animPeriodSecs: {
value:0
}
}
}
);
//---------------------------------------
Y.myrwa.banner = {
BannerView:BannerView
};
}, '0.1.1' /* module version */, {
requires: [ 'node', 'test', 'transition', 'view']
});
A page bootstraps the banner animation with code like this:
YUI().use('myrwa-banner', 'test', function(Y){
Y.log( "Hello, World!", 2, "myrwa/main.js" );
var banner = new Y.myrwa.banner.BannerView(
{
container:"#banner"
}
);
banner.render();
});
Anyway - I was pretty happy with how that all came together. This little banner slideshow lacks the nice controls in bootstrap's slideshow, but it was good enough for what I needed, and I avoided pulling in bootstrap's dependencies (jquery, whatever). The code is available on github, and a page I used for testing is also online for now.