Jump to content
Search Community

Example: HTML Banner using Greensock JS Beta

Randall test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Attached to this post is a super simple (non-optimized/minified for tutorial purposes!) HTML banner example using the Greensock JS Beta animation platform. This does not use HTML5...though it easily could.


There will be a lot of work (and money spent - hopefully earned by us) converting Flash banners to HTML/CSS/JavaScript in the next 1-2 years so my challenge (to myself and any other Flash/JavaScript developers) is to get up to speed quickly and see how easy it really is. The best part is, jQuery (or other framework) is NOT required, though I suppose in enhanced rich media banners that jQuery may be utilized if it already exists within the container page holding the ad, which most certainly is the case with most media advertising outlet sites serving up the ads.


The only real issue might be file size for all of the combined assets. I predict that the change will come soon within the industry as "they" realize 20-30k more in file size is acceptable to reach nearly 100% of all devices by using straight HTML/CSS/JavaScript as an alternate to using Flash. In fact, I personally believe the 'alternate static image' scenario will be like an 8 track cassette tape someday...a thing of the past and funny conversational topic for old schooler's such as myself, where the 'alternate' will soon be the fully animated HTML/CSS/JavaScript version of the same Flash banner.


Static example:


function getElem(id){ return document.getElementById(id); }
var seq = new TimelineLite();
seq.append( TweenLite.from(getElem('prg_bannerBg'), 1, {css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('prg_bannerChic'), 0.3, {css:{alpha:0, left:310}}) );
seq.append( TweenLite.from(getElem('prg_bannerTitle'), 0.4, {css:{left:-300}}) );
seq.append( TweenLite.from(getElem('prg_bannerSubTitle'), 0.3, {css:{left:-300}}) );
seq.append( TweenLite.from(getElem('prg_bannerZipCodeInput'), 0.2, {css:{left:-300}}) );
seq.append( TweenLite.from(getElem('prg_bannerZipCode'), 0.2, {css:{left:-300}}) );
seq.append( TweenLite.from(getElem('prg_bannerButton'), 0.4, {css:{top:260}}) );


Dynamic example:


function getElem(id){ return document.getElementById(id); }
var scon = getElem('bg1');
var stars = [];
var sCnt = 0;
function star(x,y){
scon.innerHTML += '<img id="star'+sCnt+'" src="star.png" style="position:absolute; left:'+x+'px; top:'+y+'px;" />';
function makeStars(){
var i = 0;
var max = 40;
for(i = 0; i < max; i++){
	star( Math.floor(Math.random()*300) , Math.floor(Math.random()*140) ) ;
setTimeout('tweenStars()', 200);
function tweenStars(){
var i = 0;
var max = stars.length - 1;
var dur = 8;
var tog = true;
var s = null;
for(i = 0; i < max; i++){
	tog = !tog;
	TweenLite.to(getElem(stars[i]), (dur + Math.floor(Math.random()*12)), { css:{rotation:(tog == true ? 720 : -720)} });
var seq = new TimelineLite();
seq.append( TweenLite.from(getElem('bg1'), 1, {css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('www'), 0.8, {css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('com'), 0.8, {css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('title'), 0.7, {delay:0.4, css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('bg2'), 1.1, {delay:1.5, css:{top:310}}) );
seq.append( TweenLite.from(getElem('tag'), 0.7, {css:{left:-310}}) );
seq.append( TweenLite.from(getElem('ysb'), 0.6, {css:{top:310}}) );
seq.append( TweenLite.from(getElem('reg'), 0.7, {css:{top:310}}) );


Interactive example:


// wrapper function for element selector (could use jQuery here, but that adds more file size)
function getElem(id){ return document.getElementById(id); }
var seq = new TimelineLite();
seq.append( TweenLite.from(getElem('prg_container'), 0.6, {css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('prg_logo'), 0.4, {css:{alpha:0, left:200}}) );
seq.append( TweenLite.from(getElem('prg_track'), 0.1, {css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('prg_btnl'), 0.1, {css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('prg_btnr'), 0.1, {css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('prg_drag'), 0.2, {css:{alpha:0}}) );
seq.append( TweenLite.from(getElem('prg_bg2'), 0.2, {css:{alpha:0, top:310}}) );
seq.append( TweenLite.from(getElem('prg_zip'), 0.2, {css:{alpha:0, left:-200}}) );
seq.append( TweenLite.from(getElem('prg_btn'), 0.2, {css:{alpha:0, left:310}}) );
seq.append( TweenLite.from(getElem('prg_g1'), 0.4, {css:{alpha:0, top:300}}) );
seq.append( TweenLite.from(getElem('prg_tag1'), 0.4, {css:{alpha:0}}) );
seq.append( TweenLite.to(getElem('prg_tag1'), 0.4, {delay:0.5, css:{alpha:0}}) );
seq.append( TweenLite.to(getElem('prg_drag'), 0.5, {css:{left:110}, onComplete:function(){getElem('prg_val').value = "$50";}}) );
seq.append( TweenLite.from(getElem('prg_tag2'), 0.4, {css:{alpha:0}}) );
seq.append( TweenLite.to(getElem('prg_tag2'), 0.4, {delay:0.7, css:{alpha:0}}) );
seq.append( TweenLite.to(getElem('prg_drag'), 1, {css:{left:210}, onComplete:function(){getElem('prg_val').value = "$150";}}) );
seq.append( TweenLite.from(getElem('prg_tag3'), 0.4, {css:{alpha:0}}) );
seq.append( TweenLite.to(getElem('prg_tag3'), 0.4, {delay:0.7, css:{alpha:0}}) );
seq.append( TweenLite.to(getElem('prg_g1'), 0.2, {css:{alpha:0, left:-220}}) );
seq.append( TweenLite.from(getElem('prg_g2'), 0.2, {css:{alpha:0, left:-220}}) );
seq.append( TweenLite.from(getElem('prg_tag4'), 0.4, {css:{alpha:0}}) );
seq.append( TweenLite.to(getElem('prg_drag'), 0.8, {css:{left:160}, onComplete:function(){getElem('prg_val').value = "$100"; initInteractivity();}}) );
// store some global variables for the interactivity elements (could also nest this inside an object/class to avoid conflicts on container page)
var btnLeft = getElem('prg_btnl');
var btnRight = getElem('prg_btnr');
var dragger = getElem('prg_drag');
var dragVal = getElem('prg_val');
var amount = 5;
function updateDraggerDollars(){
   var x = parseInt(dragger.style.left);
   dragVal.value = "$" + (x - 60);
   setTimeout('updateDraggerDollars()', 100);
function initInteractivity(){
   btnLeft.onclick = tweenDraggerLeft;
   btnRight.onclick = tweenDraggerRight;
function tweenDraggerLeft(event){
   var x = parseInt(dragger.style.left);
   if(x > 112){TweenLite.to(dragger, 0.3, {css:{left:(x - amount)}, overwrite:true});}
function tweenDraggerRight(event){
   var x = parseInt(dragger.style.left);
   if(x < 207){TweenLite.to(dragger, 0.3, {css:{left:(x + amount)}, overwrite:true});}
updateDraggerDollars(); // sets up polling to update the $dollars as the dragger is tweened 


By the way, the specs for the original Flash version of this banner were 35k for example #1 and #2, and 60K for example #3. We've exceeded that by quite a bit (almost +30k), maybe not as much if the server sends the stuff GZipped. The payoff is that it works on all modern browsers/devices if JavaScript is enabled.

  • Like 4
Link to comment
Share on other sites

Yes, indeed, this whole thread is awesome. Lots of real world example usefulness here. Also the snorkl.tv example (only thing else we could've asked for is some code to check out - but suspect you'll have more samples to come :). Anyway, thanks guys!


I've also been experimenting making simple banner ad examples using the different JS tweening options out there. Seems so far already that Greensock is the most robust and actually meant for these types of animations.

  • Like 1
Link to comment
Share on other sites

Hi jayy,


Glad you are excited about GSAP12 js and are enjoying stuff from Randall and I. I zipped up all the files for the htcBanner demo for you (see attached).


I didn't post them to snorkl.tv as that demo was meant to focus more on the visual output and potential of the platform than the "how it was made" side. I'm kind of hacking my way through the css/js side of things so I didn't want to unleash a mess upon a bunch of folks.


Feel free to poke through the files. If you have been using TimelineLite with Flash and have a little jQuery knowledge you should be able to make some sense of what is going on.


Will certainly be making more demos and sharing more code in the future.


Thanks again for your interest.




  • Like 2
Link to comment
Share on other sites

Below is another example of an expandable all-HTML banner that uses GSAP for all animations/interactivity. Note that jQuery is NOT being used here, thus saving a lot of kb. Instead, we use a jQuery-like wrapper function nested in a class for a similar syntax on selecting elements.



[user=rhaws pass=guest]


And here's the code snippet for just the animation portion using GSAP for this example:

// dynamically create unique selector IDs to ensure they never pose a conflict on the page
var X = 'gm__', // set prefix for all id selectors
Z = new Date().getTime(),
sc = X + ++Z,
v1 = X + ++Z,
v1T = X + ++Z,
v2 = X + ++Z,
v2T = X + ++Z,
v3 = X + ++Z,
v3T = X + ++Z,
v4 = X + ++Z,
v4T = X + ++Z,
Lg = X + ++Z,
LgT = X + ++Z,
Fc = X + ++Z
;// end vars declaration chain
code removed that creates DIV elements for all of the vars above that are tweened below
function jQueryWrapper(){
// public method exposed similar syntax to jQuery id only selector (no # sign needed)
this.$ = function(selector){
	var doc = window.document;
	return doc.getElementById(selector);
// create new instance of jQuery-like selector wrapper function
var _$ = new jQueryWrapper();
// separate tween on the main container background to scroll it upwards
TweenLite.to(_$.$(sc), 9, {css:{backgroundPosition:'0px -130px'}});
// a linear approach to the seq chain - not dynamic or 'smart', just for example purposes
var T = new TimelineLite();
 T.from(_$.$(v1), 0.5, {css:{alpha:0}})
  .from(_$.$(v1T), 0.2, {css:{alpha:0, left:'-220px'}})
  .to(_$.$(v1), 0.5, {delay:1.5,css:{alpha:0}}) // delay here creates the pause between frames
  .to(_$.$(v1T), 0.2, {css:{alpha:0, top:'260px'}})
  .from(_$.$(v2), 0.5, {css:{alpha:0}})
  .from(_$.$(v2T), 0.2, {css:{alpha:0, left:'-220px'}})
  .to(_$.$(v2), 0.5, {delay:1.6,css:{alpha:0}}) // delay here creates the pause between frames
  .to(_$.$(v2T), 0.2, {css:{alpha:0, top:'260px'}})
  .from(_$.$(v3), 0.5, {css:{alpha:0}})
  .from(_$.$(v3T), 0.2, {css:{alpha:0, left:'-220px'}})
  .to(_$.$(v3), 0.5, {delay:1.7,css:{alpha:0}}) // delay here creates the pause between frames
  .to(_$.$(v3T), 0.2, {css:{alpha:0, top:'260px'}})
  .from(_$.$(v4), 0.5, {css:{alpha:0}})
  .from(_$.$(v4T), 0.2, {css:{alpha:0, left:'-220px'}})
  .to(_$.$(v4), 0.7, {delay:1.8,css:{alpha:0}}) // delay here creates the pause between frames
  .to(_$.$(v4T), 0.2, {css:{alpha:0, top:'260px'}})
  .to(_$.$(sc), 0.7, {css:{backgroundPosition:'0px -260px'}}) // now tween bg within the chain
  .from(_$.$(Lg), 0.3, {css:{alpha:0, top:'-80px'}})
  .from(_$.$(LgT), 0.2, {css:{alpha:0, top:'310px'}})
  .from(_$.$(v5), 0.4, {css:{alpha:0, top:'310px'}})
  .from(_$.$(Fc), 0.3, {css:{alpha:0, top:'310px'}}) // end of animation, expandable button is now available for further interactivity
;// end T chain

GSAP is so awesome!

  • Like 3
Link to comment
Share on other sites

Wow, that's really a very good example on how far Javascript/HTML/CSS, mixed with a bit of Greensock sweetness can go in replicating the same features that only Flash ads could do previously. Probably takes a bit longer to create than a Flash equivalent, but still..


An expanding ad with animation, an integrated gallery, rotations, external link, etc. Like a microsite within an ad format. Good find!

  • Like 2
Link to comment
Share on other sites

AND the original Flash version of that GM banner was a whopping ~3.75Mb across 12 dynamically loaded SWF files. The HTML banner version is only around ~683k and took just 6 hours to convert from its original Flash 8 AS2 FLA.


"Greensock sweetness..." - best quote of the day heyitsjayy

  • Like 2
Link to comment
Share on other sites

Here's another example where a banner element pops out (pseudo expanded) on the page. Same login info (see above). http://www.rhaws.com...ners/PUFFS.html



//Note:  jb var holds the main banner code and setup stuff
// ids in array point to the PNG image sequence for the character animation
jb.chars = [ci1,ci2,ci3,ci4,ci5,ci6,ci7,ci8,ci9,
function initCharacter(){
jb.$(jb.chars[jb.cFrame]).style.opacity = '0';
jb.ti = ti; // special scope var for tissue image reference
jb.cFrame = 1;
jb.mFrame = jb.chars.length;
jb.startCharacter = function(){
jb.$(jb.chars[(jb.cFrame - 1)]).style.opacity = '0'; // hide previous frame
jb.cFrame++; // increment PNG sequence frame number
if(jb.cFrame == 6){ // special frame bookmark to time tissue removal below
	// remove tissue from box to match PNG sequence frame image
	TweenLite.to(jb.$(jb.ti), 0.05, {css:{y:'+22'}});
if(jb.cFrame <= jb.mFrame){
	if(jb.cFrame != jb.mFrame){
		jb.$(jb.chars[(jb.cFrame - 1)]).style.opacity = '1'; // show current frame
		setTimeout('jb.startCharacter()', 88);
		//end animation
		jb.$(jb.chars[(jb.cFrame - 1)]).style.opacity = '1'; // show current frame
		// put tissue back in box
		TweenLite.to(jb.$(jb.ti), 0.3, {delay:0.3, css:{y:'-1'}});
// ids below use same metho as previous example in this thread; jQuery-like wrapper
var T = new TimelineLite(); // a linear approach to the chain
  T.from(jb.$(bc), 0.5, {css:{alpha:0}})
.from(jb.$(lc), 0.5, {css:{alpha:0, y:-80}})
.from(jb.$(bi), 0.4, {css:{alpha:0, x:310}})
.from(jb.$(ti), 0.2, {css:{alpha:0, y:'+22'}})
.from(jb.$(ci1), 0.5, {css:{x:-100}})
.from(jb.$(tx1), 0.5, {css:{alpha:0}, onComplete:initCharacter})
.from(jb.$(tx2), 0.5, {delay:1, css:{alpha:0}}) // tag text with delay
.from(jb.$(btn), 0.5, {css:{alpha:0, y:601}})// ending click button
;// end T chain

Note, the size of this banner example could be reduced by another 150k by chopping up the character to arms, body, head, legs and so on and then animating them independently with joint-type effects. For example purposes, a PNG sequence is used to make the animation effect where this example has a total of 18 rendered PNG frames and thus that is why the total file size is larger.

Link to comment
Share on other sites

Nice job, Randall.


Just one thing I'd mention: I noticed that you're using this code to hide things:

jb.$(jb.chars[jb.cFrame]).style.opacity = '0';

As far as I know, that won't work in IE8 and below. You must use an IE-only filter:alpha(opacity=0). Annoying, I know. But you could also just do a TweenLite.set() to make the opacity 0 because it automatically implements some code workarounds internally like the IE filter stuff.


Again, nice job and thanks for sharing!

  • Like 1
Link to comment
Share on other sites

So one must wonder when the mass of these banner ads will start being done in HTML/javascript.. are bigger agencies, and big clients wanting these right now? Or is that a bit in the future yet.


Either way, these are great examples!

Link to comment
Share on other sites

  • 1 month later...

Just re-visiting this topic again, and continue to be impressed at the solutions that people have come up so far to made great banner animations, yet keep the file size in tact.


The jQueryWrapper selector paired with Greensock JS is impressive.


Has anyone come across any other recent banner style ads with Greensock JS style animation lately?

Link to comment
Share on other sites

  • 1 year later...

I apologize that I am new to all of this code, most of it is beyond me. I wanted to use this for HTML 5 /JavaScript banner ads but it seems none of the sample code here, which is fantastic, didn't include the necessary code to make the ads clickable so the viewer is transported to the advertisers site. I love the effects you can do with greensock, I just wish it was easier to use. 



Link to comment
Share on other sites

Hi and welcome to the forums.


Yes unfortunately knowing at least the basis of javascript and/or JQuery is needed to do that, although is not much what you'll need in order to achieve that result.


There is one online tool that you should take a look at, is, as far as I know, the only WYSIWYG Greensock JS editor and is created with banners in mind and is also cross-platform which is very cool for this device era:



Also take a look at this post, there are some pointers about starting with Javascript and JQuery:



And finally if more questions arise just keep asking.


Hope this helps,



  • Like 1
Link to comment
Share on other sites

  • 5 weeks later...
  • 2 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...