Reputation: 43
I'm new to ScrollMagic and I'm trying to figure out things. I'm working on a site with quite complex animations and I haven't found any tutorial or example on writing effective ScrollMagic code.
I'm not a programmer and have just a basic understanding of jQuery and JavaScript.
I have the following code at this point (I know it's a bit messy):
var controller;
$(document).ready(function($) {
// init controller
controller = new ScrollMagic({
globalSceneOptions: {
reverse: false
}
});
});
$(document).ready(function($) {
var tween = new TimelineMax()
.add( TweenMax.from(".helloHeadline", 1.5, {opacity: 0, ease: Sine.easeOut}), 0 )
.add( TweenMax.from(".helloTxt", 1.5, {opacity: 0, ease: Sine.easeOut}), 0 );
var scene = new ScrollScene({triggerElement: "#hello", duration: 0, offset: 0})
.setTween(tween)
.addTo(controller);
scene.addIndicators();
});
$(document).ready(function($) {
var tween = new TimelineMax()
.add( TweenMax.from(".components .simpleHeadline", 1.5, {opacity: 0, ease: Sine.easeOut}), 0 )
.add( TweenMax.from("#components_bg", 1.25, {scale: 0, ease: Elastic.easeOut}), .5 )
.add( TweenMax.from("#components_rabbit", 1.25, {scale: 0, ease: Elastic.easeOut}), 1 )
.add( TweenMax.from("#components_overlay", 1, {opacity: 0, ease: Sine.easeInOut}), 1.25 );
var scene = new ScrollScene({triggerElement: "#components_graphic", duration: 0, offset: -150})
.setTween(tween)
.addTo(controller);
scene.addIndicators();
});
$(document).ready(function($) {
var tween = TweenMax.from("#magic", 0.5,
{opacity: 0, ease: Quart.easeInOut}
);
var scene = new ScrollScene({triggerElement: "#magic", duration: 400, offset: 0, triggerHook: 1})
.setTween(tween)
.addTo(controller);
scene.addIndicators();
});
$(document).ready(function($) {
var tween = new TimelineMax()
.add( TweenMax.fromTo("#magic_hat", 0.01, {bottom: 800, opacity: 0}, {bottom: 800, opacity: 1}), 0 )
.add( TweenMax.to("#magic_hat", 1, {bottom: "0", ease: Bounce.easeOut}), 0.02 )
.add( TweenMax.from("#magic_hat_shadow", 1, {scale: 0, ease: Bounce.easeOut}), 0.02 )
.add( TweenMax.fromTo("#magic_hand", .75, {opacity: 0}, {opacity: 1, left: 60, top: -10 ,ease: Quart.easeIn}), .75 )
.add( TweenMax.to("#magic_hand", .5, {left: "-20", top: "4", ease: Circ.easeInOut, repeat: 2, yoyo: true}) )
.add( TweenMax.to("#magic_hand", .5, {left: "0", top: "0", ease: Quart.easeOut}) )
.add( TweenMax.from("#magic_hat_rabbit", .65, {top: "166", ease: Quart.easeOut}) );
var scene = new ScrollScene({triggerElement: "#magic", duration: 0, offset: 100})
.setTween(tween)
.addTo(controller);
scene.addIndicators();
});
$(document).ready(function($) {
var tween = TweenMax.from(".magicCntnt", 1,
{scale: 0, ease: Elastic.easeOut}
);
var scene = new ScrollScene({triggerElement: "#magic", duration: 0, offset: 200})
.setTween(tween)
.addTo(controller);
scene.addIndicators();
});
$(document).ready(function($) {
var tween = TweenMax.from("#works_macbook_top", 1,
{ rotationX: -80, ease: Sine.easeOut }
);
var scene = new ScrollScene({triggerElement: ".works .graphic", duration: 250, offset: -100, reverse: true})
.setTween(tween)
.addTo(controller);
scene.addIndicators();
});
var stuffDuration = 1;
$(document).ready(function($) {
var tween = TweenMax.from(".stuff.ruler", stuffDuration, {scale: 0, ease: Elastic.easeOut} );
var scene = new ScrollScene({triggerElement: ".stuff.ruler", duration: 0, offset: 0}).setTween(tween).addTo(controller);
});
$(document).ready(function($) {
var tween = TweenMax.from(".stuff.pencil", stuffDuration, {scale: 0, ease: Elastic.easeOut} );
var scene = new ScrollScene({triggerElement: ".stuff.pencil", duration: 0, offset: 0}).setTween(tween).addTo(controller);
});
$(document).ready(function($) {
var tween = TweenMax.from(".stuff.lightbulb", stuffDuration, {scale: 0, ease: Elastic.easeOut} );
var scene = new ScrollScene({triggerElement: ".stuff.lightbulb", duration: 0, offset: 0}).setTween(tween).addTo(controller);
});
I'm waiting for your tips on how to manage this in a simpler way.
I wasn't sure if I need separate document ready functions, but I was guessing declaring the same variables should only work in separate functions (sorry, lack of JavaScript / jQuery knowledge).
Is there a shorter and more effective way to manage multiple scenes and tweens? Any tips are welcome!
Upvotes: 2
Views: 6472
Reputation: 3743
You should consider working with require.js now, since ScrollMagic supports UMD module loading since v1.2. If you don't know about require.js yet you should go on an read some tutorials like http://requirejs.org/ itself. Trust me, it really makes life easier when handling with encapsulated modules.
Set up an index.html with the Bootstrap starter template like so:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 101 Template</title>
<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<h1>Hello, world!</h1>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
<script src="libs/modernizr/modernizr.js"></script>
<script src="libs/requirejs/require.js" data-main="js/app.js"></script>
</body>
</html>
Since you load the scripts at the end you don't have to do that $(document).ready(...)
thing. After you've done the above step you can start over with your app.js
as an entry point for your project:
;(function (exports) {
"use strict";
require.config({
baseUrl: '/js',
packages: [
{
name: 'when',
location: 'libs/when',
main: 'when'
}
],
paths: {
image: 'libs/requirejs-plugins/src/image',
Spinner: 'libs/spin.js/spin',
iScroll: 'libs/iscroll/build/iscroll-probe',
ScrollMagic: 'libs/ScrollMagic/js/jquery.scrollmagic.min',
ScrollScene: 'libs/ScrollMagic/js/jquery.scrollmagic.min',
TimelineMax: 'libs/gsap/src/minified/TimelineMax.min',
TweenMax: 'libs/gsap/src/minified/TweenMax.min',
TweenLite: 'libs/gsap/src/minified/TweenLite.min',
sprintf: 'libs/sprintf/dist/sprintf.min'
},
ScrollMagic: ['libs/ScrollMagic/js/jquery.scrollmagic.debug'], // FIXME debug
},
map: {
'*': {
'css': '/js/libs/require-css/css.min.js'
}
}
});
// Fixes the issue when jquery is already loaded (in our case in the <head>)
define('jquery', function () {
return jQuery;
});
require(['start']);
}(window));
The next step would be to define one global controller with an iScroll fallback for mobile devices. My controller module looks like this:
define(['jquery', 'ScrollMagic', 'iScroll'], function ($, ScrollMagic, iScroll) {
"use strict";
var controller;
if (!true || Modernizr.touch) {
var myScroll;
$(function () {
// wrap for iscroll
var $container = $("#content-wrapper")
.addClass("scrollContainer")
.wrapInner('<div class="scrollContent"></div>');
// add iScroll
myScroll = new IScroll($container[0], {
scrollX: false,
scrollY: true,
mouseWheel: true,
scrollbars: true,
useTransform: false, // Modernizr.csstransforms, // Do NOT enable transforms, since Scrollscene.setPin() won't work then
useTransition: false, // Modernizr.csstransitions,
probeType: 3,
bounce: true,
momentum: true,
click: true
});
// init the controller
controller = new ScrollMagic({container: $container[0]});
$('html').css('overflow', 'hidden');
// update container on scroll
myScroll.on("scroll", function () {
controller.update(true);
});
// overwrite scroll position calculation to use child's offset instead of parents scrollTop();
controller.scrollPos(function () {
return -myScroll.y;
});
$(window).on('load', function () {
controller.update();
myScroll.refresh();
});
});
} else {
controller = new ScrollMagic();
}
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
};
$(window).on('resize', debounce(function () {
controller.update(true);
}, 100)).trigger('resize');
controller.debounce = debounce;
return controller;
});
Your start.js
should then require the global ScrollMagic controller
you set before and every other dependency that are necessary for your scrollmagic-project. Here is a brief snippet of how I accomplished that:
define(['jquery', 'spinner', 'preload', 'controller', 'ScrollScene', 'TweenMax'], function ($, spinner, preload, controller, ScrollScene, TweenMax) {
"use strict";
spinner = spinner().spin(document.body);
require([ 'when', 'slide1', 'slide2', 'slide3'], function (when, slide1, slide2, slide3) {
var preloading = when.all([slide1, slide2, slide3]);
preloading.spread(function (slide1, slide2, slide3) {
// Be awesome here
});
preloading.catch(function (e) {
console.warn('Error', e);
debugger;
});
preloading.progress(function (progress) {
console.log(progress);
spinner.progress(progress.current, progress.of);
});
preloading.finally(function () {
spinner.stop();
});
});
});
From that point it's just a transformation of the above told.
Have fun fiddling around!
Greetings
Upvotes: 3