Team:Shenzhen BGIC 0101/superscrollorama

From 2013.igem.org

/* SUPERSCROLLORAMA - The jQuery plugin for doing scroll animations by John Polacek (@johnpolacek)

Powered by the Greensock Tweening Platform http://www.greensock.com Greensock License info at http://www.greensock.com/licensing/

Dual licensed under MIT and GPL.

Thanks to Jan Paepke (@janpaepke) for making many nice improvements

  • /

(function($) {

$.superscrollorama = function(options) {

var superscrollorama = this; var defaults = { isVertical:true, // are we scrolling vertically or horizontally? triggerAtCenter: true, // the animation triggers when the respective Element's origin is in the center of the scrollarea. This can be changed here to be at the edge (-> false) playoutAnimations: true, // when scrolling past the animation should they be played out (true) or just be jumped to the respective last frame (false)? Does not affect animations where duration = 0 reverse: true // make reverse configurable so you don't have to pass it in for every tween to reverse globally }; superscrollorama.settings = $.extend({}, defaults, options);

       var $window = $(window);

// PRIVATE VARS

var animObjects = [], pinnedObjects = [], scrollContainerOffset = {x: 0, y: 0}, doUpdateOnNextTick = false, targetOffset, i;

// PRIVATE FUNCTIONS

function init() { // set event handlers $window.scroll(function() { doUpdateOnNextTick = true; }); TweenLite.ticker.addEventListener("tick", tickHandler); }

function cssNumericPosition ($elem) { // return 0 when value is auto var obj = { top: parseFloat($elem.css("top")), left: parseFloat($elem.css("left")) }; if (isNaN(obj.top)) { obj.top = 0; } if (isNaN(obj.left)) { obj.left = 0; } return obj; }

function tickHandler() { if (doUpdateOnNextTick) { checkScrollAnim(); doUpdateOnNextTick = false; } }

// reset a pin Object function resetPinObj (pinObj) { pinObj.el.css('position', pinObj.origPositioning.pos); pinObj.el.css('top', pinObj.origPositioning.top); pinObj.el.css('left', pinObj.origPositioning.left); } // set a Tween Progress (use totalProgress for TweenMax and TimelineMax to include repeats) function setTweenProgress(tween, progress) { if (tween) { if (tween.totalProgress) { tween.totalProgress(progress).pause(); } else { tween.progress(progress).pause(); } } }

function checkScrollAnim() {

var currScrollPoint = superscrollorama.settings.isVertical ? $window.scrollTop() + scrollContainerOffset.y : $window.scrollLeft() + scrollContainerOffset.x; var offsetAdjust = superscrollorama.settings.triggerAtCenter ? (superscrollorama.settings.isVertical ? - $window.height()/2 : - $window.width()/2) : 0; var i, startPoint, endPoint;

// check all animObjects var numAnim = animObjects.length; for (i=0; i<numAnim; i++) { var animObj = animObjects[i], target = animObj.target, offset = animObj.offset;

if (typeof(target) === 'string') {

                   targetOffset = $(target).offset() || {};

startPoint = superscrollorama.settings.isVertical ? targetOffset.top + scrollContainerOffset.y : targetOffset.left + scrollContainerOffset.x; offset += offsetAdjust; } else if (typeof(target) === 'number') { startPoint = target; } else if ($.isFunction(target)) { startPoint = target.call(this); } else {

                   targetOffset = target.offset();
                   startPoint = superscrollorama.settings.isVertical ? targetOffset.top + scrollContainerOffset.y : targetOffset.left + scrollContainerOffset.x;

offset += offsetAdjust; }

startPoint += offset; endPoint = startPoint + animObj.dur; // if the duration is 0 the animation should autoplay (forward going from BEFORE to AFTER and reverse going from AFTER to BEFORE)

if ((currScrollPoint > startPoint && currScrollPoint < endPoint) && animObj.state !== 'TWEENING') { // if it should be TWEENING and isn't.. animObj.state = 'TWEENING'; animObj.start = startPoint; animObj.end = endPoint; } if (currScrollPoint < startPoint && animObj.state !== 'BEFORE' && animObj.reverse) { // if it should be at the BEFORE tween state and isn't.. if (superscrollorama.settings.playoutAnimations || animObj.dur === 0) { animObj.tween.reverse(); } else { setTweenProgress(animObj.tween, 0); } animObj.state = 'BEFORE'; } else if (currScrollPoint > endPoint && animObj.state !== 'AFTER') { // if it should be at the AFTER tween state and isn't.. if (superscrollorama.settings.playoutAnimations || animObj.dur === 0) { animObj.tween.play(); } else { setTweenProgress(animObj.tween, 1); } animObj.state = 'AFTER'; } else if (animObj.state === 'TWEENING') { // if it is TWEENING.. var repeatIndefinitely = false; if (animObj.tween.repeat) { // does the tween have the repeat option (TweenMax / TimelineMax) repeatIndefinitely = (animObj.tween.repeat() === -1); }

if (repeatIndefinitely) { // if the animation loops indefinitely it will just play for the time of the duration var playheadPosition = animObj.tween.totalProgress(); // there is no "isPlaying" value so we need to save the playhead to determine whether the animation is running if (animObj.playeadLastPosition === null || playheadPosition === animObj.playeadLastPosition) { if (playheadPosition === 1) { if (animObj.tween.yoyo()) { // reverse Playback with infinitely looped tweens only works with yoyo true animObj.tween.reverse(); } else { animObj.tween.totalProgress(0).play(); } } else { animObj.tween.play(); } } animObj.playeadLastPosition = playheadPosition; } else { setTweenProgress(animObj.tween, (currScrollPoint - animObj.start)/(animObj.end - animObj.start)); } } }

// check all pinned elements var numPinned = pinnedObjects.length; for (i=0; i<numPinned; i++) { var pinObj = pinnedObjects[i]; var el = pinObj.el;

// should object be pinned (or updated)? if (pinObj.state !== 'PINNED') {

                   var pinObjSpacerOffset = pinObj.spacer.offset();

if (pinObj.state === 'UPDATE') { resetPinObj(pinObj); // revert to original Position so startPoint and endPoint will be calculated to the correct values }

startPoint = superscrollorama.settings.isVertical ? pinObjSpacerOffset.top + scrollContainerOffset.y : pinObjSpacerOffset.left + scrollContainerOffset.x; startPoint += pinObj.offset; endPoint = startPoint + pinObj.dur;

var jumpedPast = ((currScrollPoint > endPoint && pinObj.state === 'BEFORE') || (currScrollPoint < startPoint && pinObj.state === 'AFTER')); // if we jumped past a pinarea (i.e. when refreshing or using a function) we need to temporarily pin the element so it gets positioned to start or end respectively var inPinAra = (currScrollPoint > startPoint && currScrollPoint < endPoint); if (inPinAra || jumpedPast) { // set original position values for unpinning if (pinObj.pushFollowers && el.css('position') === "static") { // this can't be. If we want to pass following elements we need to at least allow relative positioning el.css('position', "relative"); } // save original positioning pinObj.origPositioning = { pos: el.css('position'), top: pinObj.spacer.css('top'), left: pinObj.spacer.css('left') }; // change to fixed position pinObj.fixedPositioning = { top: superscrollorama.settings.isVertical ? -pinObj.offset : pinObjSpacerOffset.top, left: superscrollorama.settings.isVertical ? pinObjSpacerOffset.left : -pinObj.offset }; el.css('position','fixed'); el.css('top', pinObj.fixedPositioning.top); el.css('left', pinObj.fixedPositioning.left);

// save values pinObj.pinStart = startPoint; pinObj.pinEnd = endPoint;

// If we want to push down following Items we need a spacer to do it, while and after our element is fixed. if (pinObj.pushFollowers) { if (superscrollorama.settings.isVertical) { pinObj.spacer.height(pinObj.dur + el.outerHeight()); } else { pinObj.spacer.width(pinObj.dur + el.outerWidth()); } } else { if (pinObj.origPositioning.pos === "absolute") { // no spacer pinObj.spacer.width(0); pinObj.spacer.height(0); } else { // spacer needs to reserve the elements space, while pinned if (superscrollorama.settings.isVertical) { pinObj.spacer.height(el.outerHeight()); } else { pinObj.spacer.width(el.outerWidth()); } } }


if (pinObj.state === "UPDATE") { if (pinObj.anim) { setTweenProgress(pinObj.anim, 0); // reset the progress, otherwise the animation won't be updated to the new position } } else if (pinObj.onPin) { pinObj.onPin(pinObj.state === "AFTER"); }

// pin it! pinObj.state = 'PINNED'; } } // If state changed to pinned (or already was) we need to position the element if (pinObj.state === 'PINNED') { // Check to see if object should be unpinned if (currScrollPoint < pinObj.pinStart || currScrollPoint > pinObj.pinEnd) { // unpin it var before = currScrollPoint < pinObj.pinStart; pinObj.state = before ? 'BEFORE' : 'AFTER'; // set Animation to end or beginning setTweenProgress(pinObj.anim, before ? 0 : 1);

var spacerSize = before ? 0 : pinObj.dur;

if (superscrollorama.settings.isVertical) { pinObj.spacer.height(pinObj.pushFollowers ? spacerSize : 0); } else { pinObj.spacer.width(pinObj.pushFollowers ? spacerSize : 0); }

// correct values if pin Object was moved (animated) during PIN (pinObj.el.css values will never be auto as they are set by the class) var deltay = pinObj.fixedPositioning.top - cssNumericPosition(pinObj.el).top; var deltax = pinObj.fixedPositioning.left - cssNumericPosition(pinObj.el).left;

// first revert to start values resetPinObj(pinObj);

// position element correctly if (!pinObj.pushFollowers || pinObj.origPositioning.pos === "absolute") { var pinOffset;

if (pinObj.origPositioning.pos === "relative") { // position relative and pushFollowers = false pinOffset = superscrollorama.settings.isVertical ? parseFloat(pinObj.origPositioning.top) : parseFloat(pinObj.origPositioning.left); if (isNaN(pinOffset)) { // if Position was "auto" parseFloat will result in NaN pinOffset = 0; } } else { pinOffset = superscrollorama.settings.isVertical ? pinObj.spacer.position().top : pinObj.spacer.position().left; }

var direction = superscrollorama.settings.isVertical ? "top" : "left";

pinObj.el.css(direction, pinOffset + spacerSize); } // if position relative and pushFollowers is true the element remains untouched.

// now correct values if they have been changed during pin if (deltay !== 0) { pinObj.el.css("top", cssNumericPosition(pinObj.el).top - deltay); } if (deltax !== 0) { pinObj.el.css("left", cssNumericPosition(pinObj.el).left - deltax); } if (pinObj.onUnpin) { pinObj.onUnpin(!before); } } else if (pinObj.anim) { // do animation setTweenProgress(pinObj.anim, (currScrollPoint - pinObj.pinStart)/(pinObj.pinEnd - pinObj.pinStart)); } } } }

// PUBLIC FUNCTIONS superscrollorama.addTween = function(target, tween, dur, offset, reverse) {

tween.pause();

animObjects.push({ target:target, tween: tween, offset: offset || 0, dur: dur || 0, reverse: (typeof reverse !== "undefined") ? reverse : superscrollorama.settings.reverse, // determine if reverse animation has been disabled state:'BEFORE' });

return superscrollorama; };

superscrollorama.pin = function(el, dur, vars) { if (typeof(el) === 'string') { el = $(el); } var defaults = { offset: 0, pushFollowers: true // if true following elements will be "pushed" down, if false the pinned element will just scroll past them }; vars = $.extend({}, defaults, vars); if (vars.anim) { vars.anim.pause(); }

var spacer = $('
');

spacer.css("position", "relative"); spacer.css("top", el.css("top")); spacer.css("left", el.css("left")); el.before(spacer);

pinnedObjects.push({ el:el, state:'BEFORE', dur:dur, offset: vars.offset, anim:vars.anim, pushFollowers:vars.pushFollowers, spacer:spacer, onPin:vars.onPin, onUnpin:vars.onUnpin }); return superscrollorama; };

superscrollorama.updatePin = function (el, dur, vars) { // Update a Pinned object. dur and vars are optional to only change vars and keep dur just pass NULL for dur if (typeof(el) === 'string') { el = $(el); } if (vars.anim) { vars.anim.pause(); }

var numPinned = pinnedObjects.length;

for (i=0; i<numPinned; i++) { var pinObj = pinnedObjects[i]; if (el.get(0) === pinObj.el.get(0)) {

if (dur) { pinObj.dur = dur; } if (vars.anim) { pinObj.anim = vars.anim; } if (vars.offset) { pinObj.offset = vars.offset; } if (typeof vars.pushFollowers !== "undefined") { pinObj.pushFollowers = vars.pushFollowers; } if (vars.onPin) { pinObj.onPin = vars.onPin; } if (vars.onUnpin) { pinObj.onUnpin = vars.onUnpin; } if ((dur || vars.anim || vars.offset) && pinObj.state === 'PINNED') { // this calls for an immediate update! pinObj.state = 'UPDATE'; checkScrollAnim(); } } } return superscrollorama; };

superscrollorama.removeTween = function (target, tween, reset) { var count = animObjects.length; if (typeof reset === "undefined") { reset = true; } for (var index = 0; index < count; index++) { var value = animObjects[index]; if (value.target === target && (!tween || value.tween === tween)) { // tween is optional. if not set just remove element animObjects.splice(index,1); if (reset) { setTweenProgress(value.tween, 0); } count--; index--; } } return superscrollorama; };

superscrollorama.removePin = function (el, reset) { if (typeof(el) === 'string') { el = $(el); } if (typeof reset === "undefined") { reset = true; } var count = pinnedObjects.length; for (var index = 0; index < count; index++) { var value = pinnedObjects[index]; if (value.el.is(el)) { pinnedObjects.splice(index,1); if (reset) { value.spacer.remove(); resetPinObj(value); if (value.anim) { setTweenProgress(value.anim, 0); } } count--; index--; } } return superscrollorama; };

superscrollorama.setScrollContainerOffset = function (x, y) { scrollContainerOffset.x = x; scrollContainerOffset.y = y; return superscrollorama; };

superscrollorama.triggerCheckAnim = function (immediately) { // if immedeately is true it will be updated right now, if false it will wait until next tweenmax tick. default is false if (immediately) { checkScrollAnim(); } else { doUpdateOnNextTick = true; } return superscrollorama; };


// INIT init();

return superscrollorama; };

})(jQuery);