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.