From cdc78d7b9a74fa3a02e3e833045d17a06a537a12 Mon Sep 17 00:00:00 2001 From: Jan Paepke Date: Thu, 21 Aug 2014 20:24:29 +0200 Subject: [PATCH 01/72] Changed behavior to cache trigger element position MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit added ScrollScene.updateTriggerElementPosition. For now it calls functions direct. needs event… --- ScrollMagic.jquery.json | 2 +- bower.json | 2 +- dev/package.json | 2 +- dev/src/class.ScrollMagic.js | 19 ++- dev/src/class.ScrollScene.js | 123 ++++++++----- docs/ScrollMagic.html | 61 +++++-- docs/ScrollScene.html | 237 +++++++++++++++----------- docs/classes.list.html | 14 +- docs/index.html | 2 +- docs/jquery.scrollmagic.debug.js.html | 4 +- docs/jquery.scrollmagic.js.html | 146 ++++++++++------ js/jquery.scrollmagic.debug.js | 4 +- js/jquery.scrollmagic.js | 146 ++++++++++------ js/jquery.scrollmagic.min.js | 4 +- 14 files changed, 486 insertions(+), 280 deletions(-) diff --git a/ScrollMagic.jquery.json b/ScrollMagic.jquery.json index 6a9b729c..7acd6d88 100644 --- a/ScrollMagic.jquery.json +++ b/ScrollMagic.jquery.json @@ -1,6 +1,6 @@ { "name": "ScrollMagic", - "version": "1.0.9", + "version": "1.1.0", "title": "ScrollMagic", "author": { "name": "Jan Paepke", diff --git a/bower.json b/bower.json index 8a5573d9..f1c828f2 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "ScrollMagic", "description": "The jQuery plugin for magical scroll interactions.", - "version": "1.0.9", + "version": "1.1.0", "homepage": "http://janpaepke.github.io/ScrollMagic/", "author": { "name": "Jan Paepke", diff --git a/dev/package.json b/dev/package.json index 33ba1367..90e4a62d 100644 --- a/dev/package.json +++ b/dev/package.json @@ -1,6 +1,6 @@ { "name": "scrollmagic-dev", - "version": "1.0.9", + "version": "1.1.0", "private": true, "devDependencies": { "chalk": "^0.5.1", diff --git a/dev/src/class.ScrollMagic.js b/dev/src/class.ScrollMagic.js index 6742e9fa..9eb1a967 100644 --- a/dev/src/class.ScrollMagic.js +++ b/dev/src/class.ScrollMagic.js @@ -20,6 +20,7 @@ ** `1` => errors ** `2` => errors, warnings ** `3` => errors, warnings, debuginfo + * @param {boolean} [options.triggerPosUpdateInterval=100] - Interval in which the position of the trigger elements of the scenes are updated. If you don't use trigger elements or have static layouts, where the positions of the trigger elements don't change, you can set this to 0 disable interval checking and improve performance. * */ var ScrollMagic = function(options) { @@ -35,7 +36,8 @@ container: window, vertical: true, globalSceneOptions: {}, - loglevel: 2 + loglevel: 2, + triggerPosUpdateInterval: 100 }; /* @@ -54,7 +56,8 @@ _isDocument = true, _viewPortSize = 0, _tickerUsed = false, - _enabled = true; + _enabled = true, + _triggerPosUpdateInterval; /* * ---------------------------------------------------------------- @@ -76,7 +79,7 @@ _options.container = $(_options.container).first(); // check ScrolContainer if (_options.container.length === 0) { - log(1, "ERROR creating object ScrollMagic: No valid scroll container supplied"); + log(1, "ERROR creating object " + NAMESPACE + ": No valid scroll container supplied"); return; // cancel } _isDocument = !$.contains(document, _options.container.get(0)); @@ -92,6 +95,15 @@ _tickerUsed = false; } + _options.triggerPosUpdateInterval = parseInt(_options.triggerPosUpdateInterval); + if (_options.triggerPosUpdateInterval > 0) { + window.setInterval(function () { + $.each(_sceneObjects, function (index, scene) { + scene.updateTriggerElementPosition(); + }); + }, _options.triggerPosUpdateInterval); + } + log(3, "added new " + NAMESPACE + " controller"); }; @@ -495,6 +507,7 @@ * @returns {null} Null to unset handler variables. */ this.destroy = function (resetScenes) { + window.clearTimeout(_triggerPosUpdateInterval); while (_sceneObjects.length > 0) { var scene = _sceneObjects[_sceneObjects.length - 1]; scene.destroy(resetScenes); diff --git a/dev/src/class.ScrollScene.js b/dev/src/class.ScrollScene.js index e69b34cc..01e1083d 100644 --- a/dev/src/class.ScrollScene.js +++ b/dev/src/class.ScrollScene.js @@ -72,6 +72,7 @@ _state = 'BEFORE', _progress = 0, _scrollOffset = {start: 0, end: 0}, // reflects the parent's scroll position for the start and end of the scene respectively + _triggerOffset = 0, _enabled = true, _parent, _tween, @@ -95,8 +96,11 @@ // internal event listeners ScrollScene.on("change.internal", function (e) { checkOptionsValidity(); - if (e.what != "loglevel" && e.what != "tweenChanges") { // no need for a scene update scene with these options... - if (e.what != "reverse" && _options.triggerElement === null) { // otherwise not necessary or it will be updated in ScrollScene.update() + if (e.what !== "loglevel" && e.what !== "tweenChanges") { // no need for a scene update scene with these options... + if (e.what === "triggerElement") { + ScrollScene.updateTriggerElementPosition(); + } + if (e.what === "offset" || e.what === "duration" || e.what === "triggerHook") { updateScrollOffset(); } ScrollScene.update(); @@ -184,11 +188,16 @@ /** * Update the start and end scrollOffset of the container. * The positions reflect what the parent's scroll position will be at the start and end respectively. + * Is called, when: + * - ScrollScene event "change" is called with: offset, triggerHook, duration + * - scroll container event "resize" is called + * - the position of the triggerElement changes + * - the parent changes -> addTo() * @private */ var updateScrollOffset = function () { - _scrollOffset = {start: ScrollScene.triggerOffset()}; - if (_parent) { + _scrollOffset = {start: _triggerOffset + _options.offset}; + if (_parent && _options.triggerElement) { // take away triggerHook portion to get relative to top _scrollOffset.start -= _parent.info("size") * ScrollScene.triggerHook(); } @@ -203,8 +212,8 @@ * @return {boolean} true if the Tween was updated. */ var updateTweenProgress = function (to) { - var progress = (to >= 0 && to <= 1) ? to : _progress; if (_tween) { + var progress = (to >= 0 && to <= 1) ? to : _progress; if (_tween.repeat() === -1) { // infinite loop, so not in relation to progress if ((_state === "DURING" || (_state === "AFTER" && _options.duration === 0)) && _tween.paused()) { @@ -218,7 +227,7 @@ // no infinite loop - so should we just play or go to a specific point in time? if (_options.duration === 0) { // play the animation - if (_state == "AFTER") { // play from 0 to 1 + if (_state === "AFTER") { // play from 0 to 1 _tween.play(); } else { // play from 1 to 0 _tween.reverse(); @@ -641,18 +650,9 @@ this.state = function () { return _state; }; - - /** - * **Get** the trigger offset of the scene. - * @public - * @deprecated Method is deprecated since 1.0.7. You should now use {@link ScrollScene.triggerOffset} - */ - this.startPosition = function () { - return this.triggerOffset(); - }; /** - * **Get** the trigger offset of the scene. + * **Get** the trigger offset of the scene (including the value of the `offset` option). * @public * @example * // get the scene's trigger offset @@ -661,34 +661,15 @@ * @returns {number} Start position of the scene. Top position value for vertical and left position value for horizontal scrolls. */ this.triggerOffset = function () { - var pos = _options.offset; + var pos = _options.offset; // the offset is the basis if (_parent) { - var containerInfo = _parent.info(); // get the trigger position - if (_options.triggerElement === null) { - // return the triggerHook to start right at the beginning - pos += containerInfo.size * ScrollScene.triggerHook(); - } else { + if (_options.triggerElement) { // Element as trigger - var - element = $(_options.triggerElement).first(), - containerOffset = getOffset(_parent.info("container")); // container position is needed because element offset is returned in relation to document, not in relation to container. - - // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. - while (element.parent().data("ScrollMagicPinSpacer")) { - element = element.parent(); - } - - var elementOffset = getOffset(element); - - if (!containerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent - containerOffset.top -= containerInfo.scrollPos; - containerOffset.left -= containerInfo.scrollPos; - } - - pos += containerInfo.vertical ? - elementOffset.top - containerOffset.top - : elementOffset.left - containerOffset.left; + pos += _triggerOffset; + } else { + // return the height of the triggerHook to start at the beginning + pos += _parent.info("size") * ScrollScene.triggerHook(); } } return pos; @@ -743,10 +724,6 @@ var scrollPos = _parent.info("scrollPos"), newProgress; - // if triggerElement is set we need to update the start position as it may have changed. - if (_options.triggerElement !== null) { - updateScrollOffset(); - } if (_options.duration > 0) { newProgress = (scrollPos - _scrollOffset.start)/(_scrollOffset.end - _scrollOffset.start); @@ -767,6 +744,59 @@ return ScrollScene; }; + + /** + * Updates the position of the triggerElement, if present. + * This method is automatically called ... + * - ... when the triggerElement is changed + * - ... when the scene is added to a (new) controller + * - ... in regular intervals from the controller (TODO: SEE XYZ) + * + * You can call it to minimize lag, when you intentionally change the position of the triggerElement. + * @public + * @since v1.1.0 + * @example + * scene = new ScrollScene({triggerElement: "#trigger"}); + * + * // change the position of the trigger + * $("#trigger").css("top", 500); + * // immediately let the scene know of this change + * scene.updateTriggerElementPosition(); + * + * @returns {ScrollScene} Parent object for chaining. + */ + this.updateTriggerElementPosition = function () { + var elementPos = 0; + if (_parent && _options.triggerElement) { + var + element = $(_options.triggerElement).first(), + controllerInfo = _parent.info(), + containerOffset = getOffset(controllerInfo.container), // container position is needed because element offset is returned in relation to document, not in relation to container. + param = controllerInfo.vertical ? "top" : "left"; // which param is of interest ? + + // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. + while (element.parent().data("ScrollMagicPinSpacer")) { + element = element.parent(); + } + + var elementOffset = getOffset(element); + + if (!controllerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent + containerOffset[param] -= controllerInfo.scrollPos; + } + + elementPos = elementOffset[param] - containerOffset[param]; + } + var changed = elementPos != _triggerOffset; + _triggerOffset = elementPos; + if (changed) { + updateScrollOffset(); + ScrollScene.update(); + } + return ScrollScene; + }; + + /** * **Get** or **Set** the scene's progress. * Usually it shouldn't be necessary to use this as a setter, as it is set automatically by scene.update(). @@ -1086,9 +1116,11 @@ } _parent = controller; checkOptionsValidity(); + ScrollScene.updateTriggerElementPosition(); updateScrollOffset(); updatePinSpacerSize(); _parent.info("container").on("resize", updateRelativePinSpacer); + _parent.info("container").on("resize", updateScrollOffset); log(3, "added " + NAMESPACE + " to controller"); controller.addScene(ScrollScene); ScrollScene.update(); @@ -1136,6 +1168,7 @@ this.remove = function () { if (_parent) { _parent.info("container").off("resize", updateRelativePinSpacer); + _parent.info("container").off("resize", updateScrollOffset); var tmpParent = _parent; _parent = undefined; log(3, "removed " + NAMESPACE + " from controller"); diff --git a/docs/ScrollMagic.html b/docs/ScrollMagic.html index 1240de64..d48015df 100644 --- a/docs/ScrollMagic.html +++ b/docs/ScrollMagic.html @@ -373,6 +373,45 @@
Properties
+ + + + triggerPosUpdateInterval + + + + + +boolean + + + + + + + + + <optional>
+ + + + + + + + + + + + 100 + + + + +

Interval in which the position of the trigger elements of the scenes are updated. If you don't use trigger elements or have static layouts, where the positions of the trigger elements don't change, you can set this to 0 disable interval checking and improve performance.

+ + + @@ -408,7 +447,7 @@
Properties
Source:
@@ -553,7 +592,7 @@
Parameters:
Source:
@@ -712,7 +751,7 @@
Parameters:
Source:
@@ -863,7 +902,7 @@
Parameters:
Source:
@@ -1025,7 +1064,7 @@
Parameters:
Source:
@@ -1179,7 +1218,7 @@
Parameters:
Source:
@@ -1324,7 +1363,7 @@
Parameters:
Source:
@@ -1479,7 +1518,7 @@
Parameters:
Source:
@@ -1644,7 +1683,7 @@
Parameters:
Source:
@@ -1804,7 +1843,7 @@
Parameters:
Source:
@@ -1998,7 +2037,7 @@
Parameters:
Source:
diff --git a/docs/ScrollScene.html b/docs/ScrollScene.html index d2ae5520..67f039af 100644 --- a/docs/ScrollScene.html +++ b/docs/ScrollScene.html @@ -533,7 +533,7 @@
Properties
Source:
@@ -1117,7 +1117,7 @@
Parameters:
Source:
@@ -1276,7 +1276,7 @@
Parameters:
Source:
@@ -1427,7 +1427,7 @@
Parameters:
Source:
@@ -1596,7 +1596,7 @@
Parameters:
Source:
@@ -1750,7 +1750,7 @@
Parameters:
Source:
@@ -1950,7 +1950,7 @@
Parameters:
Source:
@@ -2101,7 +2101,7 @@
Parameters:
Source:
@@ -2281,7 +2281,7 @@
Parameters:
Source:
@@ -2371,7 +2371,7 @@

parentSource:
@@ -2522,7 +2522,7 @@
Parameters:
Source:
@@ -2640,7 +2640,7 @@

removeSource:
@@ -2799,7 +2799,7 @@
Parameters:
Source:
@@ -2958,7 +2958,7 @@
Parameters:
Source:
@@ -3109,7 +3109,7 @@
Parameters:
Source:
@@ -3217,7 +3217,7 @@

scrollOff
Source:
@@ -3549,7 +3549,7 @@

Properties
Source:
@@ -3688,7 +3688,7 @@
Parameters:
Source:
@@ -3732,77 +3732,6 @@
Example
- - - - -
-

startPosition()

- - -
-
- - -
-

Get the trigger offset of the scene.

-
- - - - - - - - - -
- - - - - - - - - -
Deprecated:
  • Method is deprecated since 1.0.7. You should now use ScrollScene.triggerOffset
    - - - - - - - - - - - -
    Source:
    -
    - - - - - - - -
    - - - - - - - - - - - - -
    @@ -3849,7 +3778,7 @@

    stateSource:
    @@ -4031,7 +3960,7 @@
    Parameters:
    Source:
    @@ -4185,7 +4114,7 @@
    Parameters:
    Source:
    @@ -4360,7 +4289,7 @@
    Parameters:
    Source:
    @@ -4435,7 +4364,7 @@

    triggerO
    -

    Get the trigger offset of the scene.

    +

    Get the trigger offset of the scene (including the value of the offset option).

    @@ -4468,7 +4397,7 @@

    triggerO
    Source:
    @@ -4619,7 +4548,7 @@

    Parameters:
    Source:
    @@ -4796,7 +4725,7 @@
    Parameters:
    Source:
    @@ -4845,6 +4774,106 @@
    Example
    + + + + +
    +

    updateTriggerElementPosition() → {ScrollScene}

    + + +
    +
    + + +
    +

    Updates the position of the triggerElement, if present. +This method is automatically called ...

    +
      +
    • ... when the triggerElement is changed
    • +
    • ... when the scene is added to a (new) controller
    • +
    • ... in regular intervals from the controller (TODO: SEE XYZ)
    • +
    +

    You can call it to minimize lag, when you intentionally change the position of the triggerElement.

    +
    + + + + + + + + + +
    + + + + + +
    Since:
    +
    • v1.1.0
    + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + +
    +

    Parent object for chaining.

    + + { +ScrollScene + +} + +
    + + + + + +
    Example
    + +
    scene = new ScrollScene({triggerElement: "#trigger"});
    
    // change the position of the trigger
    $("#trigger").css("top", 500);
    // immediately let the scene know of this change
    scene.updateTriggerElementPosition();
    + + +
    @@ -5068,7 +5097,7 @@
    Properties
    Source:
    @@ -5335,7 +5364,7 @@
    Properties
    Source:
    @@ -5602,7 +5631,7 @@
    Properties
    Source:
    @@ -5869,7 +5898,7 @@
    Properties
    Source:
    @@ -6136,7 +6165,7 @@
    Properties
    Source:
    @@ -6403,7 +6432,7 @@
    Properties
    Source:
    @@ -6670,7 +6699,7 @@
    Properties
    Source:
    diff --git a/docs/classes.list.html b/docs/classes.list.html index b76c326f..6ac356da 100644 --- a/docs/classes.list.html +++ b/docs/classes.list.html @@ -387,7 +387,7 @@
    Properties
    Source:
    @@ -654,7 +654,7 @@
    Properties
    Source:
    @@ -921,7 +921,7 @@
    Properties
    Source:
    @@ -1188,7 +1188,7 @@
    Properties
    Source:
    @@ -1455,7 +1455,7 @@
    Properties
    Source:
    @@ -1722,7 +1722,7 @@
    Properties
    Source:
    @@ -1989,7 +1989,7 @@
    Properties
    Source:
    diff --git a/docs/index.html b/docs/index.html index f4788ea9..ba4ee4bc 100644 --- a/docs/index.html +++ b/docs/index.html @@ -210,7 +210,7 @@

    Version:
    -
    • 1.0.9
    +
    • 1.1.0
    diff --git a/docs/jquery.scrollmagic.debug.js.html b/docs/jquery.scrollmagic.debug.js.html index f4e915a9..42396283 100644 --- a/docs/jquery.scrollmagic.debug.js.html +++ b/docs/jquery.scrollmagic.debug.js.html @@ -99,7 +99,7 @@

    Source: jquery.scrollmagic.debug.js

    /*
    -ScrollMagic v1.0.9
    +ScrollMagic v1.1.0
     The jQuery plugin for doing magical scroll interactions.
     (c) 2014 Jan Paepke (@janpaepke)
     License & Info: http://janpaepke.github.io/ScrollMagic
    @@ -112,7 +112,7 @@ 

    Source: jquery.scrollmagic.debug.js

    */ /* @overview Debug Extension for ScrollMagic. - @version 1.0.9 + @version 1.1.0 @license Dual licensed under MIT license and GPL. @author Jan Paepke - e-mail@janpaepke.de */ diff --git a/docs/jquery.scrollmagic.js.html b/docs/jquery.scrollmagic.js.html index 2da7ef45..0f1fe433 100644 --- a/docs/jquery.scrollmagic.js.html +++ b/docs/jquery.scrollmagic.js.html @@ -99,7 +99,7 @@

    Source: jquery.scrollmagic.js

    /*
    -ScrollMagic v1.0.9
    +ScrollMagic v1.1.0
     The jQuery plugin for doing magical scroll interactions.
     (c) 2014 Jan Paepke (@janpaepke)
     License & Info: http://janpaepke.github.io/ScrollMagic
    @@ -112,7 +112,7 @@ 

    Source: jquery.scrollmagic.js

    */ /** @overview ##Info -@version 1.0.9 +@version 1.1.0 @license Dual licensed under MIT license and GPL. @author Jan Paepke - e-mail@janpaepke.de @@ -150,6 +150,7 @@

    Source: jquery.scrollmagic.js

    ** `1` => errors ** `2` => errors, warnings ** `3` => errors, warnings, debuginfo + * @param {boolean} [options.triggerPosUpdateInterval=100] - Interval in which the position of the trigger elements of the scenes are updated. If you don't use trigger elements or have static layouts, where the positions of the trigger elements don't change, you can set this to 0 disable interval checking and improve performance. * */ var ScrollMagic = function(options) { @@ -165,7 +166,8 @@

    Source: jquery.scrollmagic.js

    container: window, vertical: true, globalSceneOptions: {}, - loglevel: 2 + loglevel: 2, + triggerPosUpdateInterval: 100 }; /* @@ -184,7 +186,8 @@

    Source: jquery.scrollmagic.js

    _isDocument = true, _viewPortSize = 0, _tickerUsed = false, - _enabled = true; + _enabled = true, + _triggerPosUpdateInterval; /* * ---------------------------------------------------------------- @@ -206,7 +209,7 @@

    Source: jquery.scrollmagic.js

    _options.container = $(_options.container).first(); // check ScrolContainer if (_options.container.length === 0) { - log(1, "ERROR creating object ScrollMagic: No valid scroll container supplied"); + log(1, "ERROR creating object " + NAMESPACE + ": No valid scroll container supplied"); return; // cancel } _isDocument = !$.contains(document, _options.container.get(0)); @@ -222,6 +225,15 @@

    Source: jquery.scrollmagic.js

    _tickerUsed = false; } + _options.triggerPosUpdateInterval = parseInt(_options.triggerPosUpdateInterval); + if (_options.triggerPosUpdateInterval > 0) { + window.setInterval(function () { + $.each(_sceneObjects, function (index, scene) { + scene.updateTriggerElementPosition(); + }); + }, _options.triggerPosUpdateInterval); + } + log(3, "added new " + NAMESPACE + " controller"); }; @@ -625,6 +637,7 @@

    Source: jquery.scrollmagic.js

    * @returns {null} Null to unset handler variables. */ this.destroy = function (resetScenes) { + window.clearTimeout(_triggerPosUpdateInterval); while (_sceneObjects.length > 0) { var scene = _sceneObjects[_sceneObjects.length - 1]; scene.destroy(resetScenes); @@ -718,6 +731,7 @@

    Source: jquery.scrollmagic.js

    _state = 'BEFORE', _progress = 0, _scrollOffset = {start: 0, end: 0}, // reflects the parent's scroll position for the start and end of the scene respectively + _triggerOffset = 0, _enabled = true, _parent, _tween, @@ -741,8 +755,11 @@

    Source: jquery.scrollmagic.js

    // internal event listeners ScrollScene.on("change.internal", function (e) { checkOptionsValidity(); - if (e.what != "loglevel" && e.what != "tweenChanges") { // no need for a scene update scene with these options... - if (e.what != "reverse" && _options.triggerElement === null) { // otherwise not necessary or it will be updated in ScrollScene.update() + if (e.what !== "loglevel" && e.what !== "tweenChanges") { // no need for a scene update scene with these options... + if (e.what === "triggerElement") { + ScrollScene.updateTriggerElementPosition(); + } + if (e.what === "offset" || e.what === "duration" || e.what === "triggerHook") { updateScrollOffset(); } ScrollScene.update(); @@ -830,11 +847,16 @@

    Source: jquery.scrollmagic.js

    /** * Update the start and end scrollOffset of the container. * The positions reflect what the parent's scroll position will be at the start and end respectively. + * Is called, when: + * - ScrollScene event "change" is called with: offset, triggerHook, duration + * - scroll container event "resize" is called + * - the position of the triggerElement changes + * - the parent changes -> addTo() * @private */ var updateScrollOffset = function () { - _scrollOffset = {start: ScrollScene.triggerOffset()}; - if (_parent) { + _scrollOffset = {start: _triggerOffset + _options.offset}; + if (_parent && _options.triggerElement) { // take away triggerHook portion to get relative to top _scrollOffset.start -= _parent.info("size") * ScrollScene.triggerHook(); } @@ -849,8 +871,8 @@

    Source: jquery.scrollmagic.js

    * @return {boolean} true if the Tween was updated. */ var updateTweenProgress = function (to) { - var progress = (to >= 0 && to <= 1) ? to : _progress; if (_tween) { + var progress = (to >= 0 && to <= 1) ? to : _progress; if (_tween.repeat() === -1) { // infinite loop, so not in relation to progress if ((_state === "DURING" || (_state === "AFTER" && _options.duration === 0)) && _tween.paused()) { @@ -864,7 +886,7 @@

    Source: jquery.scrollmagic.js

    // no infinite loop - so should we just play or go to a specific point in time? if (_options.duration === 0) { // play the animation - if (_state == "AFTER") { // play from 0 to 1 + if (_state === "AFTER") { // play from 0 to 1 _tween.play(); } else { // play from 1 to 0 _tween.reverse(); @@ -1287,18 +1309,9 @@

    Source: jquery.scrollmagic.js

    this.state = function () { return _state; }; - - /** - * **Get** the trigger offset of the scene. - * @public - * @deprecated Method is deprecated since 1.0.7. You should now use {@link ScrollScene.triggerOffset} - */ - this.startPosition = function () { - return this.triggerOffset(); - }; /** - * **Get** the trigger offset of the scene. + * **Get** the trigger offset of the scene (including the value of the `offset` option). * @public * @example * // get the scene's trigger offset @@ -1307,34 +1320,15 @@

    Source: jquery.scrollmagic.js

    * @returns {number} Start position of the scene. Top position value for vertical and left position value for horizontal scrolls. */ this.triggerOffset = function () { - var pos = _options.offset; + var pos = _options.offset; // the offset is the basis if (_parent) { - var containerInfo = _parent.info(); // get the trigger position - if (_options.triggerElement === null) { - // return the triggerHook to start right at the beginning - pos += containerInfo.size * ScrollScene.triggerHook(); - } else { + if (_options.triggerElement) { // Element as trigger - var - element = $(_options.triggerElement).first(), - containerOffset = getOffset(_parent.info("container")); // container position is needed because element offset is returned in relation to document, not in relation to container. - - // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. - while (element.parent().data("ScrollMagicPinSpacer")) { - element = element.parent(); - } - - var elementOffset = getOffset(element); - - if (!containerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent - containerOffset.top -= containerInfo.scrollPos; - containerOffset.left -= containerInfo.scrollPos; - } - - pos += containerInfo.vertical ? - elementOffset.top - containerOffset.top - : elementOffset.left - containerOffset.left; + pos += _triggerOffset; + } else { + // return the height of the triggerHook to start at the beginning + pos += _parent.info("size") * ScrollScene.triggerHook(); } } return pos; @@ -1389,10 +1383,6 @@

    Source: jquery.scrollmagic.js

    var scrollPos = _parent.info("scrollPos"), newProgress; - // if triggerElement is set we need to update the start position as it may have changed. - if (_options.triggerElement !== null) { - updateScrollOffset(); - } if (_options.duration > 0) { newProgress = (scrollPos - _scrollOffset.start)/(_scrollOffset.end - _scrollOffset.start); @@ -1413,6 +1403,59 @@

    Source: jquery.scrollmagic.js

    return ScrollScene; }; + + /** + * Updates the position of the triggerElement, if present. + * This method is automatically called ... + * - ... when the triggerElement is changed + * - ... when the scene is added to a (new) controller + * - ... in regular intervals from the controller (TODO: SEE XYZ) + * + * You can call it to minimize lag, when you intentionally change the position of the triggerElement. + * @public + * @since v1.1.0 + * @example + * scene = new ScrollScene({triggerElement: "#trigger"}); + * + * // change the position of the trigger + * $("#trigger").css("top", 500); + * // immediately let the scene know of this change + * scene.updateTriggerElementPosition(); + * + * @returns {ScrollScene} Parent object for chaining. + */ + this.updateTriggerElementPosition = function () { + var elementPos = 0; + if (_parent && _options.triggerElement) { + var + element = $(_options.triggerElement).first(), + controllerInfo = _parent.info(), + containerOffset = getOffset(controllerInfo.container), // container position is needed because element offset is returned in relation to document, not in relation to container. + param = controllerInfo.vertical ? "top" : "left"; // which param is of interest ? + + // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. + while (element.parent().data("ScrollMagicPinSpacer")) { + element = element.parent(); + } + + var elementOffset = getOffset(element); + + if (!controllerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent + containerOffset[param] -= controllerInfo.scrollPos; + } + + elementPos = elementOffset[param] - containerOffset[param]; + } + var changed = elementPos != _triggerOffset; + _triggerOffset = elementPos; + if (changed) { + updateScrollOffset(); + ScrollScene.update(); + } + return ScrollScene; + }; + + /** * **Get** or **Set** the scene's progress. * Usually it shouldn't be necessary to use this as a setter, as it is set automatically by scene.update(). @@ -1732,9 +1775,11 @@

    Source: jquery.scrollmagic.js

    } _parent = controller; checkOptionsValidity(); + ScrollScene.updateTriggerElementPosition(); updateScrollOffset(); updatePinSpacerSize(); _parent.info("container").on("resize", updateRelativePinSpacer); + _parent.info("container").on("resize", updateScrollOffset); log(3, "added " + NAMESPACE + " to controller"); controller.addScene(ScrollScene); ScrollScene.update(); @@ -1782,6 +1827,7 @@

    Source: jquery.scrollmagic.js

    this.remove = function () { if (_parent) { _parent.info("container").off("resize", updateRelativePinSpacer); + _parent.info("container").off("resize", updateScrollOffset); var tmpParent = _parent; _parent = undefined; log(3, "removed " + NAMESPACE + " from controller"); diff --git a/js/jquery.scrollmagic.debug.js b/js/jquery.scrollmagic.debug.js index ca83c63b..4fc39a4b 100644 --- a/js/jquery.scrollmagic.debug.js +++ b/js/jquery.scrollmagic.debug.js @@ -1,5 +1,5 @@ /* -ScrollMagic v1.0.9 +ScrollMagic v1.1.0 The jQuery plugin for doing magical scroll interactions. (c) 2014 Jan Paepke (@janpaepke) License & Info: http://janpaepke.github.io/ScrollMagic @@ -12,7 +12,7 @@ Greensock License info at http://www.greensock.com/licensing/ */ /* @overview Debug Extension for ScrollMagic. - @version 1.0.9 + @version 1.1.0 @license Dual licensed under MIT license and GPL. @author Jan Paepke - e-mail@janpaepke.de */ diff --git a/js/jquery.scrollmagic.js b/js/jquery.scrollmagic.js index 2c10e3c6..2b3158fc 100644 --- a/js/jquery.scrollmagic.js +++ b/js/jquery.scrollmagic.js @@ -1,5 +1,5 @@ /* -ScrollMagic v1.0.9 +ScrollMagic v1.1.0 The jQuery plugin for doing magical scroll interactions. (c) 2014 Jan Paepke (@janpaepke) License & Info: http://janpaepke.github.io/ScrollMagic @@ -12,7 +12,7 @@ Greensock License info at http://www.greensock.com/licensing/ */ /** @overview ##Info -@version 1.0.9 +@version 1.1.0 @license Dual licensed under MIT license and GPL. @author Jan Paepke - e-mail@janpaepke.de @@ -50,6 +50,7 @@ Greensock License info at http://www.greensock.com/licensing/ ** `1` => errors ** `2` => errors, warnings ** `3` => errors, warnings, debuginfo + * @param {boolean} [options.triggerPosUpdateInterval=100] - Interval in which the position of the trigger elements of the scenes are updated. If you don't use trigger elements or have static layouts, where the positions of the trigger elements don't change, you can set this to 0 disable interval checking and improve performance. * */ var ScrollMagic = function(options) { @@ -65,7 +66,8 @@ Greensock License info at http://www.greensock.com/licensing/ container: window, vertical: true, globalSceneOptions: {}, - loglevel: 2 + loglevel: 2, + triggerPosUpdateInterval: 100 }; /* @@ -84,7 +86,8 @@ Greensock License info at http://www.greensock.com/licensing/ _isDocument = true, _viewPortSize = 0, _tickerUsed = false, - _enabled = true; + _enabled = true, + _triggerPosUpdateInterval; /* * ---------------------------------------------------------------- @@ -106,7 +109,7 @@ Greensock License info at http://www.greensock.com/licensing/ _options.container = $(_options.container).first(); // check ScrolContainer if (_options.container.length === 0) { - log(1, "ERROR creating object ScrollMagic: No valid scroll container supplied"); + log(1, "ERROR creating object " + NAMESPACE + ": No valid scroll container supplied"); return; // cancel } _isDocument = !$.contains(document, _options.container.get(0)); @@ -122,6 +125,15 @@ Greensock License info at http://www.greensock.com/licensing/ _tickerUsed = false; } + _options.triggerPosUpdateInterval = parseInt(_options.triggerPosUpdateInterval); + if (_options.triggerPosUpdateInterval > 0) { + window.setInterval(function () { + $.each(_sceneObjects, function (index, scene) { + scene.updateTriggerElementPosition(); + }); + }, _options.triggerPosUpdateInterval); + } + log(3, "added new " + NAMESPACE + " controller"); }; @@ -525,6 +537,7 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {null} Null to unset handler variables. */ this.destroy = function (resetScenes) { + window.clearTimeout(_triggerPosUpdateInterval); while (_sceneObjects.length > 0) { var scene = _sceneObjects[_sceneObjects.length - 1]; scene.destroy(resetScenes); @@ -618,6 +631,7 @@ Greensock License info at http://www.greensock.com/licensing/ _state = 'BEFORE', _progress = 0, _scrollOffset = {start: 0, end: 0}, // reflects the parent's scroll position for the start and end of the scene respectively + _triggerOffset = 0, _enabled = true, _parent, _tween, @@ -641,8 +655,11 @@ Greensock License info at http://www.greensock.com/licensing/ // internal event listeners ScrollScene.on("change.internal", function (e) { checkOptionsValidity(); - if (e.what != "loglevel" && e.what != "tweenChanges") { // no need for a scene update scene with these options... - if (e.what != "reverse" && _options.triggerElement === null) { // otherwise not necessary or it will be updated in ScrollScene.update() + if (e.what !== "loglevel" && e.what !== "tweenChanges") { // no need for a scene update scene with these options... + if (e.what === "triggerElement") { + ScrollScene.updateTriggerElementPosition(); + } + if (e.what === "offset" || e.what === "duration" || e.what === "triggerHook") { updateScrollOffset(); } ScrollScene.update(); @@ -730,11 +747,16 @@ Greensock License info at http://www.greensock.com/licensing/ /** * Update the start and end scrollOffset of the container. * The positions reflect what the parent's scroll position will be at the start and end respectively. + * Is called, when: + * - ScrollScene event "change" is called with: offset, triggerHook, duration + * - scroll container event "resize" is called + * - the position of the triggerElement changes + * - the parent changes -> addTo() * @private */ var updateScrollOffset = function () { - _scrollOffset = {start: ScrollScene.triggerOffset()}; - if (_parent) { + _scrollOffset = {start: _triggerOffset + _options.offset}; + if (_parent && _options.triggerElement) { // take away triggerHook portion to get relative to top _scrollOffset.start -= _parent.info("size") * ScrollScene.triggerHook(); } @@ -749,8 +771,8 @@ Greensock License info at http://www.greensock.com/licensing/ * @return {boolean} true if the Tween was updated. */ var updateTweenProgress = function (to) { - var progress = (to >= 0 && to <= 1) ? to : _progress; if (_tween) { + var progress = (to >= 0 && to <= 1) ? to : _progress; if (_tween.repeat() === -1) { // infinite loop, so not in relation to progress if ((_state === "DURING" || (_state === "AFTER" && _options.duration === 0)) && _tween.paused()) { @@ -764,7 +786,7 @@ Greensock License info at http://www.greensock.com/licensing/ // no infinite loop - so should we just play or go to a specific point in time? if (_options.duration === 0) { // play the animation - if (_state == "AFTER") { // play from 0 to 1 + if (_state === "AFTER") { // play from 0 to 1 _tween.play(); } else { // play from 1 to 0 _tween.reverse(); @@ -1187,18 +1209,9 @@ Greensock License info at http://www.greensock.com/licensing/ this.state = function () { return _state; }; - - /** - * **Get** the trigger offset of the scene. - * @public - * @deprecated Method is deprecated since 1.0.7. You should now use {@link ScrollScene.triggerOffset} - */ - this.startPosition = function () { - return this.triggerOffset(); - }; /** - * **Get** the trigger offset of the scene. + * **Get** the trigger offset of the scene (including the value of the `offset` option). * @public * @example * // get the scene's trigger offset @@ -1207,34 +1220,15 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {number} Start position of the scene. Top position value for vertical and left position value for horizontal scrolls. */ this.triggerOffset = function () { - var pos = _options.offset; + var pos = _options.offset; // the offset is the basis if (_parent) { - var containerInfo = _parent.info(); // get the trigger position - if (_options.triggerElement === null) { - // return the triggerHook to start right at the beginning - pos += containerInfo.size * ScrollScene.triggerHook(); - } else { + if (_options.triggerElement) { // Element as trigger - var - element = $(_options.triggerElement).first(), - containerOffset = getOffset(_parent.info("container")); // container position is needed because element offset is returned in relation to document, not in relation to container. - - // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. - while (element.parent().data("ScrollMagicPinSpacer")) { - element = element.parent(); - } - - var elementOffset = getOffset(element); - - if (!containerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent - containerOffset.top -= containerInfo.scrollPos; - containerOffset.left -= containerInfo.scrollPos; - } - - pos += containerInfo.vertical ? - elementOffset.top - containerOffset.top - : elementOffset.left - containerOffset.left; + pos += _triggerOffset; + } else { + // return the height of the triggerHook to start at the beginning + pos += _parent.info("size") * ScrollScene.triggerHook(); } } return pos; @@ -1289,10 +1283,6 @@ Greensock License info at http://www.greensock.com/licensing/ var scrollPos = _parent.info("scrollPos"), newProgress; - // if triggerElement is set we need to update the start position as it may have changed. - if (_options.triggerElement !== null) { - updateScrollOffset(); - } if (_options.duration > 0) { newProgress = (scrollPos - _scrollOffset.start)/(_scrollOffset.end - _scrollOffset.start); @@ -1313,6 +1303,59 @@ Greensock License info at http://www.greensock.com/licensing/ return ScrollScene; }; + + /** + * Updates the position of the triggerElement, if present. + * This method is automatically called ... + * - ... when the triggerElement is changed + * - ... when the scene is added to a (new) controller + * - ... in regular intervals from the controller (TODO: SEE XYZ) + * + * You can call it to minimize lag, when you intentionally change the position of the triggerElement. + * @public + * @since v1.1.0 + * @example + * scene = new ScrollScene({triggerElement: "#trigger"}); + * + * // change the position of the trigger + * $("#trigger").css("top", 500); + * // immediately let the scene know of this change + * scene.updateTriggerElementPosition(); + * + * @returns {ScrollScene} Parent object for chaining. + */ + this.updateTriggerElementPosition = function () { + var elementPos = 0; + if (_parent && _options.triggerElement) { + var + element = $(_options.triggerElement).first(), + controllerInfo = _parent.info(), + containerOffset = getOffset(controllerInfo.container), // container position is needed because element offset is returned in relation to document, not in relation to container. + param = controllerInfo.vertical ? "top" : "left"; // which param is of interest ? + + // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. + while (element.parent().data("ScrollMagicPinSpacer")) { + element = element.parent(); + } + + var elementOffset = getOffset(element); + + if (!controllerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent + containerOffset[param] -= controllerInfo.scrollPos; + } + + elementPos = elementOffset[param] - containerOffset[param]; + } + var changed = elementPos != _triggerOffset; + _triggerOffset = elementPos; + if (changed) { + updateScrollOffset(); + ScrollScene.update(); + } + return ScrollScene; + }; + + /** * **Get** or **Set** the scene's progress. * Usually it shouldn't be necessary to use this as a setter, as it is set automatically by scene.update(). @@ -1632,9 +1675,11 @@ Greensock License info at http://www.greensock.com/licensing/ } _parent = controller; checkOptionsValidity(); + ScrollScene.updateTriggerElementPosition(); updateScrollOffset(); updatePinSpacerSize(); _parent.info("container").on("resize", updateRelativePinSpacer); + _parent.info("container").on("resize", updateScrollOffset); log(3, "added " + NAMESPACE + " to controller"); controller.addScene(ScrollScene); ScrollScene.update(); @@ -1682,6 +1727,7 @@ Greensock License info at http://www.greensock.com/licensing/ this.remove = function () { if (_parent) { _parent.info("container").off("resize", updateRelativePinSpacer); + _parent.info("container").off("resize", updateScrollOffset); var tmpParent = _parent; _parent = undefined; log(3, "removed " + NAMESPACE + " from controller"); diff --git a/js/jquery.scrollmagic.min.js b/js/jquery.scrollmagic.min.js index f73c2c6d..c309999f 100644 --- a/js/jquery.scrollmagic.min.js +++ b/js/jquery.scrollmagic.min.js @@ -1,2 +1,2 @@ -/* ScrollMagic v1.0.9 | (c) 2014 Jan Paepke (@janpaepke) | license & info: http://janpaepke.github.io/ScrollMagic */ -!function(e,t){"use strict";var r=function(r){var i="ScrollMagic",o={container:t,vertical:!0,globalSceneOptions:{},loglevel:2},l=this,a=e.extend({},o,r),c=[],g=!1,u=0,f="PAUSED",d=!0,h=0,p=!1,v=!0,m=function(){if(e.each(a,function(e){o.hasOwnProperty(e)||(S(2,'WARNING: Unknown option "'+e+'"'),delete a[e])}),a.container=e(a.container).first(),0===a.container.length)return void S(1,"ERROR creating object ScrollMagic: No valid scroll container supplied");d=!e.contains(document,a.container.get(0)),h=a.vertical?a.container.height():a.container.width(),a.container.on("scroll resize",y);try{TweenLite.ticker.addEventListener("tick",E),p=!0}catch(t){a.container.on("scroll resize",E),p=!1}S(3,"added new "+i+" controller")},w=function(){return a.vertical?a.container.scrollTop():a.container.scrollLeft()},R=function(e){a.vertical?a.container.scrollTop(e):a.container.scrollLeft(e)},E=function(){if(g&&v){var t=e.isArray(g)?g:c,r=u;u=l.scrollPos();var n=u-r;f=0===n?"PAUSED":n>0?"FORWARD":"REVERSE",l.updateScene(t,!0),0===t.length&&a.loglevel>=3&&S(3,"updating 0 Scenes (nothing added to controller)"),g=!1}},y=function(e){"resize"==e.type&&(h=a.vertical?a.container.height():a.container.width()),g=!0},S=function(e){if(a.loglevel>=e){var r="("+i+") ->",n=Array.prototype.splice.call(arguments,1),o=Function.prototype.bind.call(s,t);n.unshift(e,r),o.apply(t,n)}};return this.addScene=function(t){return e.isArray(t)?e.each(t,function(e,t){l.addScene(t)}):t.parent()!=l?t.addTo(l):-1==e.inArray(c,t)&&(c.push(t),e.each(a.globalSceneOptions,function(e,r){t[e]&&t[e].call(t,r)}),S(3,"added Scene ("+c.length+" total)")),l},this.removeScene=function(t){if(e.isArray(t))e.each(t,function(e,t){l.removeScene(t)});else{var r=e.inArray(t,c);r>-1&&(c.splice(r,1),t.remove(),S(3,"removed Scene ("+c.length+" total)"))}return l},this.updateScene=function(t,r){return e.isArray(t)?e.each(t,function(e,n){S(3,"updating Scene "+(e+1)+"/"+t.length+" ("+c.length+" total)"),l.updateScene(n,r)}):r?t.update(!0):(e.isArray(g)||(g=[]),-1==e.inArray(t,g)&&g.push(t)),l},this.update=function(e){return y({type:"resize"}),e&&E(),l},this.scrollTo=function(t){return t instanceof n?t.parent()===l?l.scrollTo(t.scrollOffset()):S(1,"The supplied scene does not belong to this controller."):e.isFunction(t)?R=t:R.call(a.container[0],t),l},this.scrollPos=function(t){return arguments.length?(e.isFunction(t)?w=t:S(2,"Provided value for method 'scrollPos' is not a function. To change the current scroll position use 'scrollTo()'."),l):w.call(l)},this.info=function(e){var t={size:h,vertical:a.vertical,scrollPos:u,scrollDirection:f,container:a.container,isDocument:d};return arguments.length?void 0!==t[e]?t[e]:void S(1,'ERROR: option "'+e+'" is not available'):t},this.loglevel=function(e){return arguments.length?(a.loglevel!=e&&(a.loglevel=e),l):a.loglevel},this.enabled=function(e){return arguments.length?(v!=e&&(v=!!e,l.updateScene(c,!0)),l):v},this.destroy=function(e){for(;c.length>0;){var t=c[c.length-1];t.destroy(e)}return a.container.off("scroll resize",y),p?TweenLite.ticker.removeEventListener("tick",E):a.container.off("scroll resize",E),S(3,"destroyed "+i+" (reset: "+(e?"true":"false")+")"),null},m(),l},n=function(r){var n,i,o,a,c=["onCenter","onEnter","onLeave"],g="ScrollScene",u={duration:0,offset:0,triggerElement:null,triggerHook:c[0],reverse:!0,tweenChanges:!1,loglevel:2},f=this,d=e.extend({},u,r),h="BEFORE",p=0,v={start:0,end:0},m=!0,w=function(){E(),f.on("change.internal",function(e){E(),"loglevel"!=e.what&&"tweenChanges"!=e.what&&("reverse"!=e.what&&null===d.triggerElement&&y(),f.update(),("DURING"!==h&&"duration"==e.what||"AFTER"===h&&0===d.duration)&&b())}),f.on("progress.internal",function(){S(),b()})},R=function(e){if(d.loglevel>=e){var r="("+g+") ->",n=Array.prototype.splice.call(arguments,1),i=Function.prototype.bind.call(s,t);n.unshift(e,r),i.apply(t,n)}},E=function(){if(e.each(d,function(e){u.hasOwnProperty(e)||(R(2,'WARNING: Unknown option "'+e+'"'),delete d[e])}),d.duration=parseFloat(d.duration),(!e.isNumeric(d.duration)||d.duration<0)&&(R(1,'ERROR: Invalid value for option "duration":',d.duration),d.duration=u.duration),d.offset=parseFloat(d.offset),e.isNumeric(d.offset)||(R(1,'ERROR: Invalid value for option "offset":',d.offset),d.offset=u.offset),null!==d.triggerElement&&0===e(d.triggerElement).length&&(R(1,'ERROR: Element defined in option "triggerElement" was not found:',d.triggerElement),d.triggerElement=u.triggerElement),e.isNumeric(d.triggerHook)||-1!=e.inArray(d.triggerHook,c)||(R(1,'ERROR: Invalid value for option "triggerHook": ',d.triggerHook),d.triggerHook=u.triggerHook),!e.isNumeric(d.loglevel)||d.loglevel<0||d.loglevel>3){var t=d.loglevel;d.loglevel=u.loglevel,R(1,'ERROR: Invalid value for option "loglevel":',t)}if(i&&n&&d.triggerElement&&d.loglevel>=2){var r=i.getTweensOf(e(d.triggerElement)),o=n.info("vertical");e.each(r,function(e,t){var r=t.vars.css||t.vars,n=o?void 0!==r.top||void 0!==r.bottom:void 0!==r.left||void 0!==r.right;return n?(R(2,"WARNING: Tweening the position of the trigger element affects the scene timing and should be avoided!"),!1):void 0})}},y=function(){v={start:f.triggerOffset()},n&&(v.start-=n.info("size")*f.triggerHook()),v.end=v.start+d.duration},S=function(e){var t=e>=0&&1>=e?e:p;if(i){if(-1===i.repeat())if(("DURING"===h||"AFTER"===h&&0===d.duration)&&i.paused())i.play();else{if("DURING"===h||i.paused())return!1;i.pause()}else{if(t==i.progress())return!1;0===d.duration?"AFTER"==h?i.play():i.reverse():d.tweenChanges?i.tweenTo(t*i.duration()):i.progress(t).pause()}return!0}return!1},b=function(e){if(o&&n){var t=n.info();if(e||"DURING"!==h&&("AFTER"!==h||0!==d.duration)){var r={position:a.inFlow?"relative":"absolute",top:0,left:0},i=o.css("position")!=r.position;a.pushFollowers?"AFTER"===h&&0===parseFloat(a.spacer.css("padding-top"))?i=!0:"BEFORE"===h&&0===parseFloat(a.spacer.css("padding-bottom"))&&(i=!0):r[t.vertical?"top":"left"]=d.duration*p,o.css(r),i&&(o.removeClass(a.pinnedClass),F())}else{"fixed"!=o.css("position")&&(o.css("position","fixed"),F(),o.addClass(a.pinnedClass));var s=l(a.spacer,!0),c=d.reverse||0===d.duration?t.scrollPos-v.start:Math.round(p*d.duration*10)/10;s.top-=parseFloat(a.spacer.css("margin-top")),s[t.vertical?"top":"left"]+=c,o.css({top:s.top,left:s.left})}}},F=function(){if(o&&n&&a.inFlow){var r="AFTER"===h,i="BEFORE"===h,s="DURING"===h,l="fixed"==o.css("position"),c=n.info("vertical"),g=a.spacer.children().first(),u=e.inArray(a.spacer.css("display"),["block","flex","list-item","table","-webkit-box"])>-1,f={};u?(f["margin-top"]=i||s&&l?o.css("margin-top"):"auto",f["margin-bottom"]=r||s&&l?o.css("margin-bottom"):"auto"):f["margin-top"]=f["margin-bottom"]="auto",a.relSize.width?l?e(t).width()==a.spacer.parent().width()?o.css("width","inherit"):o.css("width",a.spacer.width()):o.css("width","100%"):(f["min-width"]=g.outerWidth(!0),f.width=l?f["min-width"]:"auto"),a.relSize.height?l?e(t).height()==a.spacer.parent().height()?o.css("height","inherit"):o.css("height",a.spacer.height()):o.css("height","100%"):(f["min-height"]=g.outerHeight(!u),f.height=l?f["min-height"]:"auto"),a.pushFollowers&&(f["padding"+(c?"Top":"Left")]=d.duration*p,f["padding"+(c?"Bottom":"Right")]=d.duration*(1-p)),a.spacer.css(f)}},T=function(){n&&o&&"DURING"===h&&!n.info("isDocument")&&b()},A=function(){n&&o&&("DURING"===h||"AFTER"===h&&0===d.duration)&&(a.relSize.width&&e(t).width()!=a.spacer.parent().width()||a.relSize.height&&e(t).height()!=a.spacer.parent().height())&&F()};return this.parent=function(){return n},this.duration=function(e){return arguments.length?(d.duration!=e&&(d.duration=e,f.trigger("change",{what:"duration",newval:e})),f):d.duration},this.offset=function(e){return arguments.length?(d.offset!=e&&(d.offset=e,f.trigger("change",{what:"offset",newval:e})),f):d.offset},this.triggerElement=function(e){return arguments.length?(d.triggerElement!=e&&(d.triggerElement=e,f.trigger("change",{what:"triggerElement",newval:e})),f):d.triggerElement},this.triggerHook=function(t){if(!arguments.length){var r;if(e.isNumeric(d.triggerHook))r=d.triggerHook;else switch(d.triggerHook){case"onCenter":r=.5;break;case"onLeave":r=0;break;case"onEnter":default:r=1}return r}return d.triggerHook!=t&&(d.triggerHook=t,f.trigger("change",{what:"triggerHook",newval:t})),f},this.reverse=function(e){return arguments.length?(d.reverse!=e&&(d.reverse=e,f.trigger("change",{what:"reverse",newval:e})),f):d.reverse},this.tweenChanges=function(e){return arguments.length?(d.tweenChanges!=e&&(d.tweenChanges=e,f.trigger("change",{what:"tweenChanges",newval:e})),f):d.tweenChanges},this.loglevel=function(e){return arguments.length?(d.loglevel!=e&&(d.loglevel=e,f.trigger("change",{what:"loglevel",newval:e})),f):d.loglevel},this.state=function(){return h},this.startPosition=function(){return this.triggerOffset()},this.triggerOffset=function(){var t=d.offset;if(n){var r=n.info();if(null===d.triggerElement)t+=r.size*f.triggerHook();else{for(var i=e(d.triggerElement).first(),o=l(n.info("container"));i.parent().data("ScrollMagicPinSpacer");)i=i.parent();var s=l(i);r.isDocument||(o.top-=r.scrollPos,o.left-=r.scrollPos),t+=r.vertical?s.top-o.top:s.left-o.left}}return t},this.scrollOffset=function(){return v.start},this.update=function(e){if(n)if(e)if(n.enabled()&&m){var t,r=n.info("scrollPos");null!==d.triggerElement&&y(),t=d.duration>0?(r-v.start)/(v.end-v.start):r>=v.start?1:0,f.trigger("update",{startPos:v.start,endPos:v.end,scrollPos:r}),f.progress(t)}else o&&"fixed"==o.css("position")&&b(!0);else n.updateScene(f,!1);return f},this.progress=function(e){if(arguments.length){var t=!1,r=h,i=n?n.info("scrollDirection"):"PAUSED";if(0>=e&&"BEFORE"!==h&&(e>=p||d.reverse)?(p=0,h="BEFORE",t=!0):e>0&&1>e&&(e>=p||d.reverse)?(p=e,h="DURING",t=!0):e>=1&&"AFTER"!==h?(p=1,h="AFTER",t=!0):"DURING"!==h||d.reverse||b(),t){var o={progress:p,state:h,scrollDirection:i},s=h!=r,l="BEFORE"===h&&0===d.duration;s&&(("DURING"===h||0===d.duration)&&f.trigger("enter",o),("BEFORE"===h||"BEFORE"===r)&&f.trigger(l?"end":"start",o)),f.trigger("progress",o),s&&(("AFTER"===h||"AFTER"===r)&&f.trigger(l?"start":"end",o),("DURING"!==h||0===d.duration)&&f.trigger("leave",o))}return f}return p},this.setTween=function(e){i&&f.removeTween();try{i=new TimelineMax({smoothChildTiming:!0}).add(e).pause()}catch(t){R(1,"ERROR calling method 'setTween()': Supplied argument is not a valid TweenMaxObject")}finally{return e.repeat&&-1===e.repeat()&&(i.repeat(-1),i.yoyo(e.yoyo())),E(),R(3,"added tween"),S(),f}},this.removeTween=function(e){return i&&(e&&S(0),i.kill(),i=void 0,R(3,"removed tween (reset: "+(e?"true":"false")+")")),f},this.setPin=function(r,n){var i={pushFollowers:!0,spacerClass:"scrollmagic-pin-spacer",pinnedClass:""};if(n=e.extend({},i,n),r=e(r).first(),0===r.length)return R(1,"ERROR calling method 'setPin()': Invalid pin element supplied."),f;if("fixed"==r.css("position"))return R(1,"ERROR: Pin does not work with elements that are positioned 'fixed'."),f;if(o){if(o===r)return f;f.removePin()}o=r,o.parent().hide();var s="absolute"!=o.css("position"),l=o.css(["display","top","left","bottom","right"]),c=o.css(["width","height"]);o.parent().show();var g=e("
    ").addClass(n.spacerClass).css(l).data("ScrollMagicPinSpacer",!0).css({position:s?"relative":"absolute","margin-left":"auto","margin-right":"auto","box-sizing":"content-box","-moz-box-sizing":"content-box","-webkit-box-sizing":"content-box"});return!s&&n.pushFollowers&&(R(2,"WARNING: If the pinned element is positioned absolutely pushFollowers is disabled."),n.pushFollowers=!1),a={spacer:g,relSize:{width:"%"===c.width.slice(-1),height:"%"===c.height.slice(-1)},pushFollowers:n.pushFollowers,inFlow:s,origStyle:o.attr("style"),pinnedClass:n.pinnedClass},a.relSize.width&&g.css("width",c.width),a.relSize.height&&g.css("height",c.height),o.before(g).appendTo(g).css({position:s?"relative":"absolute",top:"auto",left:"auto",bottom:"auto",right:"auto"}),e(t).on("scroll resize",T),R(3,"added pin"),b(),f},this.removePin=function(r){return o&&(r||!n?(o.insertBefore(a.spacer).attr("style",a.origStyle),a.spacer.remove()):"DURING"===h&&b(!0),e(t).off("scroll resize",T),o=void 0,R(3,"removed pin (reset: "+(r?"true":"false")+")")),f},this.addTo=function(e){return n!=e?(n&&n.removeScene(f),n=e,E(),y(),F(),n.info("container").on("resize",A),R(3,"added "+g+" to controller"),e.addScene(f),f.update(),f):void 0},this.enabled=function(e){return arguments.length?(m!=e&&(m=!!e,f.update(!0)),f):m},this.remove=function(){if(n){n.info("container").off("resize",A);var e=n;n=void 0,R(3,"removed "+g+" from controller"),e.removeScene(f)}return f},this.destroy=function(e){return this.removeTween(e),this.removePin(e),this.remove(),this.off("start end enter leave progress change update change.internal progress.internal"),R(3,"destroyed "+g+" (reset: "+(e?"true":"false")+")"),null},this.on=function(t,r){if(e.isFunction(r)){var n=e.trim(t).toLowerCase().replace(/(\w+)\.(\w+)/g,"$1."+g+"_$2").replace(/( |^)(\w+)( |$)/g,"$1$2."+g+"$3");e(f).on(n,r)}else R(1,"ERROR calling method 'on()': Supplied argument is not a valid callback!");return f},this.off=function(t,r){var n=e.trim(t).toLowerCase().replace(/(\w+)\.(\w+)/g,"$1."+g+"_$2").replace(/( |^)(\w+)( |$)/g,"$1$2."+g+"$3");return e(f).off(n,r),f},this.trigger=function(t,r){R(3,"event fired:",t,"->",r);var n={type:e.trim(t).toLowerCase(),target:f};return e.isPlainObject(r)&&(n=e.extend({},r,n)),e(f).trigger(n),f},w(),f};t.ScrollScene=n,t.ScrollMagic=r;var i=t.console=t.console||{},o=["error","warn","log"];i.log||(i.log=e.noop),e.each(o,function(e,t){i[t]||(i[t]=i.log)});var s=function(e){(e>o.length||0>=e)&&(e=o.length);var t=new Date,r=("0"+t.getHours()).slice(-2)+":"+("0"+t.getMinutes()).slice(-2)+":"+("0"+t.getSeconds()).slice(-2)+":"+("00"+t.getMilliseconds()).slice(-3),n=o[e-1],s=Array.prototype.splice.call(arguments,1),l=Function.prototype.bind.call(i[n],i);s.unshift(r),l.apply(i,s)},l=function(t,r){var n={top:0,left:0},i=t[0];if(i)if(i.getBoundingClientRect){var o=i.getBoundingClientRect();n.top=o.top,n.left=o.left,r||(n.top+=e(document).scrollTop(),n.left+=e(document).scrollLeft())}else n=t.offset()||n,r&&(n.top-=e(document).scrollTop(),n.left-=e(document).scrollLeft());return n}}(jQuery,window); \ No newline at end of file +/* ScrollMagic v1.1.0 | (c) 2014 Jan Paepke (@janpaepke) | license & info: http://janpaepke.github.io/ScrollMagic */ +!function(e,t){"use strict";var r=function(r){var i,o="ScrollMagic",a={container:t,vertical:!0,globalSceneOptions:{},loglevel:2,triggerPosUpdateInterval:100},l=this,c=e.extend({},a,r),g=[],u=!1,d=0,f="PAUSED",h=!0,p=0,v=!1,m=!0,w=function(){if(e.each(c,function(e){a.hasOwnProperty(e)||(b(2,'WARNING: Unknown option "'+e+'"'),delete c[e])}),c.container=e(c.container).first(),0===c.container.length)return void b(1,"ERROR creating object "+o+": No valid scroll container supplied");h=!e.contains(document,c.container.get(0)),p=c.vertical?c.container.height():c.container.width(),c.container.on("scroll resize",S);try{TweenLite.ticker.addEventListener("tick",y),v=!0}catch(r){c.container.on("scroll resize",y),v=!1}c.triggerPosUpdateInterval=parseInt(c.triggerPosUpdateInterval),c.triggerPosUpdateInterval>0&&t.setInterval(function(){e.each(g,function(e,t){t.updateTriggerElementPosition()})},c.triggerPosUpdateInterval),b(3,"added new "+o+" controller")},R=function(){return c.vertical?c.container.scrollTop():c.container.scrollLeft()},E=function(e){c.vertical?c.container.scrollTop(e):c.container.scrollLeft(e)},y=function(){if(u&&m){var t=e.isArray(u)?u:g,r=d;d=l.scrollPos();var n=d-r;f=0===n?"PAUSED":n>0?"FORWARD":"REVERSE",l.updateScene(t,!0),0===t.length&&c.loglevel>=3&&b(3,"updating 0 Scenes (nothing added to controller)"),u=!1}},S=function(e){"resize"==e.type&&(p=c.vertical?c.container.height():c.container.width()),u=!0},b=function(e){if(c.loglevel>=e){var r="("+o+") ->",n=Array.prototype.splice.call(arguments,1),i=Function.prototype.bind.call(s,t);n.unshift(e,r),i.apply(t,n)}};return this.addScene=function(t){return e.isArray(t)?e.each(t,function(e,t){l.addScene(t)}):t.parent()!=l?t.addTo(l):-1==e.inArray(g,t)&&(g.push(t),e.each(c.globalSceneOptions,function(e,r){t[e]&&t[e].call(t,r)}),b(3,"added Scene ("+g.length+" total)")),l},this.removeScene=function(t){if(e.isArray(t))e.each(t,function(e,t){l.removeScene(t)});else{var r=e.inArray(t,g);r>-1&&(g.splice(r,1),t.remove(),b(3,"removed Scene ("+g.length+" total)"))}return l},this.updateScene=function(t,r){return e.isArray(t)?e.each(t,function(e,n){b(3,"updating Scene "+(e+1)+"/"+t.length+" ("+g.length+" total)"),l.updateScene(n,r)}):r?t.update(!0):(e.isArray(u)||(u=[]),-1==e.inArray(t,u)&&u.push(t)),l},this.update=function(e){return S({type:"resize"}),e&&y(),l},this.scrollTo=function(t){return t instanceof n?t.parent()===l?l.scrollTo(t.scrollOffset()):b(1,"The supplied scene does not belong to this controller."):e.isFunction(t)?E=t:E.call(c.container[0],t),l},this.scrollPos=function(t){return arguments.length?(e.isFunction(t)?R=t:b(2,"Provided value for method 'scrollPos' is not a function. To change the current scroll position use 'scrollTo()'."),l):R.call(l)},this.info=function(e){var t={size:p,vertical:c.vertical,scrollPos:d,scrollDirection:f,container:c.container,isDocument:h};return arguments.length?void 0!==t[e]?t[e]:void b(1,'ERROR: option "'+e+'" is not available'):t},this.loglevel=function(e){return arguments.length?(c.loglevel!=e&&(c.loglevel=e),l):c.loglevel},this.enabled=function(e){return arguments.length?(m!=e&&(m=!!e,l.updateScene(g,!0)),l):m},this.destroy=function(e){for(t.clearTimeout(i);g.length>0;){var r=g[g.length-1];r.destroy(e)}return c.container.off("scroll resize",S),v?TweenLite.ticker.removeEventListener("tick",y):c.container.off("scroll resize",y),b(3,"destroyed "+o+" (reset: "+(e?"true":"false")+")"),null},w(),l},n=function(r){var n,i,o,l,c=["onCenter","onEnter","onLeave"],g="ScrollScene",u={duration:0,offset:0,triggerElement:null,triggerHook:c[0],reverse:!0,tweenChanges:!1,loglevel:2},d=this,f=e.extend({},u,r),h="BEFORE",p=0,v={start:0,end:0},m=0,w=!0,R=function(){y(),d.on("change.internal",function(e){y(),"loglevel"!==e.what&&"tweenChanges"!==e.what&&("triggerElement"===e.what&&d.updateTriggerElementPosition(),("offset"===e.what||"duration"===e.what||"triggerHook"===e.what)&&S(),d.update(),("DURING"!==h&&"duration"==e.what||"AFTER"===h&&0===f.duration)&&F())}),d.on("progress.internal",function(){b(),F()})},E=function(e){if(f.loglevel>=e){var r="("+g+") ->",n=Array.prototype.splice.call(arguments,1),i=Function.prototype.bind.call(s,t);n.unshift(e,r),i.apply(t,n)}},y=function(){if(e.each(f,function(e){u.hasOwnProperty(e)||(E(2,'WARNING: Unknown option "'+e+'"'),delete f[e])}),f.duration=parseFloat(f.duration),(!e.isNumeric(f.duration)||f.duration<0)&&(E(1,'ERROR: Invalid value for option "duration":',f.duration),f.duration=u.duration),f.offset=parseFloat(f.offset),e.isNumeric(f.offset)||(E(1,'ERROR: Invalid value for option "offset":',f.offset),f.offset=u.offset),null!==f.triggerElement&&0===e(f.triggerElement).length&&(E(1,'ERROR: Element defined in option "triggerElement" was not found:',f.triggerElement),f.triggerElement=u.triggerElement),e.isNumeric(f.triggerHook)||-1!=e.inArray(f.triggerHook,c)||(E(1,'ERROR: Invalid value for option "triggerHook": ',f.triggerHook),f.triggerHook=u.triggerHook),!e.isNumeric(f.loglevel)||f.loglevel<0||f.loglevel>3){var t=f.loglevel;f.loglevel=u.loglevel,E(1,'ERROR: Invalid value for option "loglevel":',t)}if(i&&n&&f.triggerElement&&f.loglevel>=2){var r=i.getTweensOf(e(f.triggerElement)),o=n.info("vertical");e.each(r,function(e,t){var r=t.vars.css||t.vars,n=o?void 0!==r.top||void 0!==r.bottom:void 0!==r.left||void 0!==r.right;return n?(E(2,"WARNING: Tweening the position of the trigger element affects the scene timing and should be avoided!"),!1):void 0})}},S=function(){v={start:m+f.offset},n&&f.triggerElement&&(v.start-=n.info("size")*d.triggerHook()),v.end=v.start+f.duration},b=function(e){if(i){var t=e>=0&&1>=e?e:p;if(-1===i.repeat())if(("DURING"===h||"AFTER"===h&&0===f.duration)&&i.paused())i.play();else{if("DURING"===h||i.paused())return!1;i.pause()}else{if(t==i.progress())return!1;0===f.duration?"AFTER"===h?i.play():i.reverse():f.tweenChanges?i.tweenTo(t*i.duration()):i.progress(t).pause()}return!0}return!1},F=function(e){if(o&&n){var t=n.info();if(e||"DURING"!==h&&("AFTER"!==h||0!==f.duration)){var r={position:l.inFlow?"relative":"absolute",top:0,left:0},i=o.css("position")!=r.position;l.pushFollowers?"AFTER"===h&&0===parseFloat(l.spacer.css("padding-top"))?i=!0:"BEFORE"===h&&0===parseFloat(l.spacer.css("padding-bottom"))&&(i=!0):r[t.vertical?"top":"left"]=f.duration*p,o.css(r),i&&(o.removeClass(l.pinnedClass),T())}else{"fixed"!=o.css("position")&&(o.css("position","fixed"),T(),o.addClass(l.pinnedClass));var s=a(l.spacer,!0),c=f.reverse||0===f.duration?t.scrollPos-v.start:Math.round(p*f.duration*10)/10;s.top-=parseFloat(l.spacer.css("margin-top")),s[t.vertical?"top":"left"]+=c,o.css({top:s.top,left:s.left})}}},T=function(){if(o&&n&&l.inFlow){var r="AFTER"===h,i="BEFORE"===h,s="DURING"===h,a="fixed"==o.css("position"),c=n.info("vertical"),g=l.spacer.children().first(),u=e.inArray(l.spacer.css("display"),["block","flex","list-item","table","-webkit-box"])>-1,d={};u?(d["margin-top"]=i||s&&a?o.css("margin-top"):"auto",d["margin-bottom"]=r||s&&a?o.css("margin-bottom"):"auto"):d["margin-top"]=d["margin-bottom"]="auto",l.relSize.width?a?e(t).width()==l.spacer.parent().width()?o.css("width","inherit"):o.css("width",l.spacer.width()):o.css("width","100%"):(d["min-width"]=g.outerWidth(!0),d.width=a?d["min-width"]:"auto"),l.relSize.height?a?e(t).height()==l.spacer.parent().height()?o.css("height","inherit"):o.css("height",l.spacer.height()):o.css("height","100%"):(d["min-height"]=g.outerHeight(!u),d.height=a?d["min-height"]:"auto"),l.pushFollowers&&(d["padding"+(c?"Top":"Left")]=f.duration*p,d["padding"+(c?"Bottom":"Right")]=f.duration*(1-p)),l.spacer.css(d)}},P=function(){n&&o&&"DURING"===h&&!n.info("isDocument")&&F()},A=function(){n&&o&&("DURING"===h||"AFTER"===h&&0===f.duration)&&(l.relSize.width&&e(t).width()!=l.spacer.parent().width()||l.relSize.height&&e(t).height()!=l.spacer.parent().height())&&T()};return this.parent=function(){return n},this.duration=function(e){return arguments.length?(f.duration!=e&&(f.duration=e,d.trigger("change",{what:"duration",newval:e})),d):f.duration},this.offset=function(e){return arguments.length?(f.offset!=e&&(f.offset=e,d.trigger("change",{what:"offset",newval:e})),d):f.offset},this.triggerElement=function(e){return arguments.length?(f.triggerElement!=e&&(f.triggerElement=e,d.trigger("change",{what:"triggerElement",newval:e})),d):f.triggerElement},this.triggerHook=function(t){if(!arguments.length){var r;if(e.isNumeric(f.triggerHook))r=f.triggerHook;else switch(f.triggerHook){case"onCenter":r=.5;break;case"onLeave":r=0;break;case"onEnter":default:r=1}return r}return f.triggerHook!=t&&(f.triggerHook=t,d.trigger("change",{what:"triggerHook",newval:t})),d},this.reverse=function(e){return arguments.length?(f.reverse!=e&&(f.reverse=e,d.trigger("change",{what:"reverse",newval:e})),d):f.reverse},this.tweenChanges=function(e){return arguments.length?(f.tweenChanges!=e&&(f.tweenChanges=e,d.trigger("change",{what:"tweenChanges",newval:e})),d):f.tweenChanges},this.loglevel=function(e){return arguments.length?(f.loglevel!=e&&(f.loglevel=e,d.trigger("change",{what:"loglevel",newval:e})),d):f.loglevel},this.state=function(){return h},this.triggerOffset=function(){var e=f.offset;return n&&(e+=f.triggerElement?m:n.info("size")*d.triggerHook()),e},this.scrollOffset=function(){return v.start},this.update=function(e){if(n)if(e)if(n.enabled()&&w){var t,r=n.info("scrollPos");t=f.duration>0?(r-v.start)/(v.end-v.start):r>=v.start?1:0,d.trigger("update",{startPos:v.start,endPos:v.end,scrollPos:r}),d.progress(t)}else o&&"fixed"==o.css("position")&&F(!0);else n.updateScene(d,!1);return d},this.updateTriggerElementPosition=function(){var t=0;if(n&&f.triggerElement){for(var r=e(f.triggerElement).first(),i=n.info(),o=a(i.container),s=i.vertical?"top":"left";r.parent().data("ScrollMagicPinSpacer");)r=r.parent();var l=a(r);i.isDocument||(o[s]-=i.scrollPos),t=l[s]-o[s]}var c=t!=m;return m=t,c&&(S(),d.update()),d},this.progress=function(e){if(arguments.length){var t=!1,r=h,i=n?n.info("scrollDirection"):"PAUSED";if(0>=e&&"BEFORE"!==h&&(e>=p||f.reverse)?(p=0,h="BEFORE",t=!0):e>0&&1>e&&(e>=p||f.reverse)?(p=e,h="DURING",t=!0):e>=1&&"AFTER"!==h?(p=1,h="AFTER",t=!0):"DURING"!==h||f.reverse||F(),t){var o={progress:p,state:h,scrollDirection:i},s=h!=r,a="BEFORE"===h&&0===f.duration;s&&(("DURING"===h||0===f.duration)&&d.trigger("enter",o),("BEFORE"===h||"BEFORE"===r)&&d.trigger(a?"end":"start",o)),d.trigger("progress",o),s&&(("AFTER"===h||"AFTER"===r)&&d.trigger(a?"start":"end",o),("DURING"!==h||0===f.duration)&&d.trigger("leave",o))}return d}return p},this.setTween=function(e){i&&d.removeTween();try{i=new TimelineMax({smoothChildTiming:!0}).add(e).pause()}catch(t){E(1,"ERROR calling method 'setTween()': Supplied argument is not a valid TweenMaxObject")}finally{return e.repeat&&-1===e.repeat()&&(i.repeat(-1),i.yoyo(e.yoyo())),y(),E(3,"added tween"),b(),d}},this.removeTween=function(e){return i&&(e&&b(0),i.kill(),i=void 0,E(3,"removed tween (reset: "+(e?"true":"false")+")")),d},this.setPin=function(r,n){var i={pushFollowers:!0,spacerClass:"scrollmagic-pin-spacer",pinnedClass:""};if(n=e.extend({},i,n),r=e(r).first(),0===r.length)return E(1,"ERROR calling method 'setPin()': Invalid pin element supplied."),d;if("fixed"==r.css("position"))return E(1,"ERROR: Pin does not work with elements that are positioned 'fixed'."),d;if(o){if(o===r)return d;d.removePin()}o=r,o.parent().hide();var s="absolute"!=o.css("position"),a=o.css(["display","top","left","bottom","right"]),c=o.css(["width","height"]);o.parent().show();var g=e("
    ").addClass(n.spacerClass).css(a).data("ScrollMagicPinSpacer",!0).css({position:s?"relative":"absolute","margin-left":"auto","margin-right":"auto","box-sizing":"content-box","-moz-box-sizing":"content-box","-webkit-box-sizing":"content-box"});return!s&&n.pushFollowers&&(E(2,"WARNING: If the pinned element is positioned absolutely pushFollowers is disabled."),n.pushFollowers=!1),l={spacer:g,relSize:{width:"%"===c.width.slice(-1),height:"%"===c.height.slice(-1)},pushFollowers:n.pushFollowers,inFlow:s,origStyle:o.attr("style"),pinnedClass:n.pinnedClass},l.relSize.width&&g.css("width",c.width),l.relSize.height&&g.css("height",c.height),o.before(g).appendTo(g).css({position:s?"relative":"absolute",top:"auto",left:"auto",bottom:"auto",right:"auto"}),e(t).on("scroll resize",P),E(3,"added pin"),F(),d},this.removePin=function(r){return o&&(r||!n?(o.insertBefore(l.spacer).attr("style",l.origStyle),l.spacer.remove()):"DURING"===h&&F(!0),e(t).off("scroll resize",P),o=void 0,E(3,"removed pin (reset: "+(r?"true":"false")+")")),d},this.addTo=function(e){return n!=e?(n&&n.removeScene(d),n=e,y(),d.updateTriggerElementPosition(),S(),T(),n.info("container").on("resize",A),n.info("container").on("resize",S),E(3,"added "+g+" to controller"),e.addScene(d),d.update(),d):void 0},this.enabled=function(e){return arguments.length?(w!=e&&(w=!!e,d.update(!0)),d):w},this.remove=function(){if(n){n.info("container").off("resize",A),n.info("container").off("resize",S);var e=n;n=void 0,E(3,"removed "+g+" from controller"),e.removeScene(d)}return d},this.destroy=function(e){return this.removeTween(e),this.removePin(e),this.remove(),this.off("start end enter leave progress change update change.internal progress.internal"),E(3,"destroyed "+g+" (reset: "+(e?"true":"false")+")"),null},this.on=function(t,r){if(e.isFunction(r)){var n=e.trim(t).toLowerCase().replace(/(\w+)\.(\w+)/g,"$1."+g+"_$2").replace(/( |^)(\w+)( |$)/g,"$1$2."+g+"$3");e(d).on(n,r)}else E(1,"ERROR calling method 'on()': Supplied argument is not a valid callback!");return d},this.off=function(t,r){var n=e.trim(t).toLowerCase().replace(/(\w+)\.(\w+)/g,"$1."+g+"_$2").replace(/( |^)(\w+)( |$)/g,"$1$2."+g+"$3");return e(d).off(n,r),d},this.trigger=function(t,r){E(3,"event fired:",t,"->",r);var n={type:e.trim(t).toLowerCase(),target:d};return e.isPlainObject(r)&&(n=e.extend({},r,n)),e(d).trigger(n),d},R(),d};t.ScrollScene=n,t.ScrollMagic=r;var i=t.console=t.console||{},o=["error","warn","log"];i.log||(i.log=e.noop),e.each(o,function(e,t){i[t]||(i[t]=i.log)});var s=function(e){(e>o.length||0>=e)&&(e=o.length);var t=new Date,r=("0"+t.getHours()).slice(-2)+":"+("0"+t.getMinutes()).slice(-2)+":"+("0"+t.getSeconds()).slice(-2)+":"+("00"+t.getMilliseconds()).slice(-3),n=o[e-1],s=Array.prototype.splice.call(arguments,1),a=Function.prototype.bind.call(i[n],i);s.unshift(r),a.apply(i,s)},a=function(t,r){var n={top:0,left:0},i=t[0];if(i)if(i.getBoundingClientRect){var o=i.getBoundingClientRect();n.top=o.top,n.left=o.left,r||(n.top+=e(document).scrollTop(),n.left+=e(document).scrollLeft())}else n=t.offset()||n,r&&(n.top-=e(document).scrollTop(),n.left-=e(document).scrollLeft());return n}}(jQuery,window); \ No newline at end of file From ce1e7358307b0cf4d14c0f02b04e6f16e8cee0fc Mon Sep 17 00:00:00 2001 From: Jan Paepke Date: Fri, 22 Aug 2014 16:48:44 +0200 Subject: [PATCH 02/72] updated docs to group methods --- dev/docs/jsdoc.conf.json | 14 + dev/docs/template/tmpl/container.tmpl | 32 +- dev/src/class.ScrollScene.js | 2 +- docs/ScrollMagic.html | 336 ++-- docs/ScrollScene.html | 2552 +++++++++++++------------ docs/jquery.scrollmagic.js.html | 2 +- js/jquery.scrollmagic.js | 2 +- 7 files changed, 1502 insertions(+), 1438 deletions(-) diff --git a/dev/docs/jsdoc.conf.json b/dev/docs/jsdoc.conf.json index 7cd7249a..2d02dafe 100644 --- a/dev/docs/jsdoc.conf.json +++ b/dev/docs/jsdoc.conf.json @@ -21,5 +21,19 @@ "markdown" : { "parser" : "gfm", "hardwrap" : true + }, + "methodGroupDefinitions" : { + "ScrollMagic" : { + "Control Methods" : "Methods", + "Parameters (getter / setter)" : ["enabled", "loglevel", "scrollPos"], + "Properties (getter)" : ["info"] + }, + "ScrollScene" : { + "Scene Control Methods" : "Methods", + "Scene Parameters (getter / setter)" : ["offset", "duration", "enabled", "loglevel", "reverse", "triggerElement", "triggerHook", "tweenChanges"], + "Scene Properties (getter)" : ["parent", "scrollOffset", "state", "triggerOffset"], + "Event Handling" : ["off", "on", "trigger"], + "Debuging" : ["addIndicators"] + } } } diff --git a/dev/docs/template/tmpl/container.tmpl b/dev/docs/template/tmpl/container.tmpl index 5028e9dd..6e92f1d0 100644 --- a/dev/docs/template/tmpl/container.tmpl +++ b/dev/docs/template/tmpl/container.tmpl @@ -111,15 +111,41 @@ -1}); + } else if (groupMethods === methodsTitle) { // overwrite method title + methodsTitle = groupName; + methodsOutput[groupName] = []; + } + }); + } + + methodsOutput[methodsTitle] = _.filter(methods, function (method) {return methodsInGroups.indexOf(method.name) === -1}); + + _.each(methodsOutput, function (methods, title) { ?> -

    Methods

    +

    - + Example

    -

    Methods

    +

    Control Methods

    @@ -800,7 +800,7 @@
    Example
    -

    enabled(newState) → {boolean|ScrollMagic}

    +

    removeScene(ScrollScene) → {ScrollMagic}

    @@ -808,7 +808,7 @@

    enabled -

    Get or Set the current enabled state of the controller.
    This can be used to disable all Scenes connected to the controller without destroying or removing them.

    +

    Remove one ore more scene(s) from the controller.
    This is the equivalent to ScrollScene.remove().

    @@ -830,8 +830,6 @@
    Parameters:
    Type - Argument - @@ -844,33 +842,26 @@
    Parameters:
    - newState + ScrollScene -boolean +ScrollScene +| +array - - - - - <optional>
    - - - + - - -

    The new enabled state of the controller true or false.

    +

    ScrollScene or Array of ScrollScenes to be removed from the controller.

    @@ -902,7 +893,7 @@
    Parameters:
    Source:
    @@ -927,12 +918,9 @@
    Returns:
    -

    Current enabled state or parent object for chaining.

    +

    Parent object for chaining.

    { -boolean -| - ScrollMagic } @@ -945,7 +933,7 @@
    Returns:
    Example
    -
    // get the current value
    var enabled = controller.enabled();
    
    // disable the controller
    controller.enabled(false);
    +
    // remove a scene from the controller
    controller.removeScene(scene);
    
    // remove multiple scenes from the controller
    controller.removeScene([scene, scene2, scene3]);
    @@ -954,7 +942,7 @@
    Example
    -

    info(about) → {mixed|object}

    +

    scrollTo(newScrollPos) → {ScrollMagic}

    @@ -962,7 +950,12 @@

    info -

    Get all infos or one in particular about the controller.

    +

    Scroll to a new scroll offset, the start of a scene or provide an alternate method for scrolling.
    For vertical controllers it will change the top scroll offset and for horizontal applications it will change the left offset.

    +
      +
    1. If a number is supplied the container will scroll to the new scroll offset.
    2. +
    3. If a ScrollScene is supplied the container will scroll to the start of this scene.
    4. +
    5. If a function is supplied this function will be used as a callback for future scroll position modifications.
      This provides a way for you to change the behaviour of scrolling and adding new behaviour like animation.
      The callback receives the new scroll position as a parameter and a reference to the container element using this.
    6. +

    @@ -998,13 +991,19 @@
    Parameters:
    - about + newScrollPos -string +number +| + +ScrollScene +| + +function @@ -1024,15 +1023,7 @@
    Parameters:
    -

    If passed only this info will be returned instead of an object containing all.
    Valid options are:

    -
      -
    • "size" => the current viewport size of the container
    • -
    • "vertical" => true if vertical scrolling, otherwise false
    • -
    • "scrollPos" => the current scroll position
    • -
    • "scrollDirection" => the last known direction of the scroll
    • -
    • "container" => the container element
    • -
    • "isDocument" => true if container element is the document.
    • -
    +

    A new scroll position or a scene to scroll to. Alternative: A function to be used for future scroll position modification.

    @@ -1064,7 +1055,7 @@
    Parameters:
    Source:
    @@ -1089,13 +1080,10 @@
    Returns:
    -

    The requested info(s).

    +

    Parent object for chaining.

    { -mixed -| - -object +ScrollMagic } @@ -1107,7 +1095,7 @@
    Returns:
    Example
    -
    // returns the current scroll position (number)
    var scrollPos = controller.info("scrollPos");
    
    // returns all infos as an object
    var infos = controller.info();
    +
    // scroll to an offset of 100
    var scrollPos = controller.scrollTo(100);
    
    // scroll to the beginning of a scene
    var scene = new ScrollScene({offset: 200});
    var scrollPos = controller.scrollTo(scene);
    
    // define a new scroll position modification function (animate instead of jump)
    controller.scrollTo(function (newScrollPos) {
    	$("body").animate({scrollTop: newScrollPos});
    });
    @@ -1116,7 +1104,7 @@
    Example
    -

    loglevel(newLoglevel) → {number|ScrollMagic}

    +

    update(immediately) → {ScrollMagic}

    @@ -1124,7 +1112,8 @@

    loglevel -

    Get or Set the current loglevel option value.

    +

    Updates the controller params and calls updateScene on every scene, that is attached to the controller.
    See ScrollMagic.updateScene() for more information about what this means.
    In most cases you will not need this function, as it is called constantly, whenever ScrollMagic detects a state change event, like resize or scroll.
    The only application for this method is when ScrollMagic fails to detect these events.
    One application is with some external scroll libraries (like iScroll) that move an internal container to a negative offset instead of actually scrolling. In this case the update on the controller needs to be called whenever the child container's position changes. +For this case there will also be the need to provide a custom function to calculate the correct scroll position. See ScrollMagic.scrollPos() for details.

    @@ -1150,6 +1139,8 @@
    Parameters:
    + Default + Description @@ -1160,13 +1151,13 @@
    Parameters:
    - newLoglevel + immediately -number +boolean @@ -1185,8 +1176,14 @@
    Parameters:
    + + + false + + + -

    The new loglevel setting of the ScrollMagic controller. [0-3]

    +

    If true the update will be instant, if false it will wait until next tweenmax tick (better performance)

    @@ -1218,7 +1215,7 @@
    Parameters:
    Source:
    @@ -1243,12 +1240,9 @@
    Returns:
    -

    Current loglevel or parent object for chaining.

    +

    Parent object for chaining.

    { -number -| - ScrollMagic } @@ -1261,7 +1255,7 @@
    Returns:
    Example
    -
    // get the current value
    var loglevel = controller.loglevel();
    
    // set a new value
    controller.loglevel(3);
    +
    // update the controller on next tick (saves performance)
    controller.update();
    
    // update the controller immediately
    controller.update(true);
    @@ -1270,7 +1264,7 @@
    Example
    -

    removeScene(ScrollScene) → {ScrollMagic}

    +

    updateScene(ScrollScene, immediately) → {ScrollMagic}

    @@ -1278,7 +1272,7 @@

    removeScen
    -

    Remove one ore more scene(s) from the controller.
    This is the equivalent to ScrollScene.remove().

    +

    Update one ore more scene(s) according to the scroll position of the container.
    This is the equivalent to ScrollScene.update().
    The update method calculates the scene's start and end position (based on the trigger element, trigger hook, duration and offset) and checks it against the current scroll position of the container.
    It then updates the current scene state accordingly (or does nothing, if the state is already correct) – Pins will be set to their correct position and tweens will be updated to their correct progress.
    Note: This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters.

    @@ -1300,8 +1294,12 @@

    Parameters:
    Type + Argument + + Default + Description @@ -1319,19 +1317,67 @@
    Parameters:
    ScrollScene -| -array + + + + + + + + + + + + + + + + + + + + +

    ScrollScene or Array of ScrollScenes that is/are supposed to be updated.

    + + + + + + + immediately + + + + + +boolean + + + <optional>
    + + + + + -

    ScrollScene or Array of ScrollScenes to be removed from the controller.

    + + + + false + + + + +

    If true the update will be instant, if false it will wait until next tweenmax tick.
    This is useful when changing multiple properties of the scene - this way it will only be updated once all new properties are set (onTick).

    @@ -1363,7 +1409,7 @@
    Parameters:
    Source:
    @@ -1403,16 +1449,20 @@
    Returns:
    Example
    -
    // remove a scene from the controller
    controller.removeScene(scene);
    
    // remove multiple scenes from the controller
    controller.removeScene([scene, scene2, scene3]);
    +
    // update a specific scene on next tick
    controller.updateScene(scene);
    
    // update a specific scene immediately
    controller.updateScene(scene, true);
    
    // update multiple scenes scene on next tick
    controller.updateScene([scene1, scene2, scene3]);
    - +

    + +

    Parameters (getter / setter)

    + +
    -

    scrollPos(scrollPosMethod) → {number|ScrollMagic}

    +

    enabled(newState) → {boolean|ScrollMagic}

    @@ -1420,11 +1470,7 @@

    scrollPos -

    Get the current scrollPosition or Set a new method to calculate it.
    -> GET: -When used as a getter this function will return the current scroll position.
    To get a cached value use ScrollMagic.info("scrollPos"), which will be updated on tick to save on performance.
    For vertical controllers it will return the top scroll offset and for horizontal applications it will return the left offset.

    -

    -> SET: -When used as a setter this method prodes a way to permanently overwrite the controller's scroll position calculation.
    A typical usecase is when the scroll position is not reflected by the containers scrollTop or scrollLeft values, but for example by the inner offset of a child container.
    Moving a child container inside a parent is a commonly used method for several scrolling frameworks, including iScroll.
    By providing an alternate calculation function you can make sure ScrollMagic receives the correct scroll position.
    Please also bear in mind that your function should return y values for vertical scrolls an x for horizontals.

    -

    To change the current scroll position please use ScrollMagic.scrollTo().

    +

    Get or Set the current enabled state of the controller.
    This can be used to disable all Scenes connected to the controller without destroying or removing them.

    @@ -1460,13 +1506,13 @@
    Parameters:
    - scrollPosMethod + newState -function +boolean @@ -1486,7 +1532,7 @@
    Parameters:
    -

    The function to be used for the scroll position calculation of the container.

    +

    The new enabled state of the controller true or false.

    @@ -1518,7 +1564,7 @@
    Parameters:
    Source:
    @@ -1543,10 +1589,10 @@
    Returns:
    -

    Current scroll position or parent object for chaining.

    +

    Current enabled state or parent object for chaining.

    { -number +boolean | ScrollMagic @@ -1561,7 +1607,7 @@
    Returns:
    Example
    -
    // get the current scroll Position
    var scrollPos = controller.scrollPos();
    
    // set a new scroll position calculation method
    controller.scrollPos(function () {
    	return this.info("vertical") ? -$mychildcontainer.y : -$mychildcontainer.x
    });
    +
    // get the current value
    var enabled = controller.enabled();
    
    // disable the controller
    controller.enabled(false);
    @@ -1570,7 +1616,7 @@
    Example
    -

    scrollTo(newScrollPos) → {ScrollMagic}

    +

    loglevel(newLoglevel) → {number|ScrollMagic}

    @@ -1578,12 +1624,7 @@

    scrollTo -

    Scroll to a new scroll offset, the start of a scene or provide an alternate method for scrolling.
    For vertical controllers it will change the top scroll offset and for horizontal applications it will change the left offset.

    -
      -
    1. If a number is supplied the container will scroll to the new scroll offset.
    2. -
    3. If a ScrollScene is supplied the container will scroll to the start of this scene.
    4. -
    5. If a function is supplied this function will be used as a callback for future scroll position modifications.
      This provides a way for you to change the behaviour of scrolling and adding new behaviour like animation.
      The callback receives the new scroll position as a parameter and a reference to the container element using this.
    6. -
    +

    Get or Set the current loglevel option value.

    @@ -1619,19 +1660,13 @@
    Parameters:
    - newScrollPos + newLoglevel number -| - -ScrollScene -| - -function @@ -1651,7 +1686,7 @@
    Parameters:
    -

    A new scroll position or a scene to scroll to. Alternative: A function to be used for future scroll position modification.

    +

    The new loglevel setting of the ScrollMagic controller. [0-3]

    @@ -1683,7 +1718,7 @@
    Parameters:
    Source:
    @@ -1708,9 +1743,12 @@
    Returns:
    -

    Parent object for chaining.

    +

    Current loglevel or parent object for chaining.

    { +number +| + ScrollMagic } @@ -1723,7 +1761,7 @@
    Returns:
    Example
    -
    // scroll to an offset of 100
    var scrollPos = controller.scrollTo(100);
    
    // scroll to the beginning of a scene
    var scene = new ScrollScene({offset: 200});
    var scrollPos = controller.scrollTo(scene);
    
    // define a new scroll position modification function (animate instead of jump)
    controller.scrollTo(function (newScrollPos) {
    	$("body").animate({scrollTop: newScrollPos});
    });
    +
    // get the current value
    var loglevel = controller.loglevel();
    
    // set a new value
    controller.loglevel(3);
    @@ -1732,7 +1770,7 @@
    Example
    -

    update(immediately) → {ScrollMagic}

    +

    scrollPos(scrollPosMethod) → {number|ScrollMagic}

    @@ -1740,8 +1778,11 @@

    update -

    Updates the controller params and calls updateScene on every scene, that is attached to the controller.
    See ScrollMagic.updateScene() for more information about what this means.
    In most cases you will not need this function, as it is called constantly, whenever ScrollMagic detects a state change event, like resize or scroll.
    The only application for this method is when ScrollMagic fails to detect these events.
    One application is with some external scroll libraries (like iScroll) that move an internal container to a negative offset instead of actually scrolling. In this case the update on the controller needs to be called whenever the child container's position changes. -For this case there will also be the need to provide a custom function to calculate the correct scroll position. See ScrollMagic.scrollPos() for details.

    +

    Get the current scrollPosition or Set a new method to calculate it.
    -> GET: +When used as a getter this function will return the current scroll position.
    To get a cached value use ScrollMagic.info("scrollPos"), which will be updated on tick to save on performance.
    For vertical controllers it will return the top scroll offset and for horizontal applications it will return the left offset.

    +

    -> SET: +When used as a setter this method prodes a way to permanently overwrite the controller's scroll position calculation.
    A typical usecase is when the scroll position is not reflected by the containers scrollTop or scrollLeft values, but for example by the inner offset of a child container.
    Moving a child container inside a parent is a commonly used method for several scrolling frameworks, including iScroll.
    By providing an alternate calculation function you can make sure ScrollMagic receives the correct scroll position.
    Please also bear in mind that your function should return y values for vertical scrolls an x for horizontals.

    +

    To change the current scroll position please use ScrollMagic.scrollTo().

    @@ -1767,8 +1808,6 @@
    Parameters:
    - Default - Description @@ -1779,13 +1818,13 @@
    Parameters:
    - immediately + scrollPosMethod -boolean +function @@ -1804,14 +1843,8 @@
    Parameters:
    - - - false - - - -

    If true the update will be instant, if false it will wait until next tweenmax tick (better performance)

    +

    The function to be used for the scroll position calculation of the container.

    @@ -1843,7 +1876,7 @@
    Parameters:
    Source:
    @@ -1868,9 +1901,12 @@
    Returns:
    -

    Parent object for chaining.

    +

    Current scroll position or parent object for chaining.

    { +number +| + ScrollMagic } @@ -1883,16 +1919,20 @@
    Returns:
    Example
    -
    // update the controller on next tick (saves performance)
    controller.update();
    
    // update the controller immediately
    controller.update(true);
    +
    // get the current scroll Position
    var scrollPos = controller.scrollPos();
    
    // set a new scroll position calculation method
    controller.scrollPos(function () {
    	return this.info("vertical") ? -$mychildcontainer.y : -$mychildcontainer.x
    });
    - +

    + +

    Properties (getter)

    + +
    -

    updateScene(ScrollScene, immediately) → {ScrollMagic}

    +

    info(about) → {mixed|object}

    @@ -1900,7 +1940,7 @@

    updateScen
    -

    Update one ore more scene(s) according to the scroll position of the container.
    This is the equivalent to ScrollScene.update().
    The update method calculates the scene's start and end position (based on the trigger element, trigger hook, duration and offset) and checks it against the current scroll position of the container.
    It then updates the current scene state accordingly (or does nothing, if the state is already correct) – Pins will be set to their correct position and tweens will be updated to their correct progress.
    Note: This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters.

    +

    Get all infos or one in particular about the controller.

    @@ -1926,8 +1966,6 @@

    Parameters:
    - Default - Description @@ -1938,48 +1976,13 @@
    Parameters:
    - ScrollScene - - - - - -ScrollScene - - - - - - - - - - - - - - - - - - - - - -

    ScrollScene or Array of ScrollScenes that is/are supposed to be updated.

    - - - - - - - immediately + about -boolean +string @@ -1998,14 +2001,16 @@
    Parameters:
    - - - false - - - -

    If true the update will be instant, if false it will wait until next tweenmax tick.
    This is useful when changing multiple properties of the scene - this way it will only be updated once all new properties are set (onTick).

    +

    If passed only this info will be returned instead of an object containing all.
    Valid options are:

    +
      +
    • "size" => the current viewport size of the container
    • +
    • "vertical" => true if vertical scrolling, otherwise false
    • +
    • "scrollPos" => the current scroll position
    • +
    • "scrollDirection" => the last known direction of the scroll
    • +
    • "container" => the container element
    • +
    • "isDocument" => true if container element is the document.
    • +
    @@ -2037,7 +2042,7 @@
    Parameters:
    Source:
    @@ -2062,10 +2067,13 @@
    Returns:
    -

    Parent object for chaining.

    +

    The requested info(s).

    { -ScrollMagic +mixed +| + +object } @@ -2077,7 +2085,7 @@
    Returns:
    Example
    -
    // update a specific scene on next tick
    controller.updateScene(scene);
    
    // update a specific scene immediately
    controller.updateScene(scene, true);
    
    // update multiple scenes scene on next tick
    controller.updateScene([scene1, scene2, scene3]);
    +
    // returns the current scroll position (number)
    var scrollPos = controller.info("scrollPos");
    
    // returns all infos as an object
    var infos = controller.info();
    diff --git a/docs/ScrollScene.html b/docs/ScrollScene.html index 67f039af..ddc43d86 100644 --- a/docs/ScrollScene.html +++ b/docs/ScrollScene.html @@ -580,12 +580,12 @@
    Example
    -

    Methods

    +

    Scene Control Methods

    -

    addIndicators(options)

    +

    addTo(controller) → {ScrollScene}

    @@ -593,7 +593,7 @@

    addIndic
    -

    Add Indicators for a ScrollScene.
    REQUIRES ScrollMagic Debug Extension: jquery.scrollmagic.debug.js
    The indicators can only be added AFTER the scene has been added to a controller.

    +

    Add the scene to a controller.
    This is the equivalent to ScrollMagic.addScene(scene).

    @@ -615,8 +615,6 @@

    Parameters:
    Type - Argument - @@ -629,35 +627,122 @@
    Parameters:
    - options + controller -object +ScrollMagic - - - <optional>
    - - + - - +

    The controller to which the scene should be added.

    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + +
    +

    Parent object for chaining.

    + + { +ScrollScene + +} +
    + + + + + +
    Example
    + +
    // add a scene to a ScrollMagic controller
    scene.addTo(controller);
    + + + + + + +
    +

    destroy(reset) → {null}

    + + +
    +
    + + +
    +

    Destroy the scene and everything.

    +
    + -

    An object containing one or more options for the indicators.

    -
    Properties
    - + + + + + +
    Parameters:
    + @@ -685,16 +770,13 @@
    Properties
    - + - + + +
    parentreset -string -| - -object +boolean @@ -715,19 +797,137 @@
    Properties
    - undefined + false

    A selector, DOM Object or a jQuery object that the indicators should be added to.
    If undefined, the scene's container will be used.

    If true the pin and tween (if existent) will be reset.

    + + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + +
    +

    Null to unset handler variables.

    + + { +null + +} + +
    + + + + + +
    Example
    + +
    // destroy the scene without resetting the pin and tween to their initial positions
    scene = scene.destroy();
    
    // destroy the scene and reset the pin and tween
    scene = scene.destroy(true);
    + + + +
    + + + +
    +

    progress(progress) → {number}

    + + +
    +
    + + +
    +

    Get or Set the scene's progress.
    Usually it shouldn't be necessary to use this as a setter, as it is set automatically by scene.update().

    +
    + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + - + - - + + +
    NameTypeArgumentDescription
    zindexprogress @@ -752,225 +952,135 @@
    Properties
    -
    - - -1 - -

    CSS zindex for the indicator container.

    The new progress value of the scene [0-1].

    - - - indent - - - - - -number - - - - - - - - - <optional>
    - - - - - - - - - - - - 0 - - - - -

    Additional position offset for the indicators (useful, when having multiple scenes starting at the same time).

    - - - - - - - suffix - - - - - -number - - - - - - - - - <optional>
    - - - - - - - - - - - - "" - - - - -

    This string will be attached to the start and end indicator (useful for identification when working with multiple scenes).

    - - - - - - - colorTrigger - - - - - -string + + + +
    + + - - + - - - - <optional>
    - + - + - - - + - - - - blue - - - + -

    CSS color definition for the trigger indicator.

    - + - + - - - colorStart - + +
    Source:
    +
    + - - - -string + + - - + +
    - - - - <optional>
    - + + +
    Fires:
    + + - + - - - + + + + + +
    Returns:
    +
      +
    • +
      +

      get - Current scene progress.

      - - - - green - - - + { +number -

      CSS color definition for the start indicator.

      - +} - +
      - - - colorEnd - +
    • + +
    • +
      +

      set - Parent object for chaining.

      - - - -string + { +ScrollScene +} - - +
      - - - - <optional>
      - +
    • +
    + + +
    Example
    + +
    // get the current scene progress
    var progress = scene.progress();
    
    // set new scene progress
    scene.progress(0.3);
    - - - - + +
    + - - - red - - - - -

    CSS color definition for the end indicator.

    - - - - - - - - - - - - +
    +

    remove() → {ScrollScene}

    + + +
    +
    + + +
    +

    Remove the scene from its parent controller.
    This is the equivalent to ScrollMagic.removeScene(scene). +The scene will not be updated anymore until you readd it to a controller. +To remove the pin or the tween you need to call removeTween() or removePin() respectively.

    +
    + + + + +
    @@ -993,7 +1103,7 @@
    Properties
    Source:
    @@ -1014,11 +1124,26 @@
    Properties
    +
    Returns:
    + + +
    +

    Parent object for chaining.

    + + { +ScrollScene + +} + +
    + + +
    Example
    -
    // add basic indicators
    scene.addIndicators()
    
    // passing options
    scene.addIndicators({zindex: 100, colorEnd: "#FFFFFF"});
    +
    // remove the scene from its parent controller
    scene.remove();
    @@ -1027,7 +1152,7 @@
    Example
    -

    addTo(controller) → {ScrollScene}

    +

    removePin(reset) → {ScrollScene}

    @@ -1035,7 +1160,7 @@

    addTo -

    Add the scene to a controller.
    This is the equivalent to ScrollMagic.addScene(scene).

    +

    Remove the pin from the scene.

    @@ -1057,8 +1182,12 @@
    Parameters:
    Type + Argument + + Default + Description @@ -1069,23 +1198,39 @@
    Parameters:
    - controller + reset -ScrollMagic +boolean + + + <optional>
    + + + + + -

    The controller to which the scene should be added.

    + + + + false + + + + +

    If false the spacer will not be removed and the element's position will not be reset.

    @@ -1117,7 +1262,7 @@
    Parameters:
    Source:
    @@ -1157,7 +1302,7 @@
    Returns:
    Example
    -
    // add a scene to a ScrollMagic controller
    scene.addTo(controller);
    +
    // remove the pin from the scene without resetting it (the spacer is not removed)
    scene.removePin();
    
    // remove the pin from the scene and reset the pin element to its initial position (spacer is removed)
    scene.removePin(true);
    @@ -1166,7 +1311,7 @@
    Example
    -

    destroy(reset) → {null}

    +

    removeTween(reset) → {ScrollScene}

    @@ -1174,7 +1319,7 @@

    destroy -

    Destroy the scene and everything.

    +

    Remove the tween from the scene.

    @@ -1244,7 +1389,7 @@
    Parameters:
    -

    If true the pin and tween (if existent) will be reset.

    +

    If true the tween will be reset to its initial values.

    @@ -1276,7 +1421,7 @@
    Parameters:
    Source:
    @@ -1301,10 +1446,10 @@
    Returns:
    -

    Null to unset handler variables.

    +

    Parent object for chaining.

    { -null +ScrollScene } @@ -1316,7 +1461,7 @@
    Returns:
    Example
    -
    // destroy the scene without resetting the pin and tween to their initial positions
    scene = scene.destroy();
    
    // destroy the scene and reset the pin and tween
    scene = scene.destroy(true);
    +
    // remove the tween from the scene without resetting it
    scene.removeTween();
    
    // remove the tween from the scene and reset it to initial position
    scene.removeTween(true);
    @@ -1325,7 +1470,7 @@
    Example
    -

    duration(newDuration) → {number}

    +

    setPin(element, settings) → {ScrollScene}

    @@ -1333,7 +1478,7 @@

    duration -

    Get or Set the duration option value.

    +

    Pin an element for the duration of the tween.
    If the scene duration is 0 the element will never be unpinned.
    Note, that pushFollowers has no effect, when the scene duration is 0.

    @@ -1352,30 +1497,198 @@
    Parameters:
    Name - Type + Type + + + Argument + + + + + Description + + + + + + + + + element + + + + + +string +| + +object + + + + + + + + + + + + + + + + + +

    A Selctor, a DOM Object or a jQuery object for the object that is supposed to be pinned.

    + + + + + + + settings + + + + + +object + + + + + + + + + <optional>
    + + + + + + + + + + +

    settings for the pin

    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + - - - + + + + + + - - + + - + + + + + +
    NameTypeArgumentDefaultDescription
    pushFollowers + + +boolean + + + + + + <optional>
    + + + + + +
    + + true + +

    If true following elements will be "pushed" down for the duration of the pin, if false the pinned element will just scroll past them.
    Ignored, when duration is 0.

    spacerClass + + +string + + + + + + <optional>
    + - -
    ArgumentDescription
    + + "scrollmagic-pin-spacer" + +

    Classname of the pin spacer element, which is used to replace the element.

    newDurationpinnedClass -number +string @@ -1394,8 +1707,21 @@
    Parameters:
    +
    + + "" + +

    The new duration of the scene.

    Classname that should be added to the pinned element during pin phase (and removed after).

    + + @@ -1427,7 +1753,7 @@
    Parameters:
    Source:
    @@ -1440,11 +1766,6 @@
    Parameters:
    -
    Fires:
    - - @@ -1454,23 +1775,10 @@
    Fires:
    Returns:
    -
      -
    • -
      -

      get - Current scene duration.

      - - { -number - -} - -
      - -
    • - -
    • + +
      -

      set - Parent object for chaining.

      +

      Parent object for chaining.

      { ScrollScene @@ -1479,13 +1787,13 @@
      Returns:
      -
    • -
    + +
    Example
    -
    // get the current duration value
    var duration = scene.duration();
    
    // set a new duration
    scene.duration(300);
    +
    // pin element and push all following elements down by the amount of the pin duration.
    scene.setPin("#pin");
    
    // pin element and keeping all following elements in their place. The pinned element will move past them.
    scene.setPin("#pin", {pushFollowers: false});
    @@ -1494,7 +1802,7 @@
    Example
    -

    enabled(newState) → {boolean|ScrollScene}

    +

    setTween(TweenMaxObject) → {ScrollScene}

    @@ -1502,7 +1810,7 @@

    enabled -

    Get or Set the current enabled state of the scene.
    This can be used to disable this scene without removing or destroying it.

    +

    Add a tween to the scene.
    If you want to add multiple tweens, wrap them into one TimelineMax object and add it.
    The duration of the tween is streched to the scroll duration of the scene, unless the scene has a duration of 0.

    @@ -1524,8 +1832,6 @@
    Parameters:
    Type - Argument - @@ -1538,33 +1844,23 @@
    Parameters:
    - newState + TweenMaxObject -boolean +object - - - <optional>
    - - - - - - - -

    The new enabled state of the scene true or false.

    +

    A TweenMax, TweenLite, TimelineMax or TimelineLite object that should be animated in the scene.

    @@ -1596,7 +1892,7 @@
    Parameters:
    Source:
    @@ -1621,12 +1917,9 @@
    Returns:
    -

    Current enabled state or parent object for chaining.

    +

    Parent object for chaining.

    { -boolean -| - ScrollScene } @@ -1639,7 +1932,7 @@
    Returns:
    Example
    -
    // get the current value
    var enabled = scene.enabled();
    
    // disable the scene
    scene.enabled(false);
    +
    // add a single tween
    scene.setTween(TweenMax.to("obj"), 1, {x: 100});
    
    // add multiple tweens, wrapped in a timeline.
    var timeline = new TimelineMax();
    var tween1 = TweenMax.from("obj1", 1, {x: 100});
    var tween2 = TweenMax.to("obj2", 1, {y: 100});
    timeline
    		.add(tween1)
    		.add(tween2);
    scene.addTween(timeline);
    @@ -1648,7 +1941,7 @@
    Example
    -

    loglevel(newLoglevel) → {number}

    +

    update(immediately) → {ScrollScene}

    @@ -1656,7 +1949,7 @@

    loglevel -

    Get or Set the loglevel option value.

    +

    Updates the Scene in the parent Controller to reflect the current state.
    This is the equivalent to ScrollMagic.updateScene(scene, immediately).
    The update method calculates the scene's start and end position (based on the trigger element, trigger hook, duration and offset) and checks it against the current scroll position of the container.
    It then updates the current scene state accordingly (or does nothing, if the state is already correct) – Pins will be set to their correct position and tweens will be updated to their correct progress.
    Note: This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters.

    @@ -1682,6 +1975,8 @@
    Parameters:
    + Default + Description @@ -1692,13 +1987,13 @@
    Parameters:
    - newLoglevel + immediately -number +boolean @@ -1717,8 +2012,14 @@
    Parameters:
    + + + false + + + -

    The new loglevel setting of the scene. [0-3]

    +

    If true the update will be instant, if false it will wait until next tweenmax tick (better performance).

    @@ -1750,190 +2051,95 @@
    Parameters:
    Source:
    - - - - - -

    - - - -
    Fires:
    - - - - - - - - - - -
    Returns:
    -
      -
    • -
      -

      get - Current loglevel.

      - - { -number - -} - -
      - -
    • - -
    • -
      -

      set - Parent object for chaining.

      - - { -ScrollScene - -} - -
      - -
    • -
    - - -
    Example
    - -
    // get the current loglevel
    var loglevel = scene.loglevel();
    
    // set new loglevel
    scene.loglevel(3);
    - - - - - - - -
    -

    off(name, callback) → {ScrollScene}

    - - -
    -
    - - -
    -

    Remove one or more event listener.

    -
    - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + - + + +
    Fires:
    + + - - - - + - +} - - - + + + +
    Example
    + +
    // update the scene on next tick
    scene.update();
    
    // update the scene immediately
    scene.update(true);
    - - - + + - - -
    NameTypeArgumentDescription
    name - - -string - - - - - - - - - -

    The name or names of the event that should be removed.

    callback + + + + + +
    Returns:
    + - -function +
    +

    Parent object for chaining.

    + { +ScrollScene - -
    - - <optional>
    - + - - -

    A specific callback function that should be removed. If none is passed all callbacks to the event listener will be removed.

    + + +
    +

    updateTriggerElementPosition() → {ScrollScene}

    + + +
    +
    + + +
    +

    Updates the position of the triggerElement, if present. +This method is automatically called ...

    +
      +
    • ... when the triggerElement is changed
    • +
    • ... when the scene is added to a (new) controller
    • +
    • ... in regular intervals from the controller. See ScrollMagic option triggerPosUpdateInterval.
    • +
    +

    You can call it to minimize lag, when you intentionally change the position of the triggerElement.

    +
    + + + + +
    +
    Since:
    +
    • v1.1.0
    + @@ -1950,7 +2156,7 @@
    Parameters:
    Source:
    @@ -1990,16 +2196,20 @@
    Returns:
    Example
    -
    function callback (event) {
    		console.log("Event fired! (" + event.type + ")");
    }
    // add listeners
    scene.on("change update", callback);
    // remove listeners
    scene.off("change update", callback);
    +
    scene = new ScrollScene({triggerElement: "#trigger"});
    
    // change the position of the trigger
    $("#trigger").css("top", 500);
    // immediately let the scene know of this change
    scene.updateTriggerElementPosition();
    - + + +

    Scene Parameters (getter / setter)

    + +
    -

    offset(newOffset) → {number}

    +

    duration(newDuration) → {number}

    @@ -2007,7 +2217,7 @@

    offset -

    Get or Set the offset option value.

    +

    Get or Set the duration option value.

    @@ -2043,7 +2253,7 @@
    Parameters:
    - newOffset + newDuration @@ -2069,7 +2279,7 @@
    Parameters:
    -

    The new offset of the scene.

    +

    The new duration of the scene.

    @@ -2101,7 +2311,7 @@
    Parameters:
    Source:
    @@ -2131,7 +2341,7 @@
    Returns:
    +

    ScrollMagic takes an object oriented approach using a controller for each scroll container and multiple "scroll scenes" to define what should happen at what point in time. While this offers a great deal of control it might be a little confusing, if you're just starting out with javascript.
    If the above points are not crucial for you and you are just looking for a simple solution to implement basic css animations I would strongly recommend taking a look at the awesome skrollr project. It almost solely relys on element attributes and thus requires minimal to no javascript knowledge.

    Installation

    Aside from jQuery make sure you have loaded the Greensock Animation Plattform (TweenMax).
    To use ScrollMagic in your project simply include the plugin js file in the head section of your HTML file:

    <script type="text/javascript" src="js/jquery.scrollmagic.js"></script>

    For deployment use the minified version instead:

    -
    <script type="text/javascript" src="js/jquery.scrollmagic.min.js"></script>

    And to have access to the debugging extension during development, include this file additionally:

    +
    <script type="text/javascript" src="js/jquery.scrollmagic.min.js"></script>

    NOTE: The logging feature is removed in the minified version for obvious file size considerations.

    +

    To have access to the debugging extension during development, include this file additionally:

    <script type="text/javascript" src="js/jquery.scrollmagic.debug.js"></script>

    You can remove the debugging extension for actual deployment.

    Usage

    // init controller
     var controller = new ScrollMagic();
    @@ -210,7 +220,7 @@ 

    Version:
    -
    • 1.1.0
    +
    • 1.1.0-alpha
    @@ -322,8 +332,8 @@

    diff --git a/docs/jquery.scrollmagic.debug.js.html b/docs/jquery.scrollmagic.debug.js.html index 3e7c85c7..af2cf6fd 100644 --- a/docs/jquery.scrollmagic.debug.js.html +++ b/docs/jquery.scrollmagic.debug.js.html @@ -53,6 +53,10 @@ change +
  • + destroy +
  • +
  • end
  • @@ -69,6 +73,10 @@ progress +
  • + shift +
  • +
  • start
  • @@ -99,7 +107,7 @@

    Source: jquery.scrollmagic.debug.js

    /*
    -ScrollMagic v1.1.0
    +ScrollMagic v1.1.0-alpha
     The jQuery plugin for doing magical scroll interactions.
     (c) 2014 Jan Paepke (@janpaepke)
     License & Info: http://janpaepke.github.io/ScrollMagic
    @@ -112,7 +120,7 @@ 

    Source: jquery.scrollmagic.debug.js

    */ /* @overview Debug Extension for ScrollMagic. - @version 1.1.0 + @version 1.1.0-alpha @license Dual licensed under MIT license and GPL. @author Jan Paepke - e-mail@janpaepke.de */ @@ -296,7 +304,7 @@

    Source: jquery.scrollmagic.debug.js

    if (!triggerOnly) { var - startPos = scene.triggerOffset(), + startPos = scene.triggerPosition(), endPos = startPos + scene.duration(), resetCSS = { "border": "none", @@ -394,8 +402,8 @@

    Source: jquery.scrollmagic.debug.js

    diff --git a/docs/jquery.scrollmagic.js.html b/docs/jquery.scrollmagic.js.html index 16e46d43..7c98491a 100644 --- a/docs/jquery.scrollmagic.js.html +++ b/docs/jquery.scrollmagic.js.html @@ -53,6 +53,10 @@ change +
  • + destroy +
  • +
  • end
  • @@ -69,6 +73,10 @@ progress +
  • + shift +
  • +
  • start
  • @@ -99,7 +107,7 @@

    Source: jquery.scrollmagic.js

    /*
    -ScrollMagic v1.1.0
    +ScrollMagic v1.1.0-alpha
     The jQuery plugin for doing magical scroll interactions.
     (c) 2014 Jan Paepke (@janpaepke)
     License & Info: http://janpaepke.github.io/ScrollMagic
    @@ -112,17 +120,13 @@ 

    Source: jquery.scrollmagic.js

    */ /** @overview ##Info -@version 1.1.0 +@version 1.1.0-alpha @license Dual licensed under MIT license and GPL. @author Jan Paepke - e-mail@janpaepke.de @todo: bug: when cascading pins (pinning one element multiple times) and later removing them without reset, positioning errors occur. @todo: bug: having multiple scroll directions with cascaded pins doesn't work (one scroll vertical, one horizontal) -@todo: bug: pin positioning problems with centered pins in IE9 (i.e. in examples) -@toto: improvement: check if its possible to take the recalculation of the start point out of the scene update, while still making sure it is always up to date (performance) -@todo: feature: consider public method to trigger pinspacerresize (in case size changes during pin) -@todo: feature: have different tweens, when scrolling up, than when scrolling down -@todo: feature: make pins work with -webkit-transform of parent for mobile applications. Might be possible by temporarily removing the pin element from its container and attaching it to the body during pin. Reverting might be difficult though (cascaded pins). +@todo: feature: optimize performance on debug plugin (huge drawbacks, when using many scenes) */ (function($, window) { @@ -142,15 +146,15 @@

    Source: jquery.scrollmagic.js

    * var controller = new ScrollMagic({container: "#myContainer", loglevel: 3}); * * @param {object} [options] - An object containing one or more options for the controller. - * @param {(string|object)} [options.container=window] - A selector, DOM Object or a jQuery object that references the main container for scrolling. + * @param {(string|object)} [options.container=window] - A selector, DOM object or a jQuery object that references the main container for scrolling. * @param {boolean} [options.vertical=true] - Sets the scroll mode to vertical (`true`) or horizontal (`false`) scrolling. * @param {object} [options.globalSceneOptions={}] - These options will be passed to every Scene that is added to the controller using the addScene method. For more information on Scene options see {@link ScrollScene}. - * @param {number} [options.loglevel=2] Loglevel for debugging: + * @param {number} [options.loglevel=2] Loglevel for debugging. Note that logging is disabled in the minified version of ScrollMagic. ** `0` => silent ** `1` => errors ** `2` => errors, warnings ** `3` => errors, warnings, debuginfo - * @param {boolean} [options.triggerPosUpdateInterval=100] - Interval in which the position of the trigger elements of the scenes are updated. If you don't use trigger elements or have static layouts, where the positions of the trigger elements don't change, you can set this to 0 disable interval checking and improve performance. + * @param {boolean} [options._sceneRefreshInterval=100] - Interval in which the position of the trigger elements of the scenes are updated. If you don't use trigger elements or have static layouts, where the positions of the trigger elements don't change, you can set this to 0 disable interval checking and improve performance. * */ var ScrollMagic = function(options) { @@ -167,7 +171,7 @@

    Source: jquery.scrollmagic.js

    vertical: true, globalSceneOptions: {}, loglevel: 2, - triggerPosUpdateInterval: 100 + sceneRefreshInterval: 100 }; /* @@ -187,7 +191,7 @@

    Source: jquery.scrollmagic.js

    _viewPortSize = 0, _tickerUsed = false, _enabled = true, - _triggerPosUpdateInterval; + _sceneRefreshInterval; /* * ---------------------------------------------------------------- @@ -225,13 +229,13 @@

    Source: jquery.scrollmagic.js

    _tickerUsed = false; } - _options.triggerPosUpdateInterval = parseInt(_options.triggerPosUpdateInterval); - if (_options.triggerPosUpdateInterval > 0) { - window.setInterval(function () { + _options.sceneRefreshInterval = parseInt(_options.sceneRefreshInterval); + if (_options.sceneRefreshInterval > 0) { + _sceneRefreshInterval = window.setInterval(function () { $.each(_sceneObjects, function (index, scene) { - scene.updateTriggerElementPosition(); + scene.refresh(); }); - }, _options.triggerPosUpdateInterval); + }, _options.sceneRefreshInterval); } log(3, "added new " + NAMESPACE + " controller"); @@ -263,14 +267,20 @@

    Source: jquery.scrollmagic.js

    var onTick = function (e) { if (_updateScenesOnNextTick && _enabled) { var - scenesToUpdate = $.isArray(_updateScenesOnNextTick) ? _updateScenesOnNextTick : _sceneObjects, + scenesToUpdate = $.isArray(_updateScenesOnNextTick) ? _updateScenesOnNextTick : _sceneObjects.slice(0), oldScrollPos = _scrollPos; // update scroll pos & direction _scrollPos = ScrollMagic.scrollPos(); var deltaScroll = _scrollPos - oldScrollPos; _scrollDirection = (deltaScroll === 0) ? "PAUSED" : (deltaScroll > 0) ? "FORWARD" : "REVERSE"; + if (deltaScroll < 0) { // reverse order if scrolling reverse + scenesToUpdate.reverse(); + } // update scenes - ScrollMagic.updateScene(scenesToUpdate, true); + $.each(scenesToUpdate, function (index, scene) { + log(3, "updating Scene " + (index + 1) + "/" + scenesToUpdate.length + " (" + _sceneObjects.length + " total)"); + scene.update(true); + }); if (scenesToUpdate.length === 0 && _options.loglevel >= 3) { log(3, "updating 0 Scenes (nothing added to controller)"); } @@ -307,6 +317,25 @@

    Source: jquery.scrollmagic.js

    } }; + /** + * Sort scenes in ascending order of their start offset. + * @private + * + * @param {array} ScrollScenesArray - an array of ScrollScenes that should be sorted + * @return {array} The sorted array of ScrollScenes. + */ + var sortScenes = function (ScrollScenesArray) { + if (ScrollScenesArray.length <= 1) { + return ScrollScenesArray; + } else { + var scenes = ScrollScenesArray.slice(0); + scenes.sort(function(a, b) { + return a.scrollOffset() > b.scrollOffset() ? 1 : -1; + }); + return scenes; + } + }; + /* * ---------------------------------------------------------------- * public functions @@ -330,25 +359,31 @@

    Source: jquery.scrollmagic.js

    * @param {(ScrollScene|array)} ScrollScene - ScrollScene or Array of ScrollScenes to be added to the controller. * @return {ScrollMagic} Parent object for chaining. */ - this.addScene = function (ScrollScene) { - if ($.isArray(ScrollScene)) { - $.each(ScrollScene, function (index, scene) { + this.addScene = function (newScene) { + if ($.isArray(newScene)) { + $.each(newScene, function (index, scene) { ScrollMagic.addScene(scene); }); - } else { - if (ScrollScene.parent() != ScrollMagic) { - ScrollScene.addTo(ScrollMagic); - } else if ($.inArray(_sceneObjects, ScrollScene) == -1){ + } else if (newScene instanceof ScrollScene) { + if (newScene.parent() != ScrollMagic) { + newScene.addTo(ScrollMagic); + } else if ($.inArray(_sceneObjects, newScene) == -1){ // new scene - _sceneObjects.push(ScrollScene); + _sceneObjects.push(newScene); // add to array + _sceneObjects = sortScenes(_sceneObjects); // sort + newScene.on("shift." + NAMESPACE + "_sort", function() { // resort whenever scene moves + _sceneObjects = sortScenes(_sceneObjects); + }); // insert Global defaults. $.each(_options.globalSceneOptions, function (key, value) { - if (ScrollScene[key]) { - ScrollScene[key].call(ScrollScene, value); + if (newScene[key]) { + newScene[key].call(newScene, value); } }); log(3, "added Scene (" + _sceneObjects.length + " total)"); } + } else { + log(1, "ERROR: invalid argument supplied for '.addScene()'"); } return ScrollMagic; }; @@ -375,6 +410,7 @@

    Source: jquery.scrollmagic.js

    } else { var index = $.inArray(ScrollScene, _sceneObjects); if (index > -1) { + ScrollScene.off("shift." + NAMESPACE + "_sort"); _sceneObjects.splice(index, 1); ScrollScene.remove(); log(3, "removed Scene (" + _sceneObjects.length + " total)"); @@ -388,7 +424,7 @@

    Source: jquery.scrollmagic.js

    * This is the equivalent to `ScrollScene.update()`. * The update method calculates the scene's start and end position (based on the trigger element, trigger hook, duration and offset) and checks it against the current scroll position of the container. * It then updates the current scene state accordingly (or does nothing, if the state is already correct) – Pins will be set to their correct position and tweens will be updated to their correct progress. - * *Note:* This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters. + * _**Note:** This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters._ * @public * @example * // update a specific scene on next tick @@ -408,19 +444,20 @@

    Source: jquery.scrollmagic.js

    this.updateScene = function (ScrollScene, immediately) { if ($.isArray(ScrollScene)) { $.each(ScrollScene, function (index, scene) { - log(3, "updating Scene " + (index + 1) + "/" + ScrollScene.length + " (" + _sceneObjects.length + " total)"); ScrollMagic.updateScene(scene, immediately); }); } else { if (immediately) { ScrollScene.update(true); } else { + // prep array for next update cycle if (!$.isArray(_updateScenesOnNextTick)) { _updateScenesOnNextTick = []; } if ($.inArray(ScrollScene, _updateScenesOnNextTick) == -1) { _updateScenesOnNextTick.push(ScrollScene); } + _updateScenesOnNextTick = sortScenes(_updateScenesOnNextTick); // sort } } return ScrollMagic; @@ -453,42 +490,57 @@

    Source: jquery.scrollmagic.js

    }; /** - * Scroll to a new scroll offset, the start of a scene or provide an alternate method for scrolling. + * Scroll to a numeric scroll offset, a DOM element, the start of a scene or provide an alternate method for scrolling. * For vertical controllers it will change the top scroll offset and for horizontal applications it will change the left offset. - * 1. If a `number` is supplied the container will scroll to the new scroll offset. - * 2. If a `ScrollScene` is supplied the container will scroll to the start of this scene. - * 3. If a `function` is supplied this function will be used as a callback for future scroll position modifications. - * This provides a way for you to change the behaviour of scrolling and adding new behaviour like animation. - * The callback receives the new scroll position as a parameter and a reference to the container element using `this`. * @public * + * @since 1.1.0 * @example * // scroll to an offset of 100 - * var scrollPos = controller.scrollTo(100); + * controller.scrollTo(100); + * + * // scroll to a DOM element + * controller.scrollTo("#anchor"); * * // scroll to the beginning of a scene * var scene = new ScrollScene({offset: 200}); - * var scrollPos = controller.scrollTo(scene); + * controller.scrollTo(scene); * * // define a new scroll position modification function (animate instead of jump) * controller.scrollTo(function (newScrollPos) { * $("body").animate({scrollTop: newScrollPos}); * }); * - * @param {(number|ScrollScene|function)} [newScrollPos] - A new scroll position or a scene to scroll to. Alternative: A function to be used for future scroll position modification. + * @param {mixed} [scrollTarget] - The supplied argument can be one of these types: + * 1. `number` -> The container will scroll to this new scroll offset. + * 2. `string` or `object` -> Can be a selector, a DOM object or a jQuery element. + * The container will scroll to the position of this element. + * 3. `ScrollScene` -> The container will scroll to the start of this scene. + * 4. `function` -> This function will be used as a callback for future scroll position modifications. + * This provides a way for you to change the behaviour of scrolling and adding new behaviour like animation. The callback receives the new scroll position as a parameter and a reference to the container element using `this`. + * _**NOTE:** All other options will still work as expected, using the new function to scroll._ * @returns {ScrollMagic} Parent object for chaining. */ - this.scrollTo = function (newScrollPos) { - if (newScrollPos instanceof ScrollScene) { - if (newScrollPos.parent() === ScrollMagic) { // check if this controller is the parent - ScrollMagic.scrollTo(newScrollPos.scrollOffset()); + this.scrollTo = function (scrollTarget) { + if (scrollTarget instanceof ScrollScene) { + if (scrollTarget.parent() === ScrollMagic) { // check if this controller is the parent + ScrollMagic.scrollTo(scrollTarget.scrollOffset()); + } else { + log (2, "scrollTo(): The supplied scene does not belong to this controller. Scroll cancelled.", scrollTarget); + } + } else if ($.type(scrollTarget) === "string" || isDomElement(scrollTarget) || scrollTarget instanceof $) { + var $elm = $(scrollTarget).first(); + if ($elm[0]) { + var + offset = $elm.offset(); + ScrollMagic.scrollTo(_options.vertical ? offset.top : offset.left); } else { - log (1, "The supplied scene does not belong to this controller."); + log (2, "scrollTo(): The supplied element could not be found. Scroll cancelled.", scrollTarget); } - } else if ($.isFunction(newScrollPos)) { - setScrollPos = newScrollPos; + } else if ($.isFunction(scrollTarget)) { + setScrollPos = scrollTarget; } else { - setScrollPos.call(_options.container[0], newScrollPos); + setScrollPos.call(_options.container[0], scrollTarget); } return ScrollMagic; }; @@ -637,10 +689,10 @@

    Source: jquery.scrollmagic.js

    * @returns {null} Null to unset handler variables. */ this.destroy = function (resetScenes) { - window.clearTimeout(_triggerPosUpdateInterval); - while (_sceneObjects.length > 0) { - var scene = _sceneObjects[_sceneObjects.length - 1]; - scene.destroy(resetScenes); + window.clearTimeout(_sceneRefreshInterval); + var i = _sceneObjects.length; + while (i--) { + _sceneObjects[i].destroy(resetScenes); } _options.container.off("scroll resize", onChange); if (_tickerUsed) { @@ -679,10 +731,11 @@

    Source: jquery.scrollmagic.js

    * @param {object} [options] - Options for the Scene. The options can be updated at any time. Instead of setting the options for each scene individually you can also set them globally in the controller as the controllers `globalSceneOptions` option. The object accepts the same properties as the ones below. When a scene is added to the controller the options defined using the ScrollScene constructor will be overwritten by those set in `globalSceneOptions`. - * @param {number} [options.duration=0] - The duration of the scene. - If `0` tweens will auto-play when reaching the scene start point, pins will be pinned indefinetly starting at the start position. + * @param {(number|function)} [options.duration=0] - The duration of the scene. + If `0` tweens will auto-play when reaching the scene start point, pins will be pinned indefinetly starting at the start position. + A function retuning the duration value is also supported. Please see `ScrollScene.duration()` for details. * @param {number} [options.offset=0] - Offset Value for the Trigger Position. If no triggerElement is defined this will be the scroll distance from the start of the page, after which the scene will start. - * @param {(string|object)} [options.triggerElement=null] - Selector, DOM Object or jQuery Object that defines the start of the scene. If undefined the scene will start right at the start of the page (unless an offset is set). + * @param {(string|object)} [options.triggerElement=null] - Selector, DOM object or jQuery Object that defines the start of the scene. If undefined the scene will start right at the start of the page (unless an offset is set). * @param {(number|string)} [options.triggerHook="onCenter"] - Can be a number between 0 and 1 defining the position of the trigger Hook in relation to the viewport. Can also be defined using a string: ** `"onEnter"` => `1` @@ -691,7 +744,7 @@

    Source: jquery.scrollmagic.js

    * @param {boolean} [options.reverse=true] - Should the scene reverse, when scrolling up? * @param {boolean} [options.tweenChanges=false] - Tweens Animation to the progress target instead of setting it. Does not affect animations where duration is `0`. - * @param {number} [options.loglevel=2] - Loglevel for debugging. + * @param {number} [options.loglevel=2] - Loglevel for debugging. Note that logging is disabled in the minified version of ScrollMagic. ** `0` => silent ** `1` => errors ** `2` => errors, warnings @@ -707,13 +760,13 @@

    Source: jquery.scrollmagic.js

    */ var - TRIGGER_HOOK_STRINGS = ["onCenter", "onEnter", "onLeave"], + TRIGGER_HOOK_VALUES = {"onCenter" : 0.5, "onEnter" : 1, "onLeave" : 0}, NAMESPACE = "ScrollScene", DEFAULT_OPTIONS = { duration: 0, offset: 0, triggerElement: null, - triggerHook: TRIGGER_HOOK_STRINGS[0], + triggerHook: "onCenter", reverse: true, tweenChanges: false, loglevel: 2 @@ -731,13 +784,99 @@

    Source: jquery.scrollmagic.js

    _state = 'BEFORE', _progress = 0, _scrollOffset = {start: 0, end: 0}, // reflects the parent's scroll position for the start and end of the scene respectively - _triggerOffset = 0, + _triggerPos = 0, _enabled = true, + _durationUpdateMethod, _parent, _tween, _pin, - _pinOptions; - + _pinOptions, + _cssClasses, + _cssClassElm; + + // object containing validator functions for various options + var _validate = { + "unknownOptionSupplied" : function () { + $.each(_options, function (key, value) { + if (!DEFAULT_OPTIONS.hasOwnProperty(key)) { + log(2, "WARNING: Unknown option \"" + key + "\""); + delete _options[key]; + } + }); + }, + "duration" : function () { + if ($.isFunction(_options.duration)) { + _durationUpdateMethod = _options.duration; + try { + _options.duration = parseFloat(_durationUpdateMethod()); + } catch (e) { + log(1, "ERROR: Invalid return value of supplied function for option \"duration\":", _options.duration); + _durationUpdateMethod = undefined; + _options.duration = DEFAULT_OPTIONS.duration; + } + } else { + _options.duration = parseFloat(_options.duration); + if (!$.isNumeric(_options.duration) || _options.duration < 0) { + log(1, "ERROR: Invalid value for option \"duration\":", _options.duration); + _options.duration = DEFAULT_OPTIONS.duration; + } + } + }, + "offset" : function () { + _options.offset = parseFloat(_options.offset); + if (!$.isNumeric(_options.offset)) { + log(1, "ERROR: Invalid value for option \"offset\":", _options.offset); + _options.offset = DEFAULT_OPTIONS.offset; + } + }, + "triggerElement" : function () { + if (_options.triggerElement !== null && $(_options.triggerElement).length === 0) { + log(1, "ERROR: Element defined in option \"triggerElement\" was not found:", _options.triggerElement); + _options.triggerElement = DEFAULT_OPTIONS.triggerElement; + } + }, + "triggerHook" : function () { + if (!(_options.triggerHook in TRIGGER_HOOK_VALUES)) { + if ($.isNumeric(_options.triggerHook)) { + _options.triggerHook = parseFloat(_options.triggerHook); + } else { + log(1, "ERROR: Invalid value for option \"triggerHook\": ", _options.triggerHook); + _options.triggerHook = DEFAULT_OPTIONS.triggerHook; + } + } + }, + "reverse" : function () { + _options.reverse = !!_options.reverse; // force boolean + }, + "tweenChanges" : function () { + _options.tweenChanges = !!_options.tweenChanges; // force boolean + }, + "loglevel" : function () { + _options.loglevel = parseInt(_options.loglevel); + if (!$.isNumeric(_options.loglevel) || _options.loglevel < 0 || _options.loglevel > 3) { + var wrongval = _options.loglevel; + _options.loglevel = DEFAULT_OPTIONS.loglevel; + log(1, "ERROR: Invalid value for option \"loglevel\":", wrongval); + } + }, + "checkIfTriggerElementIsTweened" : function () { + // check if there are position tweens defined for the trigger and warn about it :) + if (_tween && _parent && _options.triggerElement && _options.loglevel >= 2) {// parent is needed to know scroll direction. + var + triggerTweens = _tween.getTweensOf($(_options.triggerElement)), + vertical = _parent.info("vertical"); + $.each(triggerTweens, function (index, value) { + var + tweenvars = value.vars.css || value.vars, + condition = vertical ? (tweenvars.top !== undefined || tweenvars.bottom !== undefined) : (tweenvars.left !== undefined || tweenvars.right !== undefined); + if (condition) { + log(2, "WARNING: Tweening the position of the trigger element affects the scene timing and should be avoided!"); + return false; + } + }); + } + }, + }; /* * ---------------------------------------------------------------- @@ -750,31 +889,36 @@

    Source: jquery.scrollmagic.js

    * @private */ var construct = function () { - checkOptionsValidity(); - - // internal event listeners - ScrollScene.on("change.internal", function (e) { - checkOptionsValidity(); - if (e.what !== "loglevel" && e.what !== "tweenChanges") { // no need for a scene update scene with these options... - if (e.what === "triggerElement") { - ScrollScene.updateTriggerElementPosition(); - } - if (e.what === "offset" || e.what === "duration" || e.what === "triggerHook") { - updateScrollOffset(); + validateOption(); + + // event listeners + ScrollScene + .on("change.internal", function (e) { + if (e.what !== "loglevel" && e.what !== "tweenChanges") { // no need for a scene update scene with these options... + if (e.what === "triggerElement") { + updateTriggerElementPosition(); + } else if (e.what === "reverse") { // the only property left that may have an impact on the current scene state. Everything else is handled by the shift event. + ScrollScene.update(); + } } - ScrollScene.update(); - if ((_state !== "DURING" && e.what == "duration") || (_state === "AFTER" && _options.duration === 0)) { // if duration changed outside of scene (inside scene progress updates pin position) or duration is 0, we are beyond trigger and some other value changed. + }) + .on("shift.internal", function (e) { + updateScrollOffset(); + ScrollScene.update(); // update scene to reflect new position + if ((_state === "AFTER" && e.reason === "duration") || (_state === 'DURING' && _options.duration === 0)) { + // if [duration changed after a scene (inside scene progress updates pin position)] or [duration is 0, we are in pin phase and some other value changed]. updatePinState(); } - } - }); - // internal event listeners - ScrollScene.on("progress.internal", function (e) { - updateTweenProgress(); - updatePinState(); - }); + }) + .on("progress.internal", function (e) { + updateTweenProgress(); + updatePinState(); + }) + .on("destroy", function (e) { + e.preventDefault(); // otherwise jQuery would call target.destroy() by default. + }); }; - + /** * Send a debug message to the console. * @private @@ -794,54 +938,39 @@

    Source: jquery.scrollmagic.js

    }; /** - * Check the validity of all options and reset to default if neccessary. + * Checks the validity of a specific or all options and reset to default if neccessary. * @private */ - var checkOptionsValidity = function () { - $.each(_options, function (key, value) { - if (!DEFAULT_OPTIONS.hasOwnProperty(key)) { - log(2, "WARNING: Unknown option \"" + key + "\""); - delete _options[key]; + var validateOption = function (check) { + if (!arguments.length) { + check = []; + for (var key in _validate){ + check.push(key); } - }); - _options.duration = parseFloat(_options.duration); - if (!$.isNumeric(_options.duration) || _options.duration < 0) { - log(1, "ERROR: Invalid value for option \"duration\":", _options.duration); - _options.duration = DEFAULT_OPTIONS.duration; - } - _options.offset = parseFloat(_options.offset); - if (!$.isNumeric(_options.offset)) { - log(1, "ERROR: Invalid value for option \"offset\":", _options.offset); - _options.offset = DEFAULT_OPTIONS.offset; - } - if (_options.triggerElement !== null && $(_options.triggerElement).length === 0) { - log(1, "ERROR: Element defined in option \"triggerElement\" was not found:", _options.triggerElement); - _options.triggerElement = DEFAULT_OPTIONS.triggerElement; + } else if (!$.isArray(check)) { + check = [check]; } - if (!$.isNumeric(_options.triggerHook) && $.inArray(_options.triggerHook, TRIGGER_HOOK_STRINGS) == -1) { - log(1, "ERROR: Invalid value for option \"triggerHook\": ", _options.triggerHook); - _options.triggerHook = DEFAULT_OPTIONS.triggerHook; - } - if (!$.isNumeric(_options.loglevel) || _options.loglevel < 0 || _options.loglevel > 3) { - var wrongval = _options.loglevel; - _options.loglevel = DEFAULT_OPTIONS.loglevel; - log(1, "ERROR: Invalid value for option \"loglevel\":", wrongval); - } - if (_tween && _parent && _options.triggerElement && _options.loglevel >= 2) {// parent is needed to know scroll direction. - // check if there are position tweens defined for the trigger and warn about it :) - var - triggerTweens = _tween.getTweensOf($(_options.triggerElement)), - vertical = _parent.info("vertical"); - $.each(triggerTweens, function (index, value) { - var - tweenvars = value.vars.css || value.vars, - condition = vertical ? (tweenvars.top !== undefined || tweenvars.bottom !== undefined) : (tweenvars.left !== undefined || tweenvars.right !== undefined); - if (condition) { - log(2, "WARNING: Tweening the position of the trigger element affects the scene timing and should be avoided!"); - return false; - } - }); + $.each(check, function (key, value) { + if (_validate[value]) { + _validate[value](); + } + }); + }; + + /** + * Helper used by the setter/getters for scene options + * @private + */ + var changeOption = function(varname, newval) { + var + changed = false, + oldval = _options[varname]; + if (_options[varname] != newval) { + _options[varname] = newval; + validateOption(varname); // resets to default if necessary + changed = oldval != _options[varname]; } + return changed; }; /** @@ -855,7 +984,7 @@

    Source: jquery.scrollmagic.js

    * @private */ var updateScrollOffset = function () { - _scrollOffset = {start: _triggerOffset + _options.offset}; + _scrollOffset = {start: _triggerPos + _options.offset}; if (_parent && _options.triggerElement) { // take away triggerHook portion to get relative to top _scrollOffset.start -= _parent.info("size") * ScrollScene.triggerHook(); @@ -863,6 +992,66 @@

    Source: jquery.scrollmagic.js

    _scrollOffset.end = _scrollOffset.start + _options.duration; }; + /** + * Updates the duration if set to a dynamic function. + * This method is called when the scene is added to a controller and in regular intervals from the controller through scene.refresh(). + * + * @fires {@link ScrollScene.change}, if the duration changed + * @fires {@link ScrollScene.shift}, if the duration changed + * + * @param {boolean} [suppressEvents=false] - If true the shift event will be suppressed. + */ + var updateDuration = function (suppressEvents) { + // update duration + if (_durationUpdateMethod) { + var varname = "duration"; + if (changeOption(varname, _durationUpdateMethod()) && !suppressEvents) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); + ScrollScene.trigger("shift", {reason: varname}); + } + } + }; + + /** + * Updates the position of the triggerElement, if present. + * This method is called ... + * - ... when the triggerElement is changed + * - ... when the scene is added to a (new) controller + * - ... in regular intervals from the controller through scene.refresh(). + * + * @fires {@link ScrollScene.shift}, if the position changed + * + * @param {boolean} [suppressEvents=false] - If true the shift event will be suppressed. + */ + var updateTriggerElementPosition = function (suppressEvents) { + var elementPos = 0; + if (_parent && _options.triggerElement) { + var + element = $(_options.triggerElement).first(), + controllerInfo = _parent.info(), + containerOffset = getOffset(controllerInfo.container), // container position is needed because element offset is returned in relation to document, not in relation to container. + param = controllerInfo.vertical ? "top" : "left"; // which param is of interest ? + + // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. + while (element.parent().data("ScrollMagicPinSpacer")) { + element = element.parent(); + } + + var elementOffset = getOffset(element); + + if (!controllerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent + containerOffset[param] -= controllerInfo.scrollPos; + } + + elementPos = elementOffset[param] - containerOffset[param]; + } + var changed = elementPos != _triggerPos; + _triggerPos = elementPos; + if (changed && !suppressEvents) { + ScrollScene.trigger("shift", {reason: "triggerElementPosition"}); + } + }; + /** * Update the tween progress. * @private @@ -875,7 +1064,7 @@

    Source: jquery.scrollmagic.js

    var progress = (to >= 0 && to <= 1) ? to : _progress; if (_tween.repeat() === -1) { // infinite loop, so not in relation to progress - if ((_state === "DURING" || (_state === "AFTER" && _options.duration === 0)) && _tween.paused()) { + if (_state === "DURING" && _tween.paused()) { _tween.play(); } else if (_state !== "DURING" && !_tween.paused()) { _tween.pause(); @@ -886,7 +1075,7 @@

    Source: jquery.scrollmagic.js

    // no infinite loop - so should we just play or go to a specific point in time? if (_options.duration === 0) { // play the animation - if (_state === "AFTER") { // play from 0 to 1 + if (_state === "DURING") { // play from 0 to 1 _tween.play(); } else { // play from 1 to 0 _tween.reverse(); @@ -919,7 +1108,7 @@

    Source: jquery.scrollmagic.js

    var containerInfo = _parent.info(); - if (!forceUnpin && (_state === "DURING" || (_state === "AFTER" && _options.duration === 0))) { // during scene or if duration is 0 and we are past the trigger + if (!forceUnpin && _state === "DURING") { // during scene or if duration is 0 and we are past the trigger // pinned state if (_pin.css("position") != "fixed") { // change state before updating pin spacer (position changes due to fixed collapsing might occur.) @@ -959,7 +1148,7 @@

    Source: jquery.scrollmagic.js

    if (!_pinOptions.pushFollowers) { newCSS[containerInfo.vertical ? "top" : "left"] = _options.duration * _progress; - } else { + } else if (_options.duration > 0) { // only concerns scenes with duration if (_state === "AFTER" && parseFloat(_pinOptions.spacer.css("padding-top")) === 0) { change = true; // if in after state but havent updated spacer yet (jumped past pin) } else if (_state === "BEFORE" && parseFloat(_pinOptions.spacer.css("padding-bottom")) === 0) { // before @@ -992,7 +1181,7 @@

    Source: jquery.scrollmagic.js

    pinned = (_pin.css("position") == "fixed"), vertical = _parent.info("vertical"), $spacercontent = _pinOptions.spacer.children().first(), // usually the pined element but can also be another spacer (cascaded pins) - marginCollapse = ($.inArray(_pinOptions.spacer.css("display"), ["block", "flex", "list-item", "table", "-webkit-box"]) > -1), + marginCollapse = isMarginCollapseType(_pinOptions.spacer.css("display")), css = {}; if (marginCollapse) { @@ -1004,11 +1193,11 @@

    Source: jquery.scrollmagic.js

    // set new size // if relsize: spacer -> pin | else: pin -> spacer - if (_pinOptions.relSize.width) { + if (_pinOptions.relSize.width || _pinOptions.relSize.autoFullWidth) { if (pinned) { if ($(window).width() == _pinOptions.spacer.parent().width()) { // relative to body - _pin.css("width", "inherit"); + _pin.css("width", _pinOptions.relSize.autoFullWidth ? "100%" : "inherit"); } else { // not relative to body -> need to calculate _pin.css("width", _pinOptions.spacer.width()); @@ -1017,7 +1206,9 @@

    Source: jquery.scrollmagic.js

    _pin.css("width", "100%"); } } else { - css["min-width"] = $spacercontent.outerWidth(true); // needed for cascading pins + // minwidth is needed for cascading pins. + // margin is only included if it's a cascaded pin to resolve an IE9 bug + css["min-width"] = $spacercontent.outerWidth(!$spacercontent.is(_pin)); css.width = pinned ? css["min-width"] : "auto"; } if (_pinOptions.relSize.height) { @@ -1042,7 +1233,6 @@

    Source: jquery.scrollmagic.js

    css["padding" + (vertical ? "Top" : "Left")] = _options.duration * _progress; css["padding" + (vertical ? "Bottom" : "Right")] = _options.duration * (1 - _progress); } - _pinOptions.spacer.css(css); } }; @@ -1053,7 +1243,7 @@

    Source: jquery.scrollmagic.js

    * So this function is called on resize and scroll of the document. * @private */ - var updatePinInContainer = function (e) { + var updatePinInContainer = function () { if (_parent && _pin && _state === "DURING" && !_parent.info("isDocument")) { updatePinState(); } @@ -1065,11 +1255,11 @@

    Source: jquery.scrollmagic.js

    * So this function is called on resize of the container. * @private */ - var updateRelativePinSpacer = function (e) { - if ( _parent && _pin &&// well, duh - (_state === "DURING" || _state === "AFTER" && _options.duration === 0) &&// element in pinned state? - ( // is width or height relatively sized, but not in relation to body? then we need to recalc. - (_pinOptions.relSize.width && $(window).width() != _pinOptions.spacer.parent().width()) || + var updateRelativePinSpacer = function () { + if ( _parent && _pin && // well, duh + _state === "DURING" && // element in pinned state? + ( // is width or height relatively sized, but not in relation to body? then we need to recalc. + ((_pinOptions.relSize.width || _pinOptions.relSize.autoFullWidth) && $(window).width() != _pinOptions.spacer.parent().width()) || (_pinOptions.relSize.height && $(window).height() != _pinOptions.spacer.parent().height()) ) ) { @@ -1077,6 +1267,17 @@

    Source: jquery.scrollmagic.js

    } }; + /** + * Is called, when the mousewhel is used while over a pinned element. + * If the scene is in fixed state scroll events used to be ignored. This forwards the event to the scroll container. + * @private + */ + var onMousewheelOverPin = function (e) { + if (_parent && _pin && _state === "DURING") { // in pin state + _parent.scrollTo(_parent.info("scrollPos") - (e.originalEvent.wheelDelta/3 || -e.originalEvent.detail*30)); + } + }; + /* * ---------------------------------------------------------------- @@ -1100,6 +1301,14 @@

    Source: jquery.scrollmagic.js

    /** * **Get** or **Set** the duration option value. + * As a setter it also accepts a function returning a numeric value. + * This is particularly useful for responsive setups. + * + * The duration is updated using the supplied function every time `ScrollScene.refresh()` is called, which happens periodically from the controller (see ScrollMagic option `sceneRefreshInterval`). + * _**NOTE:** Be aware that it's an easy way to kill performance, if you supply a function that has high CPU demand. + * Even for size and position calculations it is recommended to use a variable to cache the value. (see example) + * This counts double if you use the same function for multiple scenes._ + * * @public * @example * // get the current duration value @@ -1108,17 +1317,36 @@

    Source: jquery.scrollmagic.js

    * // set a new duration * scene.duration(300); * + * // use a function to automatically adjust the duration to the window height. + * var durationValueCache; + * function getDuration () { + * return durationValueCache; + * } + * function updateDuration (e) { + * durationValueCache = $(window).innerHeight(); + * } + * $(window).on("resize", updateDuration); // update the duration when the window size changes + * $(window).triggerHandler("resize"); // set to initial value + * scene.duration(getDuration); // supply duration method + * * @fires {@link ScrollScene.change}, when used as setter - * @param {number} [newDuration] - The new duration of the scene. + * @fires {@link ScrollScene.shift}, when used as setter + * @param {(number|function)} [newDuration] - The new duration of the scene. * @returns {number} `get` - Current scene duration. * @returns {ScrollScene} `set` - Parent object for chaining. */ this.duration = function (newDuration) { + var varname = "duration"; if (!arguments.length) { // get - return _options.duration; - } else if (_options.duration != newDuration) { // set - _options.duration = newDuration; - ScrollScene.trigger("change", {what: "duration", newval: newDuration}); // fire event + return _options[varname]; + } else { + if (!$.isFunction(newDuration)) { + _durationUpdateMethod = undefined; + } + if (changeOption(varname, newDuration)) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); + ScrollScene.trigger("shift", {reason: varname}); + } } return ScrollScene; }; @@ -1134,22 +1362,25 @@

    Source: jquery.scrollmagic.js

    * scene.offset(100); * * @fires {@link ScrollScene.change}, when used as setter + * @fires {@link ScrollScene.shift}, when used as setter * @param {number} [newOffset] - The new offset of the scene. * @returns {number} `get` - Current scene offset. * @returns {ScrollScene} `set` - Parent object for chaining. */ this.offset = function (newOffset) { + var varname = "offset"; if (!arguments.length) { // get - return _options.offset; - } else if (_options.offset != newOffset) { // set - _options.offset = newOffset; - ScrollScene.trigger("change", {what: "offset", newval: newOffset}); // fire event + return _options[varname]; + } else if (changeOption(varname, newOffset)) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); + ScrollScene.trigger("shift", {reason: varname}); } return ScrollScene; }; /** * **Get** or **Set** the triggerElement option value. + * Does **not** fire `ScrollScene.shift`, because changing the trigger Element doesn't necessarily mean the start position changes. This will be determined in `ScrollScene.refresh()`, which is automatically triggered. * @public * @example * // get the current triggerElement @@ -1159,7 +1390,7 @@

    Source: jquery.scrollmagic.js

    * scene.triggerElement("#trigger"); * // set a new triggerElement using a jQuery Object * scene.triggerElement($("#trigger")); - * // set a new triggerElement using a DOM Object + * // set a new triggerElement using a DOM object * scene.triggerElement(document.getElementById("trigger")); * * @fires {@link ScrollScene.change}, when used as setter @@ -1168,11 +1399,11 @@

    Source: jquery.scrollmagic.js

    * @returns {ScrollScene} `set` - Parent object for chaining. */ this.triggerElement = function (newTriggerElement) { + var varname = "triggerElement"; if (!arguments.length) { // get - return _options.triggerElement; - } else if (_options.triggerElement != newTriggerElement) { // set - _options.triggerElement = newTriggerElement; - ScrollScene.trigger("change", {what: "triggerElement", newval: newTriggerElement}); // fire event + return _options[varname]; + } else if (changeOption(varname, newTriggerElement)) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); } return ScrollScene; }; @@ -1190,34 +1421,18 @@

    Source: jquery.scrollmagic.js

    * scene.triggerHook(0.7); * * @fires {@link ScrollScene.change}, when used as setter + * @fires {@link ScrollScene.shift}, when used as setter * @param {(number|string)} [newTriggerHook] - The new triggerHook of the scene. See {@link ScrollScene} parameter description for value options. * @returns {number} `get` - Current triggerHook (ALWAYS numerical). * @returns {ScrollScene} `set` - Parent object for chaining. */ this.triggerHook = function (newTriggerHook) { + var varname = "triggerHook"; if (!arguments.length) { // get - var triggerPoint; - if ($.isNumeric(_options.triggerHook)) { - triggerPoint = _options.triggerHook; - } else { - switch(_options.triggerHook) { - case "onCenter": - triggerPoint = 0.5; - break; - case "onLeave": - triggerPoint = 0; - break; - case "onEnter": - /* falls through */ - default: - triggerPoint = 1; - break; - } - } - return triggerPoint; - } else if (_options.triggerHook != newTriggerHook) { // set - _options.triggerHook = newTriggerHook; - ScrollScene.trigger("change", {what: "triggerHook", newval: newTriggerHook}); // fire event + return $.isNumeric(_options[varname]) ? _options[varname] : TRIGGER_HOOK_VALUES[_options[varname]]; + } else if (changeOption(varname, newTriggerHook)) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); + ScrollScene.trigger("shift", {reason: varname}); } return ScrollScene; }; @@ -1238,11 +1453,11 @@

    Source: jquery.scrollmagic.js

    * @returns {ScrollScene} `set` - Parent object for chaining. */ this.reverse = function (newReverse) { + var varname = "reverse"; if (!arguments.length) { // get - return _options.reverse; - } else if (_options.reverse != newReverse) { // set - _options.reverse = newReverse; - ScrollScene.trigger("change", {what: "reverse", newval: newReverse}); // fire event + return _options[varname]; + } else if (changeOption(varname, newReverse)) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); } return ScrollScene; }; @@ -1263,11 +1478,11 @@

    Source: jquery.scrollmagic.js

    * @returns {ScrollScene} `set` - Parent object for chaining. */ this.tweenChanges = function (newTweenChanges) { + var varname = "tweenChanges"; if (!arguments.length) { // get - return _options.tweenChanges; - } else if (_options.tweenChanges != newTweenChanges) { // set - _options.tweenChanges = newTweenChanges; - ScrollScene.trigger("change", {what: "tweenChanges", newval: newTweenChanges}); // fire event + return _options[varname]; + } else if (changeOption(varname, newTweenChanges)) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); } return ScrollScene; }; @@ -1288,11 +1503,11 @@

    Source: jquery.scrollmagic.js

    * @returns {ScrollScene} `set` - Parent object for chaining. */ this.loglevel = function (newLoglevel) { + var varname = "loglevel"; if (!arguments.length) { // get - return _options.loglevel; - } else if (_options.loglevel != newLoglevel) { // set - _options.loglevel = newLoglevel; - ScrollScene.trigger("change", {what: "loglevel", newval: newLoglevel}); // fire event + return _options[varname]; + } else if (changeOption(varname, newLoglevel)) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); } return ScrollScene; }; @@ -1311,21 +1526,21 @@

    Source: jquery.scrollmagic.js

    }; /** - * **Get** the trigger offset of the scene (including the value of the `offset` option). + * **Get** the trigger position of the scene (including the value of the `offset` option). * @public * @example - * // get the scene's trigger offset - * var triggerOffset = scene.triggerOffset(); + * // get the scene's trigger position + * var triggerPosition = scene.triggerPosition(); * * @returns {number} Start position of the scene. Top position value for vertical and left position value for horizontal scrolls. */ - this.triggerOffset = function () { + this.triggerPosition = function () { var pos = _options.offset; // the offset is the basis if (_parent) { // get the trigger position if (_options.triggerElement) { // Element as trigger - pos += _triggerOffset; + pos += _triggerPos; } else { // return the height of the triggerHook to start at the beginning pos += _parent.info("size") * ScrollScene.triggerHook(); @@ -1334,10 +1549,19 @@

    Source: jquery.scrollmagic.js

    return pos; }; + /** + * **Get** the trigger offset of the scene (including the value of the `offset` option). + * @public + * @deprecated Method is deprecated since 1.1.0. You should now use {@link ScrollScene.triggerPosition} + */ + this.triggerOffset = function () { + return ScrollScene.triggerPosition(); + }; + /** * **Get** the current scroll offset for the start of the scene. * Mind, that the scrollOffset is related to the size of the container, if `triggerHook` is bigger than `0` (or `"onLeave"`). - * This means, that resizing the container will influence the scene's start offset. + * This means, that resizing the container or changing the `triggerHook` will influence the scene's start offset. * @public * @example * // get the current scroll offset for the start and end of the scene. @@ -1361,8 +1585,9 @@

    Source: jquery.scrollmagic.js

    * Updates the Scene in the parent Controller to reflect the current state. * This is the equivalent to `ScrollMagic.updateScene(scene, immediately)`. * The update method calculates the scene's start and end position (based on the trigger element, trigger hook, duration and offset) and checks it against the current scroll position of the container. - * It then updates the current scene state accordingly (or does nothing, if the state is already correct) – Pins will be set to their correct position and tweens will be updated to their correct progress. - * *Note:* This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters. + * It then updates the current scene state accordingly (or does nothing, if the state is already correct) – Pins will be set to their correct position and tweens will be updated to their correct progress. + * This means an update doesn't necessarily result in a progress change. The `progress` event will be fired if the progress has indeed changed between this update and the last. + * _**NOTE:** This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters._ * @public * @example * // update the scene on next tick @@ -1403,62 +1628,63 @@

    Source: jquery.scrollmagic.js

    return ScrollScene; }; - /** - * Updates the position of the triggerElement, if present. - * This method is automatically called ... - * - ... when the triggerElement is changed - * - ... when the scene is added to a (new) controller - * - ... in regular intervals from the controller. See {@link ScrollMagic} option `triggerPosUpdateInterval`. + * Updates dynamic scene variables like the trigger element position or the duration. + * This method is automatically called in regular intervals from the controller. See {@link ScrollMagic} option `sceneRefreshInterval`. * - * You can call it to minimize lag, when you intentionally change the position of the triggerElement. + * You can call it to minimize lag, for example when you intentionally change the position of the triggerElement. + * If you don't it will simply be updated in the next refresh interval of the container, which is usually sufficient. + * * @public - * @since v1.1.0 + * @since 1.1.0 * @example * scene = new ScrollScene({triggerElement: "#trigger"}); * * // change the position of the trigger * $("#trigger").css("top", 500); * // immediately let the scene know of this change - * scene.updateTriggerElementPosition(); + * scene.refresh(); + * + * @fires {@link ScrollScene.shift}, if the trigger element position or the duration changed + * @fires {@link ScrollScene.change}, if the duration changed * * @returns {ScrollScene} Parent object for chaining. */ - this.updateTriggerElementPosition = function () { - var elementPos = 0; - if (_parent && _options.triggerElement) { - var - element = $(_options.triggerElement).first(), - controllerInfo = _parent.info(), - containerOffset = getOffset(controllerInfo.container), // container position is needed because element offset is returned in relation to document, not in relation to container. - param = controllerInfo.vertical ? "top" : "left"; // which param is of interest ? - - // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. - while (element.parent().data("ScrollMagicPinSpacer")) { - element = element.parent(); - } - - var elementOffset = getOffset(element); - - if (!controllerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent - containerOffset[param] -= controllerInfo.scrollPos; - } - - elementPos = elementOffset[param] - containerOffset[param]; - } - var changed = elementPos != _triggerOffset; - _triggerOffset = elementPos; - if (changed) { - updateScrollOffset(); - ScrollScene.update(); - } + this.refresh = function () { + updateDuration(); + updateTriggerElementPosition(); + // update trigger element position return ScrollScene; }; - /** * **Get** or **Set** the scene's progress. - * Usually it shouldn't be necessary to use this as a setter, as it is set automatically by scene.update(). + * Usually it shouldn't be necessary to use this as a setter, as it is set automatically by scene.update(). + * The order in which the events are fired depends on the duration of the scene: + * 1. Scenes with `duration == 0`: + * Scenes that have no duration by definition have no ending. Thus the `end` event will never be fired. + * When the trigger position of the scene is passed the events are always fired in this order: + * `enter`, `start`, `progress` when scrolling forward + * and + * `progress`, `start`, `leave` when scrolling in reverse + * 2. Scenes with `duration > 0`: + * Scenes with a set duration have a defined start and end point. + * When scrolling past the start position of the scene it will fire these events in this order: + * `enter`, `start`, `progress` + * When continuing to scroll and passing the end point it will fire these events: + * `progress`, `end`, `leave` + * When reversing through the end point these events are fired: + * `enter`, `end`, `progress` + * And when continuing to scroll past the start position in reverse it will fire: + * `progress`, `start`, `leave` + * In between start and end the `progress` event will be called constantly, whenever the progress changes. + * + * In short: + * `enter` events will always trigger **before** the progress update and `leave` envents will trigger **after** the progress update. + * `start` and `end` will always trigger at their respective position. + * + * Please review the event descriptions for details on the events and the event object that is passed to the callback. + * * @public * @example * // get the current scene progress @@ -1484,45 +1710,53 @@

    Source: jquery.scrollmagic.js

    var doUpdate = false, oldState = _state, - scrollDirection = _parent ? _parent.info("scrollDirection") : "PAUSED"; - if (progress <= 0 && _state !== 'BEFORE' && (progress >= _progress || _options.reverse)) { - // go back to initial state - _progress = 0; - _state = 'BEFORE'; - doUpdate = true; - } else if (progress > 0 && progress < 1 && (progress >= _progress || _options.reverse)) { - _progress = progress; - _state = 'DURING'; - doUpdate = true; - } else if (progress >= 1 && _state !== 'AFTER') { - _progress = 1; - _state = 'AFTER'; - doUpdate = true; - } else if (_state === "DURING" && !_options.reverse) { - updatePinState(); // in case we scrolled back and reverse is disabled => update the pin position, so it doesn't scroll back as well. + scrollDirection = _parent ? _parent.info("scrollDirection") : 'PAUSED', + reverseOrForward = _options.reverse || progress >= _progress; + if (_options.duration === 0) { + // zero duration scenes + doUpdate = _progress != progress; + _progress = progress < 1 && reverseOrForward ? 0 : 1; + _state = _progress === 0 ? 'BEFORE' : 'DURING'; + } else { + // scenes with start and end + if (progress <= 0 && _state !== 'BEFORE' && reverseOrForward) { + // go back to initial state + _progress = 0; + _state = 'BEFORE'; + doUpdate = true; + } else if (progress > 0 && progress < 1 && reverseOrForward) { + _progress = progress; + _state = 'DURING'; + doUpdate = true; + } else if (progress >= 1 && _state !== 'AFTER') { + _progress = 1; + _state = 'AFTER'; + doUpdate = true; + } else if (_state === 'DURING' && !reverseOrForward) { + updatePinState(); // in case we scrolled backwards mid-scene and reverse is disabled => update the pin position, so it doesn't move back as well. + } } if (doUpdate) { // fire events var eventVars = {progress: _progress, state: _state, scrollDirection: scrollDirection}, - stateChanged = _state != oldState, - instantReverse = (_state === 'BEFORE' && _options.duration === 0); + stateChanged = _state != oldState; - if (stateChanged) { - if (_state === 'DURING' || _options.duration === 0) { - ScrollScene.trigger("enter", eventVars); - } - if (_state === 'BEFORE' || oldState === 'BEFORE') { - ScrollScene.trigger(instantReverse ? "end" : "start", eventVars); + var trigger = function (eventName) { // tmp helper to simplify code + ScrollScene.trigger(eventName, eventVars); + }; + + if (stateChanged) { // enter events + if (oldState !== 'DURING') { + trigger("enter"); + trigger(oldState === 'BEFORE' ? "start" : "end"); } } - ScrollScene.trigger("progress", eventVars); - if (stateChanged) { - if (_state === 'AFTER' || oldState === 'AFTER') { - ScrollScene.trigger(instantReverse ? "start" : "end", eventVars); - } - if (_state !== 'DURING' || _options.duration === 0) { - ScrollScene.trigger("leave", eventVars); + trigger("progress"); + if (stateChanged) { // leave events + if (_state !== 'DURING') { + trigger(_state === 'BEFORE' ? "start" : "end"); + trigger("leave"); } } } @@ -1571,7 +1805,7 @@

    Source: jquery.scrollmagic.js

    _tween.yoyo(TweenMaxObject.yoyo()); } } - checkOptionsValidity(); + validateOption("checkIfTriggerElementIsTweened"); log(3, "added tween"); updateTweenProgress(); return ScrollScene; @@ -1605,8 +1839,8 @@

    Source: jquery.scrollmagic.js

    /** * Pin an element for the duration of the tween. - * If the scene duration is 0 the element will never be unpinned. - * Note, that pushFollowers has no effect, when the scene duration is 0. + * If the scene duration is 0 the element will only be unpinned, if the user scrolls back past the start position. + * _**NOTE:** The option `pushFollowers` has no effect, when the scene duration is 0._ * @public * @example * // pin element and push all following elements down by the amount of the pin duration. @@ -1615,7 +1849,7 @@

    Source: jquery.scrollmagic.js

    * // pin element and keeping all following elements in their place. The pinned element will move past them. * scene.setPin("#pin", {pushFollowers: false}); * - * @param {(string|object)} element - A Selctor, a DOM Object or a jQuery object for the object that is supposed to be pinned. + * @param {(string|object)} element - A Selector targeting an element, a DOM object or a jQuery object that is supposed to be pinned. * @param {object} [settings] - settings for the pin * @param {boolean} [settings.pushFollowers=true] - If `true` following elements will be "pushed" down for the duration of the pin, if `false` the pinned element will just scroll past them. Ignored, when duration is `0`. @@ -1639,7 +1873,7 @@

    Source: jquery.scrollmagic.js

    log(1, "ERROR calling method 'setPin()': Invalid pin element supplied."); return ScrollScene; // cancel } else if (element.css("position") == "fixed") { - log(1, "ERROR: Pin does not work with elements that are positioned 'fixed'."); + log(1, "ERROR calling method 'setPin()': Pin does not work with elements that are positioned 'fixed'."); return ScrollScene; // cancel } @@ -1662,6 +1896,14 @@

    Source: jquery.scrollmagic.js

    sizeCSS = _pin.css(["width", "height"]); _pin.parent().show(); // hack end. + if (sizeCSS.width === "0px" && inFlow && isMarginCollapseType(pinCSS.display)) { + // log (2, "WARNING: Your pinned element probably needs a defined width or it might collapse during pin."); + } + if (!inFlow && settings.pushFollowers) { + log(2, "WARNING: If the pinned element is positioned absolutely pushFollowers is disabled."); + settings.pushFollowers = false; + } + // create spacer var spacer = $("<div></div>") .addClass(settings.spacerClass) @@ -1676,21 +1918,25 @@

    Source: jquery.scrollmagic.js

    "-webkit-box-sizing": "content-box" }); - if (!inFlow && settings.pushFollowers) { - log(2, "WARNING: If the pinned element is positioned absolutely pushFollowers is disabled."); - settings.pushFollowers = false; - } - // set the pin Options + var pinInlineCSS = _pin[0].style; _pinOptions = { spacer: spacer, relSize: { // save if size is defined using % values. if so, handle spacer resize differently... width: sizeCSS.width.slice(-1) === "%", - height: sizeCSS.height.slice(-1) === "%" + height: sizeCSS.height.slice(-1) === "%", + autoFullWidth: sizeCSS.width === "0px" && inFlow && isMarginCollapseType(pinCSS.display) }, pushFollowers: settings.pushFollowers, inFlow: inFlow, // stores if the element takes up space in the document flow - origStyle: _pin.attr("style"), // save old styles (for reset) + origStyle: { + width: pinInlineCSS.width || "", + position: pinInlineCSS.position || "", + top: pinInlineCSS.position || "", + left: pinInlineCSS.position || "", + bottom: pinInlineCSS.position || "", + right: pinInlineCSS.position || "" + }, // save old styles (for reset) pinnedClass: settings.pinnedClass // the class that should be added to the element when pinned }; @@ -1715,7 +1961,9 @@

    Source: jquery.scrollmagic.js

    }); // add listener to document to update pin position in case controller is not the document. - $(window).on("scroll resize", updatePinInContainer); + $(window).on("scroll." + NAMESPACE + "_pin resize." + NAMESPACE + "_pin", updatePinInContainer); + // add mousewheel listener to catch scrolls over fixed elements + _pin.on("mousewheel DOMMouseScroll", onMousewheelOverPin); log(3, "added pin"); @@ -1742,20 +1990,75 @@

    Source: jquery.scrollmagic.js

    if (_pin) { if (reset || !_parent) { // if there's no parent no progress was made anyway... _pin.insertBefore(_pinOptions.spacer) - .attr("style", _pinOptions.origStyle); + .css(_pinOptions.origStyle); _pinOptions.spacer.remove(); } else { if (_state === "DURING") { updatePinState(true); // force unpin at position } } - $(window).off("scroll resize", updatePinInContainer); + $(window).off("scroll." + NAMESPACE + "_pin resize." + NAMESPACE + "_pin"); + _pin.off("mousewheel DOMMouseScroll", onMousewheelOverPin); _pin = undefined; log(3, "removed pin (reset: " + (reset ? "true" : "false") + ")"); } return ScrollScene; }; + /** + * Define a css class modification while the scene is active. + * When the scene triggers the classes will be added to the supplied element and removed, when the scene is over. + * If the scene duration is 0 the classes will only be removed if the user scrolls back past the start position. + * @public + * @example + * // add the class 'myclass' to the element with the id 'my-elem' for the duration of the scene + * scene.setClassToggle("#my-elem", "myclass"); + * + * // add multiple classes to multiple elements defined by the selector '.classChange' + * scene.setClassToggle(".classChange", "class1 class2 class3"); + * + * @param {(string|object)} element - A Selector targeting one or more elements, a DOM object or a jQuery object that is supposed to be modified. + * @param {string} classes - One or more Classnames (separated by space) that should be added to the element during the scene. + * + * @returns {ScrollScene} Parent object for chaining. + */ + this.setClassToggle = function (element, classes) { + var $elm = $(element); + if ($elm.length === 0 || $.type(classes) !== "string") { + log(1, "ERROR calling method 'setClassToggle()': Invalid " + ($elm.length === 0 ? "element" : "classes") + " supplied."); + return ScrollScene; + } + _cssClasses = classes; + _cssClassElm = $elm; + ScrollScene.on("enter.internal_class leave.internal_class", function (e) { + _cssClassElm.toggleClass(_cssClasses, e.type === "enter"); + }); + return ScrollScene; + }; + + /** + * Remove the class binding from the scene. + * @public + * @example + * // remove class binding from the scene without reset + * scene.removeClassToggle(); + * + * // remove class binding and remove the changes it caused + * scene.removeClassToggle(true); + * + * @param {boolean} [reset=false] - If `false` and the classes are currently active, they will remain on the element. If `true` they will be removed. + * @returns {ScrollScene} Parent object for chaining. + */ + this.removeClassToggle = function (reset) { + if (_cssClassElm && reset) { + _cssClassElm.removeClass(_cssClasses); + } + ScrollScene.off("start.internal_class end.internal_class"); + _cssClasses = undefined; + _cssClassElm = undefined; + return ScrollScene; + }; + /** * Add the scene to a controller. * This is the equivalent to `ScrollMagic.addScene(scene)`. @@ -1774,12 +2077,17 @@

    Source: jquery.scrollmagic.js

    _parent.removeScene(ScrollScene); } _parent = controller; - checkOptionsValidity(); - ScrollScene.updateTriggerElementPosition(); + validateOption(); + updateDuration(true); + updateTriggerElementPosition(true); updateScrollOffset(); updatePinSpacerSize(); - _parent.info("container").on("resize", updateRelativePinSpacer); - _parent.info("container").on("resize", updateScrollOffset); + _parent.info("container").on("resize." + NAMESPACE, function () { + updateRelativePinSpacer(); + if (ScrollScene.triggerHook() > 0) { + ScrollScene.trigger("shift", {reason: "containerSize"}); + } + }); log(3, "added " + NAMESPACE + " to controller"); controller.addScene(ScrollScene); ScrollScene.update(); @@ -1826,8 +2134,7 @@

    Source: jquery.scrollmagic.js

    */ this.remove = function () { if (_parent) { - _parent.info("container").off("resize", updateRelativePinSpacer); - _parent.info("container").off("resize", updateScrollOffset); + _parent.info("container").off("resize." + NAMESPACE); var tmpParent = _parent; _parent = undefined; log(3, "removed " + NAMESPACE + " from controller"); @@ -1850,10 +2157,12 @@

    Source: jquery.scrollmagic.js

    * @returns {null} Null to unset handler variables. */ this.destroy = function (reset) { - this.removeTween(reset); - this.removePin(reset); - this.remove(); - this.off("start end enter leave progress change update change.internal progress.internal"); + ScrollScene.removeTween(reset); + ScrollScene.removePin(reset); + ScrollScene.removeClassToggle(reset); + ScrollScene.trigger("destroy", {reset: reset}); + ScrollScene.remove(); + ScrollScene.off("start end enter leave progress change update shift destroy shift.internal change.internal progress.internal"); log(3, "destroyed " + NAMESPACE + " (reset: " + (reset ? "true" : "false") + ")"); return null; }; @@ -1869,6 +2178,8 @@

    Source: jquery.scrollmagic.js

    * Fires whenever the scroll position its the starting point of the scene. * It will also fire when scrolling back up going over the start position of the scene. If you want something to happen only when scrolling down/right, use the scrollDirection parameter passed to the callback. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.start * * @example @@ -1888,6 +2199,8 @@

    Source: jquery.scrollmagic.js

    * Fires whenever the scroll position its the ending point of the scene. * It will also fire when scrolling back up from after the scene and going over its end position. If you want something to happen only when scrolling down/right, use the scrollDirection parameter passed to the callback. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.end * * @example @@ -1907,6 +2220,8 @@

    Source: jquery.scrollmagic.js

    * Fires whenever the scene enters the "DURING" state. * Keep in mind that it doesn't matter if the scene plays forward or backward: This event always fires when the scene enters its active scroll timeframe, regardless of the scroll-direction. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.enter * * @example @@ -1926,6 +2241,8 @@

    Source: jquery.scrollmagic.js

    * Fires whenever the scene's state goes from "DURING" to either "BEFORE" or "AFTER". * Keep in mind that it doesn't matter if the scene plays forward or backward: This event always fires when the scene leaves its active scroll timeframe, regardless of the scroll-direction. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.leave * * @example @@ -1962,6 +2279,8 @@

    Source: jquery.scrollmagic.js

    * Scene progress event. * Fires whenever the progress of the scene changes. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.progress * * @example @@ -1993,6 +2312,51 @@

    Source: jquery.scrollmagic.js

    * @property {string} event.what - Indicates what value has been changed * @property {mixed} event.newval - The new value of the changed property */ + /** + * Scene shift event. + * Fires whenvever the start or end **scroll offset** of the scene change. + * This happens explicitely, when one of these values change: `offset`, `duration` or `triggerHook`. + * It will fire implicitly when the `triggerElement` changes, if the new element has a different position (most cases). + * It will also fire implicitly when the size of the container changes and the triggerHook is anything other than `onLeave`. + * + * @event ScrollScene.shift + * @since 1.1.0 + * + * @example + * scene.on("shift", function (event) { + * console.log("Scene moved, because the " + event.reason + " has changed.)"); + * }); + * + * @property {object} event - The event Object passed to each callback + * @property {string} event.type - The name of the event + * @property {ScrollScene} event.target - The ScrollScene object that triggered this event + * @property {string} event.reason - Indicates why the scene has shifted + */ + /** + * Scene destroy event. + * Fires whenvever the scene is destroyed. + * This can be used to tidy up custom behaviour used in events. + * + * @event ScrollScene.destroy + * @since 1.1.0 + * + * @example + * scene.on("enter", function (event) { + * // add custom action + * $("#my-elem").left("200"); + * }) + * .on("destroy", function (event) { + * // reset my element to start position + * if (event.reset) { + * $("#my-elem").left("0"); + * } + * }); + * + * @property {object} event - The event Object passed to each callback + * @property {string} event.type - The name of the event + * @property {ScrollScene} event.target - The ScrollScene object that triggered this event + * @property {boolean} event.reset - Indicates if the destroy method was called with reset `true` or `false`. + */ /** * Add one ore more event listener. @@ -2014,7 +2378,7 @@

    Source: jquery.scrollmagic.js

    if ($.isFunction(callback)) { var names = $.trim(name).toLowerCase() .replace(/(\w+)\.(\w+)/g, '$1.' + NAMESPACE + '_$2') // add custom namespace, if one is defined - .replace(/( |^)(\w+)( |$)/g, '$1$2.' + NAMESPACE + '$3'); // add namespace to regulars. + .replace(/( |^)(\w+)(?= |$)/g, '$1$2.' + NAMESPACE ); // add namespace to regulars. $(ScrollScene).on(names, callback); } else { log(1, "ERROR calling method 'on()': Supplied argument is not a valid callback!"); @@ -2042,7 +2406,7 @@

    Source: jquery.scrollmagic.js

    this.off = function (name, callback) { var names = $.trim(name).toLowerCase() .replace(/(\w+)\.(\w+)/g, '$1.' + NAMESPACE + '_$2') // add custom namespace, if one is defined - .replace(/( |^)(\w+)( |$)/g, '$1$2.' + NAMESPACE + '$3'); // add namespace to regulars. + .replace(/( |^)(\w+)(?= |$)/g, '$1$2.' + NAMESPACE + '$3'); // add namespace to regulars. $(ScrollScene).off(names, callback); return ScrollScene; }; @@ -2060,14 +2424,7 @@

    Source: jquery.scrollmagic.js

    */ this.trigger = function (name, vars) { log(3, 'event fired:', name, "->", vars); - var event = { - type: $.trim(name).toLowerCase(), - target: ScrollScene - }; - if ($.isPlainObject(vars)) { - event = $.extend({}, vars, event); - } - // fire all callbacks of the event + var event = $.Event($.trim(name).toLowerCase(), vars); $(ScrollScene).trigger(event); return ScrollScene; }; @@ -2077,6 +2434,8 @@

    Source: jquery.scrollmagic.js

    return ScrollScene; }; + // store version + ScrollMagic.version = "1.1.0-alpha"; // make global references available window.ScrollScene = ScrollScene; window.ScrollMagic = ScrollMagic; @@ -2086,6 +2445,7 @@

    Source: jquery.scrollmagic.js

    * global logging functions and making sure no console errors occur * ---------------------------------------------------------------- */ + var console = (window.console = window.console || {}), loglevels = [ @@ -2139,6 +2499,15 @@

    Source: jquery.scrollmagic.js

    } return offset; }; + var isDomElement = function (o){ + return ( + typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 + o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string" + ); + }; + var isMarginCollapseType = function (str) { + return ["block", "flex", "list-item", "table", "-webkit-box"].indexOf(str) > -1; + }; })(jQuery, window);
    @@ -2186,8 +2555,8 @@

    Source: jquery.scrollmagic.js

    diff --git a/docs/styles/site.cosmo.css b/docs/styles/site.cosmo.css index ec5ed957..e4d44d2c 100644 --- a/docs/styles/site.cosmo.css +++ b/docs/styles/site.cosmo.css @@ -5952,16 +5952,19 @@ code { .member-collapsed .type-signature a { color: white; } -.tag-source { +.tag-source, .tag-since { font-size: 11px; font-weight: normal; display: inline-block; margin-left: 0; } -.tag-source ul { +.tag-source ul, .tag-since ul { list-style: none; margin-left: 0; } +.tag-since ul { + font-weight: bold; +} dl.details { margin-bottom: 10px; } diff --git a/js/jquery.scrollmagic.js b/js/jquery.scrollmagic.js index f8b6c590..3f191eb8 100644 --- a/js/jquery.scrollmagic.js +++ b/js/jquery.scrollmagic.js @@ -16,17 +16,9 @@ Greensock License info at http://www.greensock.com/licensing/ @license Dual licensed under MIT license and GPL. @author Jan Paepke - e-mail@janpaepke.de -- endTrigger -@todo: document event fire order (enter, start/end, progress, start/end, leave) -@todo: issues with min width on first page load (check main demo page) -@todo: feature: have different tweens, when scrolling up, than when scrolling down @todo: bug: when cascading pins (pinning one element multiple times) and later removing them without reset, positioning errors occur. @todo: bug: having multiple scroll directions with cascaded pins doesn't work (one scroll vertical, one horizontal) -@todo: bug: pin positioning problems with centered pins in IE9 (i.e. in examples) -@toto: improvement: check if its possible to take the recalculation of the start point out of the scene update, while still making sure it is always up to date (performance) @todo: feature: optimize performance on debug plugin (huge drawbacks, when using many scenes) -@todo: feature: consider public method to trigger pinspacerresize (in case size changes during pin) -@todo: feature: make pins work with -webkit-transform of parent for mobile applications. Might be possible by temporarily removing the pin element from its container and attaching it to the body during pin. Reverting might be difficult though (cascaded pins). */ (function($, window) { @@ -54,7 +46,7 @@ Greensock License info at http://www.greensock.com/licensing/ ** `1` => errors ** `2` => errors, warnings ** `3` => errors, warnings, debuginfo - * @param {boolean} [options.triggerPosUpdateInterval=100] - Interval in which the position of the trigger elements of the scenes are updated. If you don't use trigger elements or have static layouts, where the positions of the trigger elements don't change, you can set this to 0 disable interval checking and improve performance. + * @param {boolean} [options._sceneRefreshInterval=100] - Interval in which the position of the trigger elements of the scenes are updated. If you don't use trigger elements or have static layouts, where the positions of the trigger elements don't change, you can set this to 0 disable interval checking and improve performance. * */ var ScrollMagic = function(options) { @@ -71,7 +63,7 @@ Greensock License info at http://www.greensock.com/licensing/ vertical: true, globalSceneOptions: {}, loglevel: 2, - triggerPosUpdateInterval: 100 + sceneRefreshInterval: 100 }; /* @@ -91,7 +83,7 @@ Greensock License info at http://www.greensock.com/licensing/ _viewPortSize = 0, _tickerUsed = false, _enabled = true, - _triggerPosUpdateInterval; + _sceneRefreshInterval; /* * ---------------------------------------------------------------- @@ -129,13 +121,13 @@ Greensock License info at http://www.greensock.com/licensing/ _tickerUsed = false; } - _options.triggerPosUpdateInterval = parseInt(_options.triggerPosUpdateInterval); - if (_options.triggerPosUpdateInterval > 0) { - window.setInterval(function () { + _options.sceneRefreshInterval = parseInt(_options.sceneRefreshInterval); + if (_options.sceneRefreshInterval > 0) { + _sceneRefreshInterval = window.setInterval(function () { $.each(_sceneObjects, function (index, scene) { - scene.updateTriggerElementPosition(); + scene.refresh(); }); - }, _options.triggerPosUpdateInterval); + }, _options.sceneRefreshInterval); } log(3, "added new " + NAMESPACE + " controller"); @@ -259,29 +251,31 @@ Greensock License info at http://www.greensock.com/licensing/ * @param {(ScrollScene|array)} ScrollScene - ScrollScene or Array of ScrollScenes to be added to the controller. * @return {ScrollMagic} Parent object for chaining. */ - this.addScene = function (ScrollScene) { - if ($.isArray(ScrollScene)) { - $.each(ScrollScene, function (index, scene) { + this.addScene = function (newScene) { + if ($.isArray(newScene)) { + $.each(newScene, function (index, scene) { ScrollMagic.addScene(scene); }); - } else { - if (ScrollScene.parent() != ScrollMagic) { - ScrollScene.addTo(ScrollMagic); - } else if ($.inArray(_sceneObjects, ScrollScene) == -1){ + } else if (newScene instanceof ScrollScene) { + if (newScene.parent() != ScrollMagic) { + newScene.addTo(ScrollMagic); + } else if ($.inArray(_sceneObjects, newScene) == -1){ // new scene - _sceneObjects.push(ScrollScene); // add to array + _sceneObjects.push(newScene); // add to array _sceneObjects = sortScenes(_sceneObjects); // sort - ScrollScene.on("shift." + NAMESPACE + "_sort", function() { // resort whenever scene moves + newScene.on("shift." + NAMESPACE + "_sort", function() { // resort whenever scene moves _sceneObjects = sortScenes(_sceneObjects); }); // insert Global defaults. $.each(_options.globalSceneOptions, function (key, value) { - if (ScrollScene[key]) { - ScrollScene[key].call(ScrollScene, value); + if (newScene[key]) { + newScene[key].call(newScene, value); } }); log(3, "added Scene (" + _sceneObjects.length + " total)"); } + } else { + log(1, "ERROR: invalid argument supplied for '.addScene()'"); } return ScrollMagic; }; @@ -322,7 +316,7 @@ Greensock License info at http://www.greensock.com/licensing/ * This is the equivalent to `ScrollScene.update()`. * The update method calculates the scene's start and end position (based on the trigger element, trigger hook, duration and offset) and checks it against the current scroll position of the container. * It then updates the current scene state accordingly (or does nothing, if the state is already correct) – Pins will be set to their correct position and tweens will be updated to their correct progress. - * *Note:* This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters. + * _**Note:** This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters._ * @public * @example * // update a specific scene on next tick @@ -392,7 +386,7 @@ Greensock License info at http://www.greensock.com/licensing/ * For vertical controllers it will change the top scroll offset and for horizontal applications it will change the left offset. * @public * - * @since 1.10.0 + * @since 1.1.0 * @example * // scroll to an offset of 100 * controller.scrollTo(100); @@ -427,14 +421,13 @@ Greensock License info at http://www.greensock.com/licensing/ log (2, "scrollTo(): The supplied scene does not belong to this controller. Scroll cancelled.", scrollTarget); } } else if ($.type(scrollTarget) === "string" || isDomElement(scrollTarget) || scrollTarget instanceof $) { - console.log(scrollTarget); var $elm = $(scrollTarget).first(); if ($elm[0]) { var offset = $elm.offset(); ScrollMagic.scrollTo(_options.vertical ? offset.top : offset.left); } else { - log (2, "scrollTo(): The supplied element could not be found. Scroll cancelled.", newScrollPos); + log (2, "scrollTo(): The supplied element could not be found. Scroll cancelled.", scrollTarget); } } else if ($.isFunction(scrollTarget)) { setScrollPos = scrollTarget; @@ -588,10 +581,10 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {null} Null to unset handler variables. */ this.destroy = function (resetScenes) { - window.clearTimeout(_triggerPosUpdateInterval); - while (_sceneObjects.length > 0) { - var scene = _sceneObjects[_sceneObjects.length - 1]; - scene.destroy(resetScenes); + window.clearTimeout(_sceneRefreshInterval); + var i = _sceneObjects.length; + while (i--) { + _sceneObjects[i].destroy(resetScenes); } _options.container.off("scroll resize", onChange); if (_tickerUsed) { @@ -630,8 +623,9 @@ Greensock License info at http://www.greensock.com/licensing/ * @param {object} [options] - Options for the Scene. The options can be updated at any time. Instead of setting the options for each scene individually you can also set them globally in the controller as the controllers `globalSceneOptions` option. The object accepts the same properties as the ones below. When a scene is added to the controller the options defined using the ScrollScene constructor will be overwritten by those set in `globalSceneOptions`. - * @param {number} [options.duration=0] - The duration of the scene. - If `0` tweens will auto-play when reaching the scene start point, pins will be pinned indefinetly starting at the start position. + * @param {(number|function)} [options.duration=0] - The duration of the scene. + If `0` tweens will auto-play when reaching the scene start point, pins will be pinned indefinetly starting at the start position. + A function retuning the duration value is also supported. Please see `ScrollScene.duration()` for details. * @param {number} [options.offset=0] - Offset Value for the Trigger Position. If no triggerElement is defined this will be the scroll distance from the start of the page, after which the scene will start. * @param {(string|object)} [options.triggerElement=null] - Selector, DOM object or jQuery Object that defines the start of the scene. If undefined the scene will start right at the start of the page (unless an offset is set). * @param {(number|string)} [options.triggerHook="onCenter"] - Can be a number between 0 and 1 defining the position of the trigger Hook in relation to the viewport. @@ -658,13 +652,13 @@ Greensock License info at http://www.greensock.com/licensing/ */ var - TRIGGER_HOOK_STRINGS = ["onCenter", "onEnter", "onLeave"], + TRIGGER_HOOK_VALUES = {"onCenter" : 0.5, "onEnter" : 1, "onLeave" : 0}, NAMESPACE = "ScrollScene", DEFAULT_OPTIONS = { duration: 0, offset: 0, triggerElement: null, - triggerHook: TRIGGER_HOOK_STRINGS[0], + triggerHook: "onCenter", reverse: true, tweenChanges: false, loglevel: 2 @@ -684,6 +678,7 @@ Greensock License info at http://www.greensock.com/licensing/ _scrollOffset = {start: 0, end: 0}, // reflects the parent's scroll position for the start and end of the scene respectively _triggerPos = 0, _enabled = true, + _durationUpdateMethod, _parent, _tween, _pin, @@ -702,10 +697,21 @@ Greensock License info at http://www.greensock.com/licensing/ }); }, "duration" : function () { - _options.duration = parseFloat(_options.duration); - if (!$.isNumeric(_options.duration) || _options.duration < 0) { - log(1, "ERROR: Invalid value for option \"duration\":", _options.duration); - _options.duration = DEFAULT_OPTIONS.duration; + if ($.isFunction(_options.duration)) { + _durationUpdateMethod = _options.duration; + try { + _options.duration = parseFloat(_durationUpdateMethod()); + } catch (e) { + log(1, "ERROR: Invalid return value of supplied function for option \"duration\":", _options.duration); + _durationUpdateMethod = undefined; + _options.duration = DEFAULT_OPTIONS.duration; + } + } else { + _options.duration = parseFloat(_options.duration); + if (!$.isNumeric(_options.duration) || _options.duration < 0) { + log(1, "ERROR: Invalid value for option \"duration\":", _options.duration); + _options.duration = DEFAULT_OPTIONS.duration; + } } }, "offset" : function () { @@ -722,21 +728,32 @@ Greensock License info at http://www.greensock.com/licensing/ } }, "triggerHook" : function () { - if (!$.isNumeric(_options.triggerHook) && $.inArray(_options.triggerHook, TRIGGER_HOOK_STRINGS) == -1) { - log(1, "ERROR: Invalid value for option \"triggerHook\": ", _options.triggerHook); - _options.triggerHook = DEFAULT_OPTIONS.triggerHook; + if (!(_options.triggerHook in TRIGGER_HOOK_VALUES)) { + if ($.isNumeric(_options.triggerHook)) { + _options.triggerHook = parseFloat(_options.triggerHook); + } else { + log(1, "ERROR: Invalid value for option \"triggerHook\": ", _options.triggerHook); + _options.triggerHook = DEFAULT_OPTIONS.triggerHook; + } } }, + "reverse" : function () { + _options.reverse = !!_options.reverse; // force boolean + }, + "tweenChanges" : function () { + _options.tweenChanges = !!_options.tweenChanges; // force boolean + }, "loglevel" : function () { + _options.loglevel = parseInt(_options.loglevel); if (!$.isNumeric(_options.loglevel) || _options.loglevel < 0 || _options.loglevel > 3) { var wrongval = _options.loglevel; _options.loglevel = DEFAULT_OPTIONS.loglevel; log(1, "ERROR: Invalid value for option \"loglevel\":", wrongval); } }, - "checkIfPinnedElementIsTweened" : function () { + "checkIfTriggerElementIsTweened" : function () { + // check if there are position tweens defined for the trigger and warn about it :) if (_tween && _parent && _options.triggerElement && _options.loglevel >= 2) {// parent is needed to know scroll direction. - // check if there are position tweens defined for the trigger and warn about it :) var triggerTweens = _tween.getTweensOf($(_options.triggerElement)), vertical = _parent.info("vertical"); @@ -764,29 +781,33 @@ Greensock License info at http://www.greensock.com/licensing/ * @private */ var construct = function () { - checkOptionsValidity(); + validateOption(); - // internal event listeners + // event listeners ScrollScene .on("change.internal", function (e) { if (e.what !== "loglevel" && e.what !== "tweenChanges") { // no need for a scene update scene with these options... if (e.what === "triggerElement") { - ScrollScene.updateTriggerElementPosition(); - } else if (e.what === "reverse") { // the only property left that may have an impect on the current scene state. Everything else is handled in the shift event. + updateTriggerElementPosition(); + } else if (e.what === "reverse") { // the only property left that may have an impact on the current scene state. Everything else is handled by the shift event. ScrollScene.update(); } - if ((_state !== "DURING" && e.what == "duration") || (_state === "AFTER" && _options.duration === 0)) { // if duration changed outside of scene (inside scene progress updates pin position) or duration is 0, we are beyond trigger and some other value changed. - updatePinState(); - } } }) .on("shift.internal", function (e) { updateScrollOffset(); ScrollScene.update(); // update scene to reflect new position + if ((_state === "AFTER" && e.reason === "duration") || (_state === 'DURING' && _options.duration === 0)) { + // if [duration changed after a scene (inside scene progress updates pin position)] or [duration is 0, we are in pin phase and some other value changed]. + updatePinState(); + } }) .on("progress.internal", function (e) { updateTweenProgress(); updatePinState(); + }) + .on("destroy", function (e) { + e.preventDefault(); // otherwise jQuery would call target.destroy() by default. }); }; @@ -812,7 +833,7 @@ Greensock License info at http://www.greensock.com/licensing/ * Checks the validity of a specific or all options and reset to default if neccessary. * @private */ - var checkOptionsValidity = function (check) { + var validateOption = function (check) { if (!arguments.length) { check = []; for (var key in _validate){ @@ -828,6 +849,22 @@ Greensock License info at http://www.greensock.com/licensing/ }); }; + /** + * Helper used by the setter/getters for scene options + * @private + */ + var changeOption = function(varname, newval) { + var + changed = false, + oldval = _options[varname]; + if (_options[varname] != newval) { + _options[varname] = newval; + validateOption(varname); // resets to default if necessary + changed = oldval != _options[varname]; + } + return changed; + }; + /** * Update the start and end scrollOffset of the container. * The positions reflect what the parent's scroll position will be at the start and end respectively. @@ -847,6 +884,66 @@ Greensock License info at http://www.greensock.com/licensing/ _scrollOffset.end = _scrollOffset.start + _options.duration; }; + /** + * Updates the duration if set to a dynamic function. + * This method is called when the scene is added to a controller and in regular intervals from the controller through scene.refresh(). + * + * @fires {@link ScrollScene.change}, if the duration changed + * @fires {@link ScrollScene.shift}, if the duration changed + * + * @param {boolean} [suppressEvents=false] - If true the shift event will be suppressed. + */ + var updateDuration = function (suppressEvents) { + // update duration + if (_durationUpdateMethod) { + var varname = "duration"; + if (changeOption(varname, _durationUpdateMethod()) && !suppressEvents) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); + ScrollScene.trigger("shift", {reason: varname}); + } + } + }; + + /** + * Updates the position of the triggerElement, if present. + * This method is called ... + * - ... when the triggerElement is changed + * - ... when the scene is added to a (new) controller + * - ... in regular intervals from the controller through scene.refresh(). + * + * @fires {@link ScrollScene.shift}, if the position changed + * + * @param {boolean} [suppressEvents=false] - If true the shift event will be suppressed. + */ + var updateTriggerElementPosition = function (suppressEvents) { + var elementPos = 0; + if (_parent && _options.triggerElement) { + var + element = $(_options.triggerElement).first(), + controllerInfo = _parent.info(), + containerOffset = getOffset(controllerInfo.container), // container position is needed because element offset is returned in relation to document, not in relation to container. + param = controllerInfo.vertical ? "top" : "left"; // which param is of interest ? + + // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. + while (element.parent().data("ScrollMagicPinSpacer")) { + element = element.parent(); + } + + var elementOffset = getOffset(element); + + if (!controllerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent + containerOffset[param] -= controllerInfo.scrollPos; + } + + elementPos = elementOffset[param] - containerOffset[param]; + } + var changed = elementPos != _triggerPos; + _triggerPos = elementPos; + if (changed && !suppressEvents) { + ScrollScene.trigger("shift", {reason: "triggerElementPosition"}); + } + }; + /** * Update the tween progress. * @private @@ -859,7 +956,7 @@ Greensock License info at http://www.greensock.com/licensing/ var progress = (to >= 0 && to <= 1) ? to : _progress; if (_tween.repeat() === -1) { // infinite loop, so not in relation to progress - if ((_state === "DURING" || (_state === "AFTER" && _options.duration === 0)) && _tween.paused()) { + if (_state === "DURING" && _tween.paused()) { _tween.play(); } else if (_state !== "DURING" && !_tween.paused()) { _tween.pause(); @@ -870,7 +967,7 @@ Greensock License info at http://www.greensock.com/licensing/ // no infinite loop - so should we just play or go to a specific point in time? if (_options.duration === 0) { // play the animation - if (_state === "AFTER") { // play from 0 to 1 + if (_state === "DURING") { // play from 0 to 1 _tween.play(); } else { // play from 1 to 0 _tween.reverse(); @@ -903,7 +1000,7 @@ Greensock License info at http://www.greensock.com/licensing/ var containerInfo = _parent.info(); - if (!forceUnpin && (_state === "DURING" || (_state === "AFTER" && _options.duration === 0))) { // during scene or if duration is 0 and we are past the trigger + if (!forceUnpin && _state === "DURING") { // during scene or if duration is 0 and we are past the trigger // pinned state if (_pin.css("position") != "fixed") { // change state before updating pin spacer (position changes due to fixed collapsing might occur.) @@ -943,7 +1040,7 @@ Greensock License info at http://www.greensock.com/licensing/ if (!_pinOptions.pushFollowers) { newCSS[containerInfo.vertical ? "top" : "left"] = _options.duration * _progress; - } else { + } else if (_options.duration > 0) { // only concerns scenes with duration if (_state === "AFTER" && parseFloat(_pinOptions.spacer.css("padding-top")) === 0) { change = true; // if in after state but havent updated spacer yet (jumped past pin) } else if (_state === "BEFORE" && parseFloat(_pinOptions.spacer.css("padding-bottom")) === 0) { // before @@ -976,7 +1073,7 @@ Greensock License info at http://www.greensock.com/licensing/ pinned = (_pin.css("position") == "fixed"), vertical = _parent.info("vertical"), $spacercontent = _pinOptions.spacer.children().first(), // usually the pined element but can also be another spacer (cascaded pins) - marginCollapse = ($.inArray(_pinOptions.spacer.css("display"), ["block", "flex", "list-item", "table", "-webkit-box"]) > -1), + marginCollapse = isMarginCollapseType(_pinOptions.spacer.css("display")), css = {}; if (marginCollapse) { @@ -988,11 +1085,11 @@ Greensock License info at http://www.greensock.com/licensing/ // set new size // if relsize: spacer -> pin | else: pin -> spacer - if (_pinOptions.relSize.width) { + if (_pinOptions.relSize.width || _pinOptions.relSize.autoFullWidth) { if (pinned) { if ($(window).width() == _pinOptions.spacer.parent().width()) { // relative to body - _pin.css("width", "inherit"); + _pin.css("width", _pinOptions.relSize.autoFullWidth ? "100%" : "inherit"); } else { // not relative to body -> need to calculate _pin.css("width", _pinOptions.spacer.width()); @@ -1001,7 +1098,9 @@ Greensock License info at http://www.greensock.com/licensing/ _pin.css("width", "100%"); } } else { - css["min-width"] = $spacercontent.outerWidth(true); // needed for cascading pins + // minwidth is needed for cascading pins. + // margin is only included if it's a cascaded pin to resolve an IE9 bug + css["min-width"] = $spacercontent.outerWidth(!$spacercontent.is(_pin)); css.width = pinned ? css["min-width"] : "auto"; } if (_pinOptions.relSize.height) { @@ -1026,7 +1125,6 @@ Greensock License info at http://www.greensock.com/licensing/ css["padding" + (vertical ? "Top" : "Left")] = _options.duration * _progress; css["padding" + (vertical ? "Bottom" : "Right")] = _options.duration * (1 - _progress); } - _pinOptions.spacer.css(css); } }; @@ -1050,10 +1148,10 @@ Greensock License info at http://www.greensock.com/licensing/ * @private */ var updateRelativePinSpacer = function () { - if ( _parent && _pin &&// well, duh - (_state === "DURING" || _state === "AFTER" && _options.duration === 0) &&// element in pinned state? - ( // is width or height relatively sized, but not in relation to body? then we need to recalc. - (_pinOptions.relSize.width && $(window).width() != _pinOptions.spacer.parent().width()) || + if ( _parent && _pin && // well, duh + _state === "DURING" && // element in pinned state? + ( // is width or height relatively sized, but not in relation to body? then we need to recalc. + ((_pinOptions.relSize.width || _pinOptions.relSize.autoFullWidth) && $(window).width() != _pinOptions.spacer.parent().width()) || (_pinOptions.relSize.height && $(window).height() != _pinOptions.spacer.parent().height()) ) ) { @@ -1061,6 +1159,17 @@ Greensock License info at http://www.greensock.com/licensing/ } }; + /** + * Is called, when the mousewhel is used while over a pinned element. + * If the scene is in fixed state scroll events used to be ignored. This forwards the event to the scroll container. + * @private + */ + var onMousewheelOverPin = function (e) { + if (_parent && _pin && _state === "DURING") { // in pin state + _parent.scrollTo(_parent.info("scrollPos") - (e.originalEvent.wheelDelta/3 || -e.originalEvent.detail*30)); + } + }; + /* * ---------------------------------------------------------------- @@ -1084,6 +1193,14 @@ Greensock License info at http://www.greensock.com/licensing/ /** * **Get** or **Set** the duration option value. + * As a setter it also accepts a function returning a numeric value. + * This is particularly useful for responsive setups. + * + * The duration is updated using the supplied function every time `ScrollScene.refresh()` is called, which happens periodically from the controller (see ScrollMagic option `sceneRefreshInterval`). + * _**NOTE:** Be aware that it's an easy way to kill performance, if you supply a function that has high CPU demand. + * Even for size and position calculations it is recommended to use a variable to cache the value. (see example) + * This counts double if you use the same function for multiple scenes._ + * * @public * @example * // get the current duration value @@ -1092,21 +1209,36 @@ Greensock License info at http://www.greensock.com/licensing/ * // set a new duration * scene.duration(300); * + * // use a function to automatically adjust the duration to the window height. + * var durationValueCache; + * function getDuration () { + * return durationValueCache; + * } + * function updateDuration (e) { + * durationValueCache = $(window).innerHeight(); + * } + * $(window).on("resize", updateDuration); // update the duration when the window size changes + * $(window).triggerHandler("resize"); // set to initial value + * scene.duration(getDuration); // supply duration method + * * @fires {@link ScrollScene.change}, when used as setter * @fires {@link ScrollScene.shift}, when used as setter - * @param {number} [newDuration] - The new duration of the scene. + * @param {(number|function)} [newDuration] - The new duration of the scene. * @returns {number} `get` - Current scene duration. * @returns {ScrollScene} `set` - Parent object for chaining. */ this.duration = function (newDuration) { + var varname = "duration"; if (!arguments.length) { // get - return _options.duration; - } else if (_options.duration != newDuration) { // set - var varname = "duration"; - _options[varname] = newDuration; - checkOptionsValidity(varname); - ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); - ScrollScene.trigger("shift", {reason: varname}); + return _options[varname]; + } else { + if (!$.isFunction(newDuration)) { + _durationUpdateMethod = undefined; + } + if (changeOption(varname, newDuration)) { // set + ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); + ScrollScene.trigger("shift", {reason: varname}); + } } return ScrollScene; }; @@ -1128,12 +1260,10 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {ScrollScene} `set` - Parent object for chaining. */ this.offset = function (newOffset) { + var varname = "offset"; if (!arguments.length) { // get - return _options.offset; - } else if (_options.offset != newOffset) { // set - var varname = "offset"; - _options[varname] = newOffset; - checkOptionsValidity(varname); + return _options[varname]; + } else if (changeOption(varname, newOffset)) { // set ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); ScrollScene.trigger("shift", {reason: varname}); } @@ -1142,7 +1272,7 @@ Greensock License info at http://www.greensock.com/licensing/ /** * **Get** or **Set** the triggerElement option value. - * Does **not** fire `ScrollScene.shift`, because changing the trigger Element doesn't necessarily mean the start position changes. This will be determined in `ScrollScene.updateTriggerElementPosition()`. + * Does **not** fire `ScrollScene.shift`, because changing the trigger Element doesn't necessarily mean the start position changes. This will be determined in `ScrollScene.refresh()`, which is automatically triggered. * @public * @example * // get the current triggerElement @@ -1161,12 +1291,10 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {ScrollScene} `set` - Parent object for chaining. */ this.triggerElement = function (newTriggerElement) { + var varname = "triggerElement"; if (!arguments.length) { // get - return _options.triggerElement; - } else if (_options.triggerElement != newTriggerElement) { // set - var varname = "triggerElement"; - _options[varname] = newTriggerElement; - checkOptionsValidity(varname); + return _options[varname]; + } else if (changeOption(varname, newTriggerElement)) { // set ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); } return ScrollScene; @@ -1191,30 +1319,10 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {ScrollScene} `set` - Parent object for chaining. */ this.triggerHook = function (newTriggerHook) { + var varname = "triggerHook"; if (!arguments.length) { // get - var triggerPoint; - if ($.isNumeric(_options.triggerHook)) { - triggerPoint = _options.triggerHook; - } else { - switch(_options.triggerHook) { - case "onCenter": - triggerPoint = 0.5; - break; - case "onLeave": - triggerPoint = 0; - break; - case "onEnter": - /* falls through */ - default: - triggerPoint = 1; - break; - } - } - return triggerPoint; - } else if (_options.triggerHook != newTriggerHook) { // set - var varname = "triggerHook"; - _options[varname] = newTriggerHook; - checkOptionsValidity(varname); + return $.isNumeric(_options[varname]) ? _options[varname] : TRIGGER_HOOK_VALUES[_options[varname]]; + } else if (changeOption(varname, newTriggerHook)) { // set ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); ScrollScene.trigger("shift", {reason: varname}); } @@ -1237,12 +1345,10 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {ScrollScene} `set` - Parent object for chaining. */ this.reverse = function (newReverse) { + var varname = "reverse"; if (!arguments.length) { // get - return _options.reverse; - } else if (_options.reverse != newReverse) { // set - var varname = "reverse"; - _options[varname] = !!newReverse; - checkOptionsValidity(varname); + return _options[varname]; + } else if (changeOption(varname, newReverse)) { // set ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); } return ScrollScene; @@ -1264,12 +1370,10 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {ScrollScene} `set` - Parent object for chaining. */ this.tweenChanges = function (newTweenChanges) { + var varname = "tweenChanges"; if (!arguments.length) { // get - return _options.tweenChanges; - } else if (_options.tweenChanges != newTweenChanges) { // set - var varname = "tweenChanges"; - _options[varname] = !!newTweenChanges; - checkOptionsValidity(varname); + return _options[varname]; + } else if (changeOption(varname, newTweenChanges)) { // set ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); } return ScrollScene; @@ -1291,12 +1395,10 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {ScrollScene} `set` - Parent object for chaining. */ this.loglevel = function (newLoglevel) { + var varname = "loglevel"; if (!arguments.length) { // get - return _options.loglevel; - } else if (_options.loglevel != newLoglevel) { // set - var varname = "loglevel"; - _options[varname] = newLoglevel; - checkOptionsValidity(varname); + return _options[varname]; + } else if (changeOption(varname, newLoglevel)) { // set ScrollScene.trigger("change", {what: varname, newval: _options[varname]}); } return ScrollScene; @@ -1342,7 +1444,7 @@ Greensock License info at http://www.greensock.com/licensing/ /** * **Get** the trigger offset of the scene (including the value of the `offset` option). * @public - * @deprecated Method is deprecated since 1.10.0. You should now use {@link ScrollScene.triggerPosition} + * @deprecated Method is deprecated since 1.1.0. You should now use {@link ScrollScene.triggerPosition} */ this.triggerOffset = function () { return ScrollScene.triggerPosition(); @@ -1375,8 +1477,9 @@ Greensock License info at http://www.greensock.com/licensing/ * Updates the Scene in the parent Controller to reflect the current state. * This is the equivalent to `ScrollMagic.updateScene(scene, immediately)`. * The update method calculates the scene's start and end position (based on the trigger element, trigger hook, duration and offset) and checks it against the current scroll position of the container. - * It then updates the current scene state accordingly (or does nothing, if the state is already correct) – Pins will be set to their correct position and tweens will be updated to their correct progress. - * *Note:* This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters. + * It then updates the current scene state accordingly (or does nothing, if the state is already correct) – Pins will be set to their correct position and tweens will be updated to their correct progress. + * This means an update doesn't necessarily result in a progress change. The `progress` event will be fired if the progress has indeed changed between this update and the last. + * _**NOTE:** This method gets called constantly whenever ScrollMagic detects a change. The only application for you is if you change something outside of the realm of ScrollMagic, like moving the trigger or changing tween parameters._ * @public * @example * // update the scene on next tick @@ -1418,61 +1521,62 @@ Greensock License info at http://www.greensock.com/licensing/ }; /** - * Updates the position of the triggerElement, if present. - * This method is automatically called ... - * - ... when the triggerElement is changed - * - ... when the scene is added to a (new) controller - * - ... in regular intervals from the controller. See {@link ScrollMagic} option `triggerPosUpdateInterval`. + * Updates dynamic scene variables like the trigger element position or the duration. + * This method is automatically called in regular intervals from the controller. See {@link ScrollMagic} option `sceneRefreshInterval`. * - * You can call it to minimize lag, when you intentionally change the position of the triggerElement. + * You can call it to minimize lag, for example when you intentionally change the position of the triggerElement. + * If you don't it will simply be updated in the next refresh interval of the container, which is usually sufficient. + * * @public - * @since v1.1.0 + * @since 1.1.0 * @example * scene = new ScrollScene({triggerElement: "#trigger"}); * * // change the position of the trigger * $("#trigger").css("top", 500); * // immediately let the scene know of this change - * scene.updateTriggerElementPosition(); + * scene.refresh(); * - * @fires {@link ScrollScene.shift}, if the position changed and suppressEvents is false + * @fires {@link ScrollScene.shift}, if the trigger element position or the duration changed + * @fires {@link ScrollScene.change}, if the duration changed * * @returns {ScrollScene} Parent object for chaining. */ - // @param {boolean} [suppressEvents=false] - If true the shift event will be suppressed. no doc export... - this.updateTriggerElementPosition = function (suppressEvents) { - var elementPos = 0; - if (_parent && _options.triggerElement) { - var - element = $(_options.triggerElement).first(), - controllerInfo = _parent.info(), - containerOffset = getOffset(controllerInfo.container), // container position is needed because element offset is returned in relation to document, not in relation to container. - param = controllerInfo.vertical ? "top" : "left"; // which param is of interest ? - - // if parent is spacer, use spacer position instead so correct start position is returned for pinned elements. - while (element.parent().data("ScrollMagicPinSpacer")) { - element = element.parent(); - } - - var elementOffset = getOffset(element); - - if (!controllerInfo.isDocument) { // container is not the document root, so substract scroll Position to get correct trigger element position relative to scrollcontent - containerOffset[param] -= controllerInfo.scrollPos; - } - - elementPos = elementOffset[param] - containerOffset[param]; - } - var changed = elementPos != _triggerPos; - _triggerPos = elementPos; - if (changed && !suppressEvents) { - ScrollScene.trigger("shift", {reason: "triggerElementPosition"}); - } + this.refresh = function () { + updateDuration(); + updateTriggerElementPosition(); + // update trigger element position return ScrollScene; }; /** * **Get** or **Set** the scene's progress. - * Usually it shouldn't be necessary to use this as a setter, as it is set automatically by scene.update(). + * Usually it shouldn't be necessary to use this as a setter, as it is set automatically by scene.update(). + * The order in which the events are fired depends on the duration of the scene: + * 1. Scenes with `duration == 0`: + * Scenes that have no duration by definition have no ending. Thus the `end` event will never be fired. + * When the trigger position of the scene is passed the events are always fired in this order: + * `enter`, `start`, `progress` when scrolling forward + * and + * `progress`, `start`, `leave` when scrolling in reverse + * 2. Scenes with `duration > 0`: + * Scenes with a set duration have a defined start and end point. + * When scrolling past the start position of the scene it will fire these events in this order: + * `enter`, `start`, `progress` + * When continuing to scroll and passing the end point it will fire these events: + * `progress`, `end`, `leave` + * When reversing through the end point these events are fired: + * `enter`, `end`, `progress` + * And when continuing to scroll past the start position in reverse it will fire: + * `progress`, `start`, `leave` + * In between start and end the `progress` event will be called constantly, whenever the progress changes. + * + * In short: + * `enter` events will always trigger **before** the progress update and `leave` envents will trigger **after** the progress update. + * `start` and `end` will always trigger at their respective position. + * + * Please review the event descriptions for details on the events and the event object that is passed to the callback. + * * @public * @example * // get the current scene progress @@ -1498,22 +1602,31 @@ Greensock License info at http://www.greensock.com/licensing/ var doUpdate = false, oldState = _state, - scrollDirection = _parent ? _parent.info("scrollDirection") : "PAUSED"; - if (progress <= 0 && _state !== 'BEFORE' && (progress >= _progress || _options.reverse)) { - // go back to initial state - _progress = 0; - _state = 'BEFORE'; - doUpdate = true; - } else if (progress > 0 && progress < 1 && (progress >= _progress || _options.reverse)) { - _progress = progress; - _state = 'DURING'; - doUpdate = true; - } else if (progress >= 1 && _state !== 'AFTER') { - _progress = 1; - _state = 'AFTER'; - doUpdate = true; - } else if (_state === "DURING" && !_options.reverse) { - updatePinState(); // in case we scrolled back and reverse is disabled => update the pin position, so it doesn't scroll back as well. + scrollDirection = _parent ? _parent.info("scrollDirection") : 'PAUSED', + reverseOrForward = _options.reverse || progress >= _progress; + if (_options.duration === 0) { + // zero duration scenes + doUpdate = _progress != progress; + _progress = progress < 1 && reverseOrForward ? 0 : 1; + _state = _progress === 0 ? 'BEFORE' : 'DURING'; + } else { + // scenes with start and end + if (progress <= 0 && _state !== 'BEFORE' && reverseOrForward) { + // go back to initial state + _progress = 0; + _state = 'BEFORE'; + doUpdate = true; + } else if (progress > 0 && progress < 1 && reverseOrForward) { + _progress = progress; + _state = 'DURING'; + doUpdate = true; + } else if (progress >= 1 && _state !== 'AFTER') { + _progress = 1; + _state = 'AFTER'; + doUpdate = true; + } else if (_state === 'DURING' && !reverseOrForward) { + updatePinState(); // in case we scrolled backwards mid-scene and reverse is disabled => update the pin position, so it doesn't move back as well. + } } if (doUpdate) { // fire events @@ -1526,30 +1639,16 @@ Greensock License info at http://www.greensock.com/licensing/ }; if (stateChanged) { // enter events - if (_options.duration === 0) { - if (_state === 'AFTER') { - trigger("enter"); - trigger("start"); - } - } else { - if (oldState !== 'DURING') { - trigger("enter"); - trigger(oldState === 'BEFORE' ? "start" : "end"); - } + if (oldState !== 'DURING') { + trigger("enter"); + trigger(oldState === 'BEFORE' ? "start" : "end"); } } trigger("progress"); if (stateChanged) { // leave events - if (_options.duration === 0) { - if (_state === 'BEFORE') { - trigger("start"); - trigger("leave"); - } - } else { - if (_state !== 'DURING') { - trigger(_state === 'AFTER' ? "end" : "start"); - trigger("leave"); - } + if (_state !== 'DURING') { + trigger(_state === 'BEFORE' ? "start" : "end"); + trigger("leave"); } } } @@ -1598,7 +1697,7 @@ Greensock License info at http://www.greensock.com/licensing/ _tween.yoyo(TweenMaxObject.yoyo()); } } - checkOptionsValidity("checkIfPinnedElementIsTweened"); + validateOption("checkIfTriggerElementIsTweened"); log(3, "added tween"); updateTweenProgress(); return ScrollScene; @@ -1689,6 +1788,14 @@ Greensock License info at http://www.greensock.com/licensing/ sizeCSS = _pin.css(["width", "height"]); _pin.parent().show(); // hack end. + if (sizeCSS.width === "0px" && inFlow && isMarginCollapseType(pinCSS.display)) { + // log (2, "WARNING: Your pinned element probably needs a defined width or it might collapse during pin."); + } + if (!inFlow && settings.pushFollowers) { + log(2, "WARNING: If the pinned element is positioned absolutely pushFollowers is disabled."); + settings.pushFollowers = false; + } + // create spacer var spacer = $("
    ") .addClass(settings.spacerClass) @@ -1703,22 +1810,19 @@ Greensock License info at http://www.greensock.com/licensing/ "-webkit-box-sizing": "content-box" }); - if (!inFlow && settings.pushFollowers) { - log(2, "WARNING: If the pinned element is positioned absolutely pushFollowers is disabled."); - settings.pushFollowers = false; - } - // set the pin Options var pinInlineCSS = _pin[0].style; _pinOptions = { spacer: spacer, relSize: { // save if size is defined using % values. if so, handle spacer resize differently... width: sizeCSS.width.slice(-1) === "%", - height: sizeCSS.height.slice(-1) === "%" + height: sizeCSS.height.slice(-1) === "%", + autoFullWidth: sizeCSS.width === "0px" && inFlow && isMarginCollapseType(pinCSS.display) }, pushFollowers: settings.pushFollowers, inFlow: inFlow, // stores if the element takes up space in the document flow origStyle: { + width: pinInlineCSS.width || "", position: pinInlineCSS.position || "", top: pinInlineCSS.position || "", left: pinInlineCSS.position || "", @@ -1750,6 +1854,8 @@ Greensock License info at http://www.greensock.com/licensing/ // add listener to document to update pin position in case controller is not the document. $(window).on("scroll." + NAMESPACE + "_pin resize." + NAMESPACE + "_pin", updatePinInContainer); + // add mousewheel listener to catch scrolls over fixed elements + _pin.on("mousewheel DOMMouseScroll", onMousewheelOverPin); log(3, "added pin"); @@ -1784,6 +1890,7 @@ Greensock License info at http://www.greensock.com/licensing/ } } $(window).off("scroll." + NAMESPACE + "_pin resize." + NAMESPACE + "_pin"); + _pin.off("mousewheel DOMMouseScroll", onMousewheelOverPin); _pin = undefined; log(3, "removed pin (reset: " + (reset ? "true" : "false") + ")"); } @@ -1835,7 +1942,7 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {ScrollScene} Parent object for chaining. */ this.removeClassToggle = function (reset) { - if (reset) { + if (_cssClassElm && reset) { _cssClassElm.removeClass(_cssClasses); } ScrollScene.off("start.internal_class end.internal_class"); @@ -1862,8 +1969,9 @@ Greensock License info at http://www.greensock.com/licensing/ _parent.removeScene(ScrollScene); } _parent = controller; - checkOptionsValidity(); - ScrollScene.updateTriggerElementPosition(true); + validateOption(); + updateDuration(true); + updateTriggerElementPosition(true); updateScrollOffset(); updatePinSpacerSize(); _parent.info("container").on("resize." + NAMESPACE, function () { @@ -1941,11 +2049,12 @@ Greensock License info at http://www.greensock.com/licensing/ * @returns {null} Null to unset handler variables. */ this.destroy = function (reset) { - this.removeTween(reset); - this.removePin(reset); - this.removeClassToggle(reset); - this.remove(); - this.off("start end enter leave progress change update shift shift.internal change.internal progress.internal"); + ScrollScene.removeTween(reset); + ScrollScene.removePin(reset); + ScrollScene.removeClassToggle(reset); + ScrollScene.trigger("destroy", {reset: reset}); + ScrollScene.remove(); + ScrollScene.off("start end enter leave progress change update shift destroy shift.internal change.internal progress.internal"); log(3, "destroyed " + NAMESPACE + " (reset: " + (reset ? "true" : "false") + ")"); return null; }; @@ -1961,6 +2070,8 @@ Greensock License info at http://www.greensock.com/licensing/ * Fires whenever the scroll position its the starting point of the scene. * It will also fire when scrolling back up going over the start position of the scene. If you want something to happen only when scrolling down/right, use the scrollDirection parameter passed to the callback. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.start * * @example @@ -1980,6 +2091,8 @@ Greensock License info at http://www.greensock.com/licensing/ * Fires whenever the scroll position its the ending point of the scene. * It will also fire when scrolling back up from after the scene and going over its end position. If you want something to happen only when scrolling down/right, use the scrollDirection parameter passed to the callback. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.end * * @example @@ -1999,6 +2112,8 @@ Greensock License info at http://www.greensock.com/licensing/ * Fires whenever the scene enters the "DURING" state. * Keep in mind that it doesn't matter if the scene plays forward or backward: This event always fires when the scene enters its active scroll timeframe, regardless of the scroll-direction. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.enter * * @example @@ -2018,6 +2133,8 @@ Greensock License info at http://www.greensock.com/licensing/ * Fires whenever the scene's state goes from "DURING" to either "BEFORE" or "AFTER". * Keep in mind that it doesn't matter if the scene plays forward or backward: This event always fires when the scene leaves its active scroll timeframe, regardless of the scroll-direction. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.leave * * @example @@ -2054,6 +2171,8 @@ Greensock License info at http://www.greensock.com/licensing/ * Scene progress event. * Fires whenever the progress of the scene changes. * + * For details on this event and the order in which it is fired, please review the {@link ScrollScene.progress} method. + * * @event ScrollScene.progress * * @example @@ -2093,7 +2212,7 @@ Greensock License info at http://www.greensock.com/licensing/ * It will also fire implicitly when the size of the container changes and the triggerHook is anything other than `onLeave`. * * @event ScrollScene.shift - * @since 1.10.0 + * @since 1.1.0 * * @example * scene.on("shift", function (event) { @@ -2103,7 +2222,32 @@ Greensock License info at http://www.greensock.com/licensing/ * @property {object} event - The event Object passed to each callback * @property {string} event.type - The name of the event * @property {ScrollScene} event.target - The ScrollScene object that triggered this event - * @property {ScrollScene} event.reason - Indicates why the scene has shifted + * @property {string} event.reason - Indicates why the scene has shifted + */ + /** + * Scene destroy event. + * Fires whenvever the scene is destroyed. + * This can be used to tidy up custom behaviour used in events. + * + * @event ScrollScene.destroy + * @since 1.1.0 + * + * @example + * scene.on("enter", function (event) { + * // add custom action + * $("#my-elem").left("200"); + * }) + * .on("destroy", function (event) { + * // reset my element to start position + * if (event.reset) { + * $("#my-elem").left("0"); + * } + * }); + * + * @property {object} event - The event Object passed to each callback + * @property {string} event.type - The name of the event + * @property {ScrollScene} event.target - The ScrollScene object that triggered this event + * @property {boolean} event.reset - Indicates if the destroy method was called with reset `true` or `false`. */ /** @@ -2126,7 +2270,7 @@ Greensock License info at http://www.greensock.com/licensing/ if ($.isFunction(callback)) { var names = $.trim(name).toLowerCase() .replace(/(\w+)\.(\w+)/g, '$1.' + NAMESPACE + '_$2') // add custom namespace, if one is defined - .replace(/( |^)(\w+)( |$)/g, '$1$2.' + NAMESPACE + '$3'); // add namespace to regulars. + .replace(/( |^)(\w+)(?= |$)/g, '$1$2.' + NAMESPACE ); // add namespace to regulars. $(ScrollScene).on(names, callback); } else { log(1, "ERROR calling method 'on()': Supplied argument is not a valid callback!"); @@ -2154,7 +2298,7 @@ Greensock License info at http://www.greensock.com/licensing/ this.off = function (name, callback) { var names = $.trim(name).toLowerCase() .replace(/(\w+)\.(\w+)/g, '$1.' + NAMESPACE + '_$2') // add custom namespace, if one is defined - .replace(/( |^)(\w+)( |$)/g, '$1$2.' + NAMESPACE + '$3'); // add namespace to regulars. + .replace(/( |^)(\w+)(?= |$)/g, '$1$2.' + NAMESPACE + '$3'); // add namespace to regulars. $(ScrollScene).off(names, callback); return ScrollScene; }; @@ -2172,14 +2316,7 @@ Greensock License info at http://www.greensock.com/licensing/ */ this.trigger = function (name, vars) { log(3, 'event fired:', name, "->", vars); - var event = { - type: $.trim(name).toLowerCase(), - target: ScrollScene - }; - if ($.isPlainObject(vars)) { - event = $.extend({}, vars, event); - } - // fire all callbacks of the event + var event = $.Event($.trim(name).toLowerCase(), vars); $(ScrollScene).trigger(event); return ScrollScene; }; @@ -2260,5 +2397,8 @@ Greensock License info at http://www.greensock.com/licensing/ o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string" ); }; + var isMarginCollapseType = function (str) { + return ["block", "flex", "list-item", "table", "-webkit-box"].indexOf(str) > -1; + }; })(jQuery, window); \ No newline at end of file diff --git a/js/jquery.scrollmagic.min.js b/js/jquery.scrollmagic.min.js index 0f4d0bb6..d7b7d460 100644 --- a/js/jquery.scrollmagic.min.js +++ b/js/jquery.scrollmagic.min.js @@ -1,2 +1,2 @@ /* ScrollMagic v1.1.0-alpha | (c) 2014 Jan Paepke (@janpaepke) | license & info: http://janpaepke.github.io/ScrollMagic */ -!function(e,t){"use strict";var r=function(r){var i,s="ScrollMagic",a={container:t,vertical:!0,globalSceneOptions:{},loglevel:2,triggerPosUpdateInterval:100},l=this,c=e.extend({},a,r),g=[],u=!1,f=0,h="PAUSED",p=!0,d=0,v=!1,m=!0,w=function(){if(e.each(c,function(e){a.hasOwnProperty(e)||delete c[e]}),c.container=e(c.container).first(),0!==c.container.length){p=!e.contains(document,c.container.get(0)),d=c.vertical?c.container.height():c.container.width(),c.container.on("scroll resize",S);try{TweenLite.ticker.addEventListener("tick",y),v=!0}catch(r){c.container.on("scroll resize",y),v=!1}c.triggerPosUpdateInterval=parseInt(c.triggerPosUpdateInterval),c.triggerPosUpdateInterval>0&&t.setInterval(function(){e.each(g,function(e,t){t.updateTriggerElementPosition()})},c.triggerPosUpdateInterval)}},E=function(){return c.vertical?c.container.scrollTop():c.container.scrollLeft()},T=function(e){c.vertical?c.container.scrollTop(e):c.container.scrollLeft(e)},y=function(){if(u&&m){var t=e.isArray(u)?u:g.slice(0),r=f;f=l.scrollPos();var n=f-r;h=0===n?"PAUSED":n>0?"FORWARD":"REVERSE",0>n&&t.reverse(),e.each(t,function(e,t){t.update(!0)}),0===t.length&&c.loglevel>=3,u=!1}},S=function(e){"resize"==e.type&&(d=c.vertical?c.container.height():c.container.width()),u=!0},F=function(e){if(e.length<=1)return e;var t=e.slice(0);return t.sort(function(e,t){return e.scrollOffset()>t.scrollOffset()?1:-1}),t};return this.addScene=function(t){return e.isArray(t)?e.each(t,function(e,t){l.addScene(t)}):t.parent()!=l?t.addTo(l):-1==e.inArray(g,t)&&(g.push(t),g=F(g),t.on("shift."+s+"_sort",function(){g=F(g)}),e.each(c.globalSceneOptions,function(e,r){t[e]&&t[e].call(t,r)})),l},this.removeScene=function(t){if(e.isArray(t))e.each(t,function(e,t){l.removeScene(t)});else{var r=e.inArray(t,g);r>-1&&(t.off("shift."+s+"_sort"),g.splice(r,1),t.remove())}return l},this.updateScene=function(t,r){return e.isArray(t)?e.each(t,function(e,t){l.updateScene(t,r)}):r?t.update(!0):(e.isArray(u)||(u=[]),-1==e.inArray(t,u)&&u.push(t),u=F(u)),l},this.update=function(e){return S({type:"resize"}),e&&y(),l},this.scrollTo=function(t){if(t instanceof n)t.parent()===l?l.scrollTo(t.scrollOffset()):log(2,"scrollTo(): The supplied scene does not belong to this controller. Scroll cancelled.",t);else if("string"===e.type(t)||o(t)||t instanceof e){console.log(t);var r=e(t).first();if(r[0]){var i=r.offset();l.scrollTo(c.vertical?i.top:i.left)}else log(2,"scrollTo(): The supplied element could not be found. Scroll cancelled.",newScrollPos)}else e.isFunction(t)?T=t:T.call(c.container[0],t);return l},this.scrollPos=function(t){return arguments.length?(e.isFunction(t)&&(E=t),l):E.call(l)},this.info=function(e){var t={size:d,vertical:c.vertical,scrollPos:f,scrollDirection:h,container:c.container,isDocument:p};return arguments.length?void 0!==t[e]?t[e]:void 0:t},this.loglevel=function(e){return arguments.length?(c.loglevel!=e&&(c.loglevel=e),l):c.loglevel},this.enabled=function(e){return arguments.length?(m!=e&&(m=!!e,l.updateScene(g,!0)),l):m},this.destroy=function(e){for(t.clearTimeout(i);g.length>0;){var r=g[g.length-1];r.destroy(e)}return c.container.off("scroll resize",S),v?TweenLite.ticker.removeEventListener("tick",y):c.container.off("scroll resize",y),null},w(),l},n=function(r){var n,o,s,a,l,c,g=["onCenter","onEnter","onLeave"],u="ScrollScene",f={duration:0,offset:0,triggerElement:null,triggerHook:g[0],reverse:!0,tweenChanges:!1,loglevel:2},h=this,p=e.extend({},f,r),d="BEFORE",v=0,m={start:0,end:0},w=0,E=!0,T={unknownOptionSupplied:function(){e.each(p,function(e){f.hasOwnProperty(e)||delete p[e]})},duration:function(){p.duration=parseFloat(p.duration),(!e.isNumeric(p.duration)||p.duration<0)&&(p.duration=f.duration)},offset:function(){p.offset=parseFloat(p.offset),e.isNumeric(p.offset)||(p.offset=f.offset)},triggerElement:function(){null!==p.triggerElement&&0===e(p.triggerElement).length&&(p.triggerElement=f.triggerElement)},triggerHook:function(){e.isNumeric(p.triggerHook)||-1!=e.inArray(p.triggerHook,g)||(p.triggerHook=f.triggerHook)},loglevel:function(){},checkIfPinnedElementIsTweened:function(){if(o&&n&&p.triggerElement&&p.loglevel>=2){var t=o.getTweensOf(e(p.triggerElement)),r=n.info("vertical");e.each(t,function(e,t){var n=t.vars.css||t.vars,i=r?void 0!==n.top||void 0!==n.bottom:void 0!==n.left||void 0!==n.right;return i?!1:void 0})}}},y=function(){S(),h.on("change.internal",function(e){"loglevel"!==e.what&&"tweenChanges"!==e.what&&("triggerElement"===e.what?h.updateTriggerElementPosition():"reverse"===e.what&&h.update(),("DURING"!==d&&"duration"==e.what||"AFTER"===d&&0===p.duration)&&b())}).on("shift.internal",function(){F(),h.update()}).on("progress.internal",function(){R(),b()})},S=function(t){if(arguments.length)e.isArray(t)||(t=[t]);else{t=[];for(var r in T)t.push(r)}e.each(t,function(e,t){T[t]&&T[t]()})},F=function(){m={start:w+p.offset},n&&p.triggerElement&&(m.start-=n.info("size")*h.triggerHook()),m.end=m.start+p.duration},R=function(e){if(o){var t=e>=0&&1>=e?e:v;if(-1===o.repeat())if(("DURING"===d||"AFTER"===d&&0===p.duration)&&o.paused())o.play();else{if("DURING"===d||o.paused())return!1;o.pause()}else{if(t==o.progress())return!1;0===p.duration?"AFTER"===d?o.play():o.reverse():p.tweenChanges?o.tweenTo(t*o.duration()):o.progress(t).pause()}return!0}return!1},b=function(e){if(s&&n){var t=n.info();if(e||"DURING"!==d&&("AFTER"!==d||0!==p.duration)){var r={position:a.inFlow?"relative":"absolute",top:0,left:0},o=s.css("position")!=r.position;a.pushFollowers?"AFTER"===d&&0===parseFloat(a.spacer.css("padding-top"))?o=!0:"BEFORE"===d&&0===parseFloat(a.spacer.css("padding-bottom"))&&(o=!0):r[t.vertical?"top":"left"]=p.duration*v,s.css(r),o&&(s.removeClass(a.pinnedClass),P())}else{"fixed"!=s.css("position")&&(s.css("position","fixed"),P(),s.addClass(a.pinnedClass));var l=i(a.spacer,!0),c=p.reverse||0===p.duration?t.scrollPos-m.start:Math.round(v*p.duration*10)/10;l.top-=parseFloat(a.spacer.css("margin-top")),l[t.vertical?"top":"left"]+=c,s.css({top:l.top,left:l.left})}}},P=function(){if(s&&n&&a.inFlow){var r="AFTER"===d,i="BEFORE"===d,o="DURING"===d,l="fixed"==s.css("position"),c=n.info("vertical"),g=a.spacer.children().first(),u=e.inArray(a.spacer.css("display"),["block","flex","list-item","table","-webkit-box"])>-1,f={};u?(f["margin-top"]=i||o&&l?s.css("margin-top"):"auto",f["margin-bottom"]=r||o&&l?s.css("margin-bottom"):"auto"):f["margin-top"]=f["margin-bottom"]="auto",a.relSize.width?l?e(t).width()==a.spacer.parent().width()?s.css("width","inherit"):s.css("width",a.spacer.width()):s.css("width","100%"):(f["min-width"]=g.outerWidth(!0),f.width=l?f["min-width"]:"auto"),a.relSize.height?l?e(t).height()==a.spacer.parent().height()?s.css("height","inherit"):s.css("height",a.spacer.height()):s.css("height","100%"):(f["min-height"]=g.outerHeight(!u),f.height=l?f["min-height"]:"auto"),a.pushFollowers&&(f["padding"+(c?"Top":"Left")]=p.duration*v,f["padding"+(c?"Bottom":"Right")]=p.duration*(1-v)),a.spacer.css(f)}},C=function(){n&&s&&"DURING"===d&&!n.info("isDocument")&&b()},k=function(){n&&s&&("DURING"===d||"AFTER"===d&&0===p.duration)&&(a.relSize.width&&e(t).width()!=a.spacer.parent().width()||a.relSize.height&&e(t).height()!=a.spacer.parent().height())&&P()};return this.parent=function(){return n},this.duration=function(e){if(!arguments.length)return p.duration;if(p.duration!=e){var t="duration";p[t]=e,S(t),h.trigger("change",{what:t,newval:p[t]}),h.trigger("shift",{reason:t})}return h},this.offset=function(e){if(!arguments.length)return p.offset;if(p.offset!=e){var t="offset";p[t]=e,S(t),h.trigger("change",{what:t,newval:p[t]}),h.trigger("shift",{reason:t})}return h},this.triggerElement=function(e){if(!arguments.length)return p.triggerElement;if(p.triggerElement!=e){var t="triggerElement";p[t]=e,S(t),h.trigger("change",{what:t,newval:p[t]})}return h},this.triggerHook=function(t){if(!arguments.length){var r;if(e.isNumeric(p.triggerHook))r=p.triggerHook;else switch(p.triggerHook){case"onCenter":r=.5;break;case"onLeave":r=0;break;case"onEnter":default:r=1}return r}if(p.triggerHook!=t){var n="triggerHook";p[n]=t,S(n),h.trigger("change",{what:n,newval:p[n]}),h.trigger("shift",{reason:n})}return h},this.reverse=function(e){if(!arguments.length)return p.reverse;if(p.reverse!=e){var t="reverse";p[t]=!!e,S(t),h.trigger("change",{what:t,newval:p[t]})}return h},this.tweenChanges=function(e){if(!arguments.length)return p.tweenChanges;if(p.tweenChanges!=e){var t="tweenChanges";p[t]=!!e,S(t),h.trigger("change",{what:t,newval:p[t]})}return h},this.loglevel=function(e){if(!arguments.length)return p.loglevel;if(p.loglevel!=e){var t="loglevel";p[t]=e,S(t),h.trigger("change",{what:t,newval:p[t]})}return h},this.state=function(){return d},this.triggerPosition=function(){var e=p.offset;return n&&(e+=p.triggerElement?w:n.info("size")*h.triggerHook()),e},this.triggerOffset=function(){return h.triggerPosition()},this.scrollOffset=function(){return m.start},this.update=function(e){if(n)if(e)if(n.enabled()&&E){var t,r=n.info("scrollPos");t=p.duration>0?(r-m.start)/(m.end-m.start):r>=m.start?1:0,h.trigger("update",{startPos:m.start,endPos:m.end,scrollPos:r}),h.progress(t)}else s&&"fixed"==s.css("position")&&b(!0);else n.updateScene(h,!1);return h},this.updateTriggerElementPosition=function(t){var r=0;if(n&&p.triggerElement){for(var o=e(p.triggerElement).first(),s=n.info(),a=i(s.container),l=s.vertical?"top":"left";o.parent().data("ScrollMagicPinSpacer");)o=o.parent();var c=i(o);s.isDocument||(a[l]-=s.scrollPos),r=c[l]-a[l]}var g=r!=w;return w=r,g&&!t&&h.trigger("shift",{reason:"triggerElementPosition"}),h},this.progress=function(e){if(arguments.length){var t=!1,r=d,i=n?n.info("scrollDirection"):"PAUSED";if(0>=e&&"BEFORE"!==d&&(e>=v||p.reverse)?(v=0,d="BEFORE",t=!0):e>0&&1>e&&(e>=v||p.reverse)?(v=e,d="DURING",t=!0):e>=1&&"AFTER"!==d?(v=1,d="AFTER",t=!0):"DURING"!==d||p.reverse||b(),t){var o={progress:v,state:d,scrollDirection:i},s=d!=r,a=function(e){h.trigger(e,o)};s&&(0===p.duration?"AFTER"===d&&(a("enter"),a("start")):"DURING"!==r&&(a("enter"),a("BEFORE"===r?"start":"end"))),a("progress"),s&&(0===p.duration?"BEFORE"===d&&(a("start"),a("leave")):"DURING"!==d&&(a("AFTER"===d?"end":"start"),a("leave")))}return h}return v},this.setTween=function(e){o&&h.removeTween();try{o=new TimelineMax({smoothChildTiming:!0}).add(e).pause()}catch(t){}finally{return e.repeat&&-1===e.repeat()&&(o.repeat(-1),o.yoyo(e.yoyo())),S("checkIfPinnedElementIsTweened"),R(),h}},this.removeTween=function(e){return o&&(e&&R(0),o.kill(),o=void 0),h},this.setPin=function(r,n){var i={pushFollowers:!0,spacerClass:"scrollmagic-pin-spacer",pinnedClass:""};if(n=e.extend({},i,n),r=e(r).first(),0===r.length)return h;if("fixed"==r.css("position"))return h;if(s){if(s===r)return h;h.removePin()}s=r,s.parent().hide();var o="absolute"!=s.css("position"),l=s.css(["display","top","left","bottom","right"]),c=s.css(["width","height"]);s.parent().show();var g=e("
    ").addClass(n.spacerClass).css(l).data("ScrollMagicPinSpacer",!0).css({position:o?"relative":"absolute","margin-left":"auto","margin-right":"auto","box-sizing":"content-box","-moz-box-sizing":"content-box","-webkit-box-sizing":"content-box"});!o&&n.pushFollowers&&(n.pushFollowers=!1);var f=s[0].style;return a={spacer:g,relSize:{width:"%"===c.width.slice(-1),height:"%"===c.height.slice(-1)},pushFollowers:n.pushFollowers,inFlow:o,origStyle:{position:f.position||"",top:f.position||"",left:f.position||"",bottom:f.position||"",right:f.position||""},pinnedClass:n.pinnedClass},a.relSize.width&&g.css("width",c.width),a.relSize.height&&g.css("height",c.height),s.before(g).appendTo(g).css({position:o?"relative":"absolute",top:"auto",left:"auto",bottom:"auto",right:"auto"}),e(t).on("scroll."+u+"_pin resize."+u+"_pin",C),b(),h},this.removePin=function(r){return s&&(r||!n?(s.insertBefore(a.spacer).css(a.origStyle),a.spacer.remove()):"DURING"===d&&b(!0),e(t).off("scroll."+u+"_pin resize."+u+"_pin"),s=void 0),h},this.setClassToggle=function(t,r){var n=e(t);return 0===n.length||"string"!==e.type(r)?h:(l=r,c=n,h.on("enter.internal_class leave.internal_class",function(e){c.toggleClass(l,"enter"===e.type)}),h)},this.removeClassToggle=function(e){return e&&c.removeClass(l),h.off("start.internal_class end.internal_class"),l=void 0,c=void 0,h},this.addTo=function(e){return n!=e?(n&&n.removeScene(h),n=e,S(),h.updateTriggerElementPosition(!0),F(),P(),n.info("container").on("resize."+u,function(){k(),h.triggerHook()>0&&h.trigger("shift",{reason:"containerSize"})}),e.addScene(h),h.update(),h):void 0},this.enabled=function(e){return arguments.length?(E!=e&&(E=!!e,h.update(!0)),h):E},this.remove=function(){if(n){n.info("container").off("resize."+u);var e=n;n=void 0,e.removeScene(h)}return h},this.destroy=function(e){return this.removeTween(e),this.removePin(e),this.removeClassToggle(e),this.remove(),this.off("start end enter leave progress change update shift shift.internal change.internal progress.internal"),null},this.on=function(t,r){if(e.isFunction(r)){var n=e.trim(t).toLowerCase().replace(/(\w+)\.(\w+)/g,"$1."+u+"_$2").replace(/( |^)(\w+)( |$)/g,"$1$2."+u+"$3");e(h).on(n,r)}return h},this.off=function(t,r){var n=e.trim(t).toLowerCase().replace(/(\w+)\.(\w+)/g,"$1."+u+"_$2").replace(/( |^)(\w+)( |$)/g,"$1$2."+u+"$3");return e(h).off(n,r),h},this.trigger=function(t,r){var n={type:e.trim(t).toLowerCase(),target:h};return e.isPlainObject(r)&&(n=e.extend({},r,n)),e(h).trigger(n),h},y(),h};r.version="1.1.0-alpha",t.ScrollScene=n,t.ScrollMagic=r;var i=function(t,r){var n={top:0,left:0},i=t[0];if(i)if(i.getBoundingClientRect){var o=i.getBoundingClientRect();n.top=o.top,n.left=o.left,r||(n.top+=e(document).scrollTop(),n.left+=e(document).scrollLeft())}else n=t.offset()||n,r&&(n.top-=e(document).scrollTop(),n.left-=e(document).scrollLeft());return n},o=function(e){return"object"==typeof HTMLElement?e instanceof HTMLElement:e&&"object"==typeof e&&null!==e&&1===e.nodeType&&"string"==typeof e.nodeName}}(jQuery,window); \ No newline at end of file +!function(e,t){"use strict";var n=function(n){var i,s="ScrollMagic",a={container:t,vertical:!0,globalSceneOptions:{},loglevel:2,sceneRefreshInterval:100},l=this,c=e.extend({},a,n),u=[],f=!1,g=0,h="PAUSED",d=!0,p=0,v=!1,m=!0,w=function(){if(e.each(c,function(e){a.hasOwnProperty(e)||delete c[e]}),c.container=e(c.container).first(),0!==c.container.length){d=!e.contains(document,c.container.get(0)),p=c.vertical?c.container.height():c.container.width(),c.container.on("scroll resize",F);try{TweenLite.ticker.addEventListener("tick",y),v=!0}catch(n){c.container.on("scroll resize",y),v=!1}c.sceneRefreshInterval=parseInt(c.sceneRefreshInterval),c.sceneRefreshInterval>0&&(i=t.setInterval(function(){e.each(u,function(e,t){t.refresh()})},c.sceneRefreshInterval))}},E=function(){return c.vertical?c.container.scrollTop():c.container.scrollLeft()},S=function(e){c.vertical?c.container.scrollTop(e):c.container.scrollLeft(e)},y=function(){if(f&&m){var t=e.isArray(f)?f:u.slice(0),n=g;g=l.scrollPos();var r=g-n;h=0===r?"PAUSED":r>0?"FORWARD":"REVERSE",0>r&&t.reverse(),e.each(t,function(e,t){t.update(!0)}),0===t.length&&c.loglevel>=3,f=!1}},F=function(e){"resize"==e.type&&(p=c.vertical?c.container.height():c.container.width()),f=!0},R=function(e){if(e.length<=1)return e;var t=e.slice(0);return t.sort(function(e,t){return e.scrollOffset()>t.scrollOffset()?1:-1}),t};return this.addScene=function(t){return e.isArray(t)?e.each(t,function(e,t){l.addScene(t)}):t instanceof r&&(t.parent()!=l?t.addTo(l):-1==e.inArray(u,t)&&(u.push(t),u=R(u),t.on("shift."+s+"_sort",function(){u=R(u)}),e.each(c.globalSceneOptions,function(e,n){t[e]&&t[e].call(t,n)}))),l},this.removeScene=function(t){if(e.isArray(t))e.each(t,function(e,t){l.removeScene(t)});else{var n=e.inArray(t,u);n>-1&&(t.off("shift."+s+"_sort"),u.splice(n,1),t.remove())}return l},this.updateScene=function(t,n){return e.isArray(t)?e.each(t,function(e,t){l.updateScene(t,n)}):n?t.update(!0):(e.isArray(f)||(f=[]),-1==e.inArray(t,f)&&f.push(t),f=R(f)),l},this.update=function(e){return F({type:"resize"}),e&&y(),l},this.scrollTo=function(t){if(t instanceof r)t.parent()===l?l.scrollTo(t.scrollOffset()):log(2,"scrollTo(): The supplied scene does not belong to this controller. Scroll cancelled.",t);else if("string"===e.type(t)||o(t)||t instanceof e){var n=e(t).first();if(n[0]){var i=n.offset();l.scrollTo(c.vertical?i.top:i.left)}else log(2,"scrollTo(): The supplied element could not be found. Scroll cancelled.",t)}else e.isFunction(t)?S=t:S.call(c.container[0],t);return l},this.scrollPos=function(t){return arguments.length?(e.isFunction(t)&&(E=t),l):E.call(l)},this.info=function(e){var t={size:p,vertical:c.vertical,scrollPos:g,scrollDirection:h,container:c.container,isDocument:d};return arguments.length?void 0!==t[e]?t[e]:void 0:t},this.loglevel=function(e){return arguments.length?(c.loglevel!=e&&(c.loglevel=e),l):c.loglevel},this.enabled=function(e){return arguments.length?(m!=e&&(m=!!e,l.updateScene(u,!0)),l):m},this.destroy=function(e){t.clearTimeout(i);for(var n=u.length;n--;)u[n].destroy(e);return c.container.off("scroll resize",F),v?TweenLite.ticker.removeEventListener("tick",y):c.container.off("scroll resize",y),null},w(),l},r=function(n){var r,o,a,l,c,u,f,g={onCenter:.5,onEnter:1,onLeave:0},h="ScrollScene",d={duration:0,offset:0,triggerElement:null,triggerHook:"onCenter",reverse:!0,tweenChanges:!1,loglevel:2},p=this,v=e.extend({},d,n),m="BEFORE",w=0,E={start:0,end:0},S=0,y=!0,F={unknownOptionSupplied:function(){e.each(v,function(e){d.hasOwnProperty(e)||delete v[e]})},duration:function(){if(e.isFunction(v.duration)){r=v.duration;try{v.duration=parseFloat(r())}catch(t){r=void 0,v.duration=d.duration}}else v.duration=parseFloat(v.duration),(!e.isNumeric(v.duration)||v.duration<0)&&(v.duration=d.duration)},offset:function(){v.offset=parseFloat(v.offset),e.isNumeric(v.offset)||(v.offset=d.offset)},triggerElement:function(){null!==v.triggerElement&&0===e(v.triggerElement).length&&(v.triggerElement=d.triggerElement)},triggerHook:function(){v.triggerHook in g||(v.triggerHook=e.isNumeric(v.triggerHook)?parseFloat(v.triggerHook):d.triggerHook)},reverse:function(){v.reverse=!!v.reverse},tweenChanges:function(){v.tweenChanges=!!v.tweenChanges}},R=function(){T(),p.on("change.internal",function(e){"loglevel"!==e.what&&"tweenChanges"!==e.what&&("triggerElement"===e.what?z():"reverse"===e.what&&p.update())}).on("shift.internal",function(e){C(),p.update(),("AFTER"===m&&"duration"===e.reason||"DURING"===m&&0===v.duration)&&P()}).on("progress.internal",function(){I(),P()}).on("destroy",function(e){e.preventDefault()})},T=function(t){if(arguments.length)e.isArray(t)||(t=[t]);else{t=[];for(var n in F)t.push(n)}e.each(t,function(e,t){F[t]&&F[t]()})},b=function(e,t){var n=!1,r=v[e];return v[e]!=t&&(v[e]=t,T(e),n=r!=v[e]),n},C=function(){E={start:S+v.offset},o&&v.triggerElement&&(E.start-=o.info("size")*p.triggerHook()),E.end=E.start+v.duration},D=function(e){if(r){var t="duration";b(t,r())&&!e&&(p.trigger("change",{what:t,newval:v[t]}),p.trigger("shift",{reason:t}))}},z=function(t){var n=0;if(o&&v.triggerElement){for(var r=e(v.triggerElement).first(),s=o.info(),a=i(s.container),l=s.vertical?"top":"left";r.parent().data("ScrollMagicPinSpacer");)r=r.parent();var c=i(r);s.isDocument||(a[l]-=s.scrollPos),n=c[l]-a[l]}var u=n!=S;S=n,u&&!t&&p.trigger("shift",{reason:"triggerElementPosition"})},I=function(e){if(a){var t=e>=0&&1>=e?e:w;if(-1===a.repeat())if("DURING"===m&&a.paused())a.play();else{if("DURING"===m||a.paused())return!1;a.pause()}else{if(t==a.progress())return!1;0===v.duration?"DURING"===m?a.play():a.reverse():v.tweenChanges?a.tweenTo(t*a.duration()):a.progress(t).pause()}return!0}return!1},P=function(e){if(l&&o){var t=o.info();if(e||"DURING"!==m){var n={position:c.inFlow?"relative":"absolute",top:0,left:0},r=l.css("position")!=n.position;c.pushFollowers?v.duration>0&&("AFTER"===m&&0===parseFloat(c.spacer.css("padding-top"))?r=!0:"BEFORE"===m&&0===parseFloat(c.spacer.css("padding-bottom"))&&(r=!0)):n[t.vertical?"top":"left"]=v.duration*w,l.css(n),r&&(l.removeClass(c.pinnedClass),k())}else{"fixed"!=l.css("position")&&(l.css("position","fixed"),k(),l.addClass(c.pinnedClass));var s=i(c.spacer,!0),a=v.reverse||0===v.duration?t.scrollPos-E.start:Math.round(w*v.duration*10)/10;s.top-=parseFloat(c.spacer.css("margin-top")),s[t.vertical?"top":"left"]+=a,l.css({top:s.top,left:s.left})}}},k=function(){if(l&&o&&c.inFlow){var n="AFTER"===m,r="BEFORE"===m,i="DURING"===m,a="fixed"==l.css("position"),u=o.info("vertical"),f=c.spacer.children().first(),g=s(c.spacer.css("display")),h={};g?(h["margin-top"]=r||i&&a?l.css("margin-top"):"auto",h["margin-bottom"]=n||i&&a?l.css("margin-bottom"):"auto"):h["margin-top"]=h["margin-bottom"]="auto",c.relSize.width||c.relSize.autoFullWidth?a?e(t).width()==c.spacer.parent().width()?l.css("width",c.relSize.autoFullWidth?"100%":"inherit"):l.css("width",c.spacer.width()):l.css("width","100%"):(h["min-width"]=f.outerWidth(!f.is(l)),h.width=a?h["min-width"]:"auto"),c.relSize.height?a?e(t).height()==c.spacer.parent().height()?l.css("height","inherit"):l.css("height",c.spacer.height()):l.css("height","100%"):(h["min-height"]=f.outerHeight(!g),h.height=a?h["min-height"]:"auto"),c.pushFollowers&&(h["padding"+(u?"Top":"Left")]=v.duration*w,h["padding"+(u?"Bottom":"Right")]=v.duration*(1-w)),c.spacer.css(h)}},O=function(){o&&l&&"DURING"===m&&!o.info("isDocument")&&P()},x=function(){o&&l&&"DURING"===m&&((c.relSize.width||c.relSize.autoFullWidth)&&e(t).width()!=c.spacer.parent().width()||c.relSize.height&&e(t).height()!=c.spacer.parent().height())&&k()},N=function(e){o&&l&&"DURING"===m&&o.scrollTo(o.info("scrollPos")-(e.originalEvent.wheelDelta/3||30*-e.originalEvent.detail))};return this.parent=function(){return o},this.duration=function(t){var n="duration";return arguments.length?(e.isFunction(t)||(r=void 0),b(n,t)&&(p.trigger("change",{what:n,newval:v[n]}),p.trigger("shift",{reason:n})),p):v[n]},this.offset=function(e){var t="offset";return arguments.length?(b(t,e)&&(p.trigger("change",{what:t,newval:v[t]}),p.trigger("shift",{reason:t})),p):v[t]},this.triggerElement=function(e){var t="triggerElement";return arguments.length?(b(t,e)&&p.trigger("change",{what:t,newval:v[t]}),p):v[t]},this.triggerHook=function(t){var n="triggerHook";return arguments.length?(b(n,t)&&(p.trigger("change",{what:n,newval:v[n]}),p.trigger("shift",{reason:n})),p):e.isNumeric(v[n])?v[n]:g[v[n]]},this.reverse=function(e){var t="reverse";return arguments.length?(b(t,e)&&p.trigger("change",{what:t,newval:v[t]}),p):v[t]},this.tweenChanges=function(e){var t="tweenChanges";return arguments.length?(b(t,e)&&p.trigger("change",{what:t,newval:v[t]}),p):v[t]},this.loglevel=function(e){var t="loglevel";return arguments.length?(b(t,e)&&p.trigger("change",{what:t,newval:v[t]}),p):v[t]},this.state=function(){return m},this.triggerPosition=function(){var e=v.offset;return o&&(e+=v.triggerElement?S:o.info("size")*p.triggerHook()),e},this.triggerOffset=function(){return p.triggerPosition()},this.scrollOffset=function(){return E.start},this.update=function(e){if(o)if(e)if(o.enabled()&&y){var t,n=o.info("scrollPos");t=v.duration>0?(n-E.start)/(E.end-E.start):n>=E.start?1:0,p.trigger("update",{startPos:E.start,endPos:E.end,scrollPos:n}),p.progress(t)}else l&&"fixed"==l.css("position")&&P(!0);else o.updateScene(p,!1);return p},this.refresh=function(){return D(),z(),p},this.progress=function(e){if(arguments.length){var t=!1,n=m,r=o?o.info("scrollDirection"):"PAUSED",i=v.reverse||e>=w;if(0===v.duration?(t=w!=e,w=1>e&&i?0:1,m=0===w?"BEFORE":"DURING"):0>=e&&"BEFORE"!==m&&i?(w=0,m="BEFORE",t=!0):e>0&&1>e&&i?(w=e,m="DURING",t=!0):e>=1&&"AFTER"!==m?(w=1,m="AFTER",t=!0):"DURING"!==m||i||P(),t){var s={progress:w,state:m,scrollDirection:r},a=m!=n,l=function(e){p.trigger(e,s)};a&&"DURING"!==n&&(l("enter"),l("BEFORE"===n?"start":"end")),l("progress"),a&&"DURING"!==m&&(l("BEFORE"===m?"start":"end"),l("leave"))}return p}return w},this.setTween=function(e){a&&p.removeTween();try{a=new TimelineMax({smoothChildTiming:!0}).add(e).pause()}catch(t){}finally{return e.repeat&&-1===e.repeat()&&(a.repeat(-1),a.yoyo(e.yoyo())),T("checkIfTriggerElementIsTweened"),I(),p}},this.removeTween=function(e){return a&&(e&&I(0),a.kill(),a=void 0),p},this.setPin=function(n,r){var i={pushFollowers:!0,spacerClass:"scrollmagic-pin-spacer",pinnedClass:""};if(r=e.extend({},i,r),n=e(n).first(),0===n.length)return p;if("fixed"==n.css("position"))return p;if(l){if(l===n)return p;p.removePin()}l=n,l.parent().hide();var o="absolute"!=l.css("position"),a=l.css(["display","top","left","bottom","right"]),u=l.css(["width","height"]);l.parent().show(),"0px"===u.width&&o&&s(a.display),!o&&r.pushFollowers&&(r.pushFollowers=!1);var f=e("
    ").addClass(r.spacerClass).css(a).data("ScrollMagicPinSpacer",!0).css({position:o?"relative":"absolute","margin-left":"auto","margin-right":"auto","box-sizing":"content-box","-moz-box-sizing":"content-box","-webkit-box-sizing":"content-box"}),g=l[0].style;return c={spacer:f,relSize:{width:"%"===u.width.slice(-1),height:"%"===u.height.slice(-1),autoFullWidth:"0px"===u.width&&o&&s(a.display)},pushFollowers:r.pushFollowers,inFlow:o,origStyle:{width:g.width||"",position:g.position||"",top:g.position||"",left:g.position||"",bottom:g.position||"",right:g.position||""},pinnedClass:r.pinnedClass},c.relSize.width&&f.css("width",u.width),c.relSize.height&&f.css("height",u.height),l.before(f).appendTo(f).css({position:o?"relative":"absolute",top:"auto",left:"auto",bottom:"auto",right:"auto"}),e(t).on("scroll."+h+"_pin resize."+h+"_pin",O),l.on("mousewheel DOMMouseScroll",N),P(),p},this.removePin=function(n){return l&&(n||!o?(l.insertBefore(c.spacer).css(c.origStyle),c.spacer.remove()):"DURING"===m&&P(!0),e(t).off("scroll."+h+"_pin resize."+h+"_pin"),l.off("mousewheel DOMMouseScroll",N),l=void 0),p},this.setClassToggle=function(t,n){var r=e(t);return 0===r.length||"string"!==e.type(n)?p:(u=n,f=r,p.on("enter.internal_class leave.internal_class",function(e){f.toggleClass(u,"enter"===e.type)}),p)},this.removeClassToggle=function(e){return f&&e&&f.removeClass(u),p.off("start.internal_class end.internal_class"),u=void 0,f=void 0,p},this.addTo=function(e){return o!=e?(o&&o.removeScene(p),o=e,T(),D(!0),z(!0),C(),k(),o.info("container").on("resize."+h,function(){x(),p.triggerHook()>0&&p.trigger("shift",{reason:"containerSize"})}),e.addScene(p),p.update(),p):void 0},this.enabled=function(e){return arguments.length?(y!=e&&(y=!!e,p.update(!0)),p):y},this.remove=function(){if(o){o.info("container").off("resize."+h);var e=o;o=void 0,e.removeScene(p)}return p},this.destroy=function(e){return p.removeTween(e),p.removePin(e),p.removeClassToggle(e),p.trigger("destroy",{reset:e}),p.remove(),p.off("start end enter leave progress change update shift destroy shift.internal change.internal progress.internal"),null},this.on=function(t,n){if(e.isFunction(n)){var r=e.trim(t).toLowerCase().replace(/(\w+)\.(\w+)/g,"$1."+h+"_$2").replace(/( |^)(\w+)(?= |$)/g,"$1$2."+h);e(p).on(r,n)}return p},this.off=function(t,n){var r=e.trim(t).toLowerCase().replace(/(\w+)\.(\w+)/g,"$1."+h+"_$2").replace(/( |^)(\w+)(?= |$)/g,"$1$2."+h+"$3");return e(p).off(r,n),p},this.trigger=function(t,n){var r=e.Event(e.trim(t).toLowerCase(),n);return e(p).trigger(r),p},R(),p};n.version="1.1.0-alpha",t.ScrollScene=r,t.ScrollMagic=n;var i=function(t,n){var r={top:0,left:0},i=t[0];if(i)if(i.getBoundingClientRect){var o=i.getBoundingClientRect();r.top=o.top,r.left=o.left,n||(r.top+=e(document).scrollTop(),r.left+=e(document).scrollLeft())}else r=t.offset()||r,n&&(r.top-=e(document).scrollTop(),r.left-=e(document).scrollLeft());return r},o=function(e){return"object"==typeof HTMLElement?e instanceof HTMLElement:e&&"object"==typeof e&&null!==e&&1===e.nodeType&&"string"==typeof e.nodeName},s=function(e){return["block","flex","list-item","table","-webkit-box"].indexOf(e)>-1}}(jQuery,window); \ No newline at end of file From 8b2c59609cca621ea5361c2595f466b1b12118dc Mon Sep 17 00:00:00 2001 From: Jan Paepke Date: Sat, 30 Aug 2014 10:08:59 +0200 Subject: [PATCH 40/72] added: some comments in examples --- examples/advanced/anchor_link_scrolling.html | 4 ++- examples/advanced/custom_containers.html | 6 ++-- examples/advanced/mobile_basic.html | 4 +++ examples/advanced/parallax_sections.html | 6 +++- examples/expert/image_sequence.html | 29 ++++++++------------ 5 files changed, 28 insertions(+), 21 deletions(-) diff --git a/examples/advanced/anchor_link_scrolling.html b/examples/advanced/anchor_link_scrolling.html index 3b03b745..07f49640 100644 --- a/examples/advanced/anchor_link_scrolling.html +++ b/examples/advanced/anchor_link_scrolling.html @@ -79,10 +79,12 @@

    Mastering in-page navigation.

    var id = $(this).attr("href"); if ($(id).length > 0) { e.preventDefault(); + + // trigger scroll controller.scrollTo(id); - if (window.history && window.history.pushState) { // if supported by the browser we can even update the URL. + if (window.history && window.history.pushState) { history.pushState("", document.title, id); } } diff --git a/examples/advanced/custom_containers.html b/examples/advanced/custom_containers.html index 4d655ee1..7429654d 100644 --- a/examples/advanced/custom_containers.html +++ b/examples/advanced/custom_containers.html @@ -46,14 +46,15 @@

    One page can have multiple scroll containers.