ScrollMagic Fun

Namaste!

Today I’m going to try and introduce you to a great javascript library called ScrollMagic.js. I say try because this library lets you do so many things based on the users scrolling that I’ll just be scratching the surface. For example, this library nicely incorporates the extensive Greensock animation library, which I won’t touch on at all today. 

This tutorial comes from a question on Facebook (as usual). In this case, somebody wanted to know if you could replicate one of Google’s sites advertising Inbox using Divi. On this site, as the user scrolls down the various sections have a header that pins into place when it gets to the top of the screen. As the user continues to scroll, the bottom portion of the section changes. After three-four changes, the header unpins and scrolls up until the next header is pinned. (If this was confusing, take a second to visit the site and you should understand.) There are two components that the person wanted to replicate. 

The first was scrolljacking. This is the practice of allowing the user to scroll just a little while the site automatically advances a whole section. I don’t really like this effect. As a user, it makes me feel like I’m out of control. So, for now, I’m not going to show how this is done.

The second is how to pin the headers and allow the rest of the site to continue scrolling. That is what I will actually show today. I’ve set up a demo for you to try out the end product of what we will be building today. It comes with the usual warning that I have the design sense of a color-blind parakeet on acid, plus I used images already loaded on the site instead of adding new ones to my media library.

First things first, you need to enqueue the ScrollMagic library. There are multiple ways to do this with Divi. However, that isn’t what this tutorial is about. You can use the CDN or add the libraries directly to your site. In my case, I decided to add them directly to my site so I downloaded the package located here. Once you get the library downloaded you need to unzip it and move the necessary files to your child theme folder. For my purposes, I moved the non-minified ScrollMagic.js and plugins folder to a folder named js on my site. (Most easily done with FTP.) Next, I modified my functions.php file in my child theme to enqueue each of the desired js files. 

Again, I’m not going to go through this in detail, but on a live production site I wouldn’t add in the debugging indicators and I would use the minified versions. Additionally, I would probably only load them on certain pages rather than on every non-admin page. If you need help on this step or a more detailed explanation, just reach out by e-mail or leave a comment.

Okay, now we have our scripts ready for use, let’s build our page. In the demo, I have two sections. I left the first section understyled to make it easier to see what is going on in the second section. My overall page layout is simple. A top full-width header followed by two sections, each with a text header and then four images. At the bottom is the all important code module. Let’s start with the first:

In the demo, the full-width header is blue with white text and the first header to pin is red. The actual fields don’t really matter. What is important are the CSS ID we give to the sections. Open the settings for the first section (three lines at the upper left in the dark blue area). Navigate to the Custom CSS tab and give the section an ID of “triggerOne” – the capitalization isn’t important, but it has to match what you use in the code we write later. I have the habit of writing descriptive IDs with each word after the first capitalized. If you pay attention to developing a consistent habit you have less coding problems! Next, open the settings for the header row that will be pinned. In this case the teal colored row at the top of the section. Give it a CSS ID of “pinOne”. Anything you put in this row will now be targeted for pinning. In this case I have a single text module, but it could be any number of modules. 

Okay, let’s jump to the Code module at the very bottom.  Here is where the next bit of ScrollMagic happens. I could just as easily put this in a separate enqueued script, but for ease of the tutorial I’m adding it on page. 

<script>
jQuery(document).ready(function( $ ){
var controller = new ScrollMagic.Controller();
var sceneOne = new ScrollMagic.Scene({
triggerElement: '#triggerOne',
duration: '200%',
triggerHook: .1,
})
.addIndicators({name: '1 (duration: 1000)'})
.setPin('#pinOne', {pushFollowers: false})
.addTo(controller);
/***********************/

var sceneTwo = new ScrollMagic.Scene({
triggerElement: '#triggerTwo',
duration: '250%',
triggerHook: .15,
})
.addIndicators({name: '2 (duration: 1000)'})
.setPin('#pinTwo', {pushFollowers: false})
.addTo(controller);
/***********************/

var sceneThree = new ScrollMagic.Scene({
 triggerElement: '#triggerThree',
 duration: 800,
 triggerHook: .25,
})
.addIndicators({name: '3 (duration: 1000)'})
.setPin('#imageOne', {pushFollowers: false})
.setClassToggle("#imageOne", "appear")
.addTo(controller);
/***********************/

var sceneFour = new ScrollMagic.Scene({
 triggerElement: '#triggerFour',
 duration: 800,
 triggerHook: .25,
})
.addIndicators({name: '4 (duration: 1000)'})
.setPin('#imageTwo', {pushFollowers: false})
.setClassToggle("#imageTwo", "appear")
.addTo(controller);
/***********************/

var sceneFive = new ScrollMagic.Scene({
 triggerElement: '#triggerFive',
 duration: 800,
 triggerHook: .25,
})
.addIndicators({name: '5 (duration: 1000)'})
.setPin('#imageThree', {pushFollowers: false})
.setClassToggle("#imageThree", "appear")
.addTo(controller);
/***********************/

var sceneSix = new ScrollMagic.Scene({
 triggerElement: '#triggerSix',
 duration: 800,
 triggerHook: .25,
})
.addIndicators({name: '6 (duration: 1000)'})
.setPin('#imageFour', {pushFollowers: false})
.setClassToggle("#imageFour", "appear")
.addTo(controller);
});

</script>

So, let’s start at the top. First, we need to wrap everything in a <script></script> set of tags if we are putting it in the code module – this can be left out if you are putting it in a separate file. Next, we have a piece of jQuery that tells the browser not to execute any code until everything is loaded. Finally, we get into the ScrollMagic specific code. First up is to establish a new controller. 

var controller = new ScrollMagic.Controller();

I’ve chosen to name mine “controller”, very imaginative. Typically, we only have to do this once per page. Next, we will add various scenes to this controller. For our first scene we want the header to move up to the top of the page and then pin in place until the last of the images have scrolled past.

var sceneOne = new ScrollMagic.Scene({
triggerElement: '#triggerOne',
duration: '320%',
triggerHook: 0,
})
.addIndicators({name: '1 (duration: 1000)'})
.setPin('#pinOne', {pushFollowers: false})
.addTo(controller);

So, the first thing we call is a new scene named “sceneOne” – you can call it anything you want. Next, we identify where on the page we want our controller to yell “action” ! In this case, at the top of the section that contains our header and images that we gave the CSS ID “triggerOne”. The next variable we pass to ScrollMagic is duration. This can be a whole number like 500, which would mean we want the scene to last until the user scrolled 500px. In this case, I’m using ‘320%’ – this means the scene will last for 3.2 screens of scrolling. Note, if setting the duration in pixels, you use a number without quotes, if you set a percentage it has to be in quotes. You will have to adjust this for every single scene depending on the amount of content. I really, really, really wish that you could specify an end point on the page with another ID, but I haven’t been able to find anything in the documentation. Next, we have the “triggerHook” variable. This marks where in the viewport you want the scene to happen. This is a number from 0 to 1 that represents a percentage down the viewport. So the very top is 0, the very bottom is 1, and the middle is 0.5.

Next, I’m chaining two additional actions onto this base scene. The first is a debuging variable causing the triggers to be labeled. On a production site you won’t want this to be turned on. It is a little hard to see here, but this is the scene trigger (in green) at the top of the red header of the first section about to hit the trigger. My labels are a little off, sorry – I took the screen shots when I had the site put together differently.  The next variable is ‘setPin’, this is the command that tells ScrollMagic that we want to pin the DOM element to the trigger for the duration of the scene. In this case we are pinning the row with the CSS ID of ‘pinOne’.  We are also passing another option to setPin telling ScrollMagic that we don’t want it to push everything else down on the page (you can play with this to see what it does). Finally (!!!) we add the scene to our controller with the ‘.addto’. 

If we now load up our demo, we can see that this causes the first header to pin at the top trigger and the pictures to keep scrolling. (note: I gave the header a high z-index to keep it on top.) When we hit the end of our duration the header becomes unpinned and moves with the scroll. The second section gets pinned in the same way as the first. For demonstration purposes, I moved the triggerHook down slightly so you can see it pin in a slightly different place.

Okay, on the Google page, things didn’t look much like this. The items appeared magically below the pinned headers. Let’s do that! To accomplish this we need a new command called ‘setClassToggle’. The header for the second section pins exactly like the first. We set up a second scene targeting our second section and header. Like the last, in our Divi layout, we give the section (dark blue) a CSS ID of ‘triggerTwo’ and the header row an ID or ‘pinTwo’. The code above shouldn’t need extra explanation. Because I’m styling each image separately, I broke them out into individual rows and I’m giving them CSS IDs of imageOne through imageFour in the module settings (the grey boxes). I’m also giving them a class of ‘hidden’. This allows for the images not to be visible until we want them to appear.

Now we write a scene for each image that pins it in place and adds on a class called appear, that will make it suddenly appear.

var sceneThree = new ScrollMagic.Scene({
 triggerElement: '#triggerThree',
 duration: 800,
 triggerHook: .25,
})
.addIndicators({name: '3 (duration: 1000)'})
.setPin('#imageOne', {pushFollowers: false})
.setClassToggle("#imageOne", "appear")
.addTo(controller);

Overall, it should look familiar. We set up the scene with a trigger, a duration, a location within the viewport for the action to happen, and our pin. To add the class we use our new command, ‘setClassToggle’. For each of the subsequent images we add another, almost identical, scene making sure to change our IDs to be unique.

Last thing is our CSS. I’ve kept it simple, but you can fool around with adding in animation to fade the image in and out.

.hidden {
    opacity: 0;
}
.appear {
    opacity: 1;
}

And that is it! Here is a link to the json file for the basic layout without images.From this, you have a good start on understanding ScrollMagic. The documentation is okay – not exactly beginner friendly. In future tutorials I’ll go through horizontal scrolling and starting with greensock. As usual, if you have any additional question, reach out to me through the comments or by e-mail. I’m glad to help.

Metta!!!

Published: 03.04.2017

In: CSS, Legacy, Programming

31 thoughts on “ScrollMagic Fun”

  1. giles says:

    Hi!
    Thank you so much for your post. I love both Divi + SM, I’ve been trying to get the two to work together for the last few days but have to be honest that I’m hitting some bumpers. Got to a point where I was questioning if it was possible, so your post was a real moral boost to see it in action. Would love to turn to your example but the link appears dead, is there any chance of a fix?

    1. Bob Means says:

      Hey!
      Thanks for the comment. As far as I know, the demo is still working. Is there something specific I can help you with?

  2. giles says:

    Hi Bob, 
    Yeah the demo is fine but your Json download is failing – would like a version that I know is working to help me figure out where I’m going wrong (or come back to you with a clearer question)
     
     

    1. Bob Means says:

      Sorry about the giles. I didn’t understand what you meant by the first comment. I recently changed servers and the new host turned off json upload into the media library. I had to go in and change that the re-upload the file. At any point, it should work for you now.

  3. giles says:

    No worries at all Bob. I’ll give this a run over tonight (UK time) and get back to you shortly. 
    Thanks

  4. giles says:

    Oooooh Bob, I owe you
    Thanks so much for the post & Json. Divi & SM are such great resources, Have been wondering how to bring them together for some time. Kind of hold your breath moment this evening to see it working on my own localhost. And your solution is so … elegant. I’m a big fan of concise, tidy, elegant solutions. This is a great example! Thanks also for your fast response. 
    Robert Means … you rock!
     

    1. Bob Means says:

      No problem, glad you found it useful! Thanks for catching the json problem. I have to go back through my tutorials and see if any others have file download problems.

  5. giles says:

    Hi Bob,
    So I’m trying to add the code to a separate  enqueued script as you touch on above with the following

    function robert_means() 
    {
    	wp_enqueue_script('scroll_magic_library', SCROLLMAGIC205."ScrollMagic.min.js");
    	wp_enqueue_script('scroll_magic_js_ibrary', SCROLLMAGIC205."plugins/jquery.ScrollMagic.min.js");
    	wp_enqueue_script('scroll_magic_indicators', SCROLLMAGIC205."plugins/debug.addIndicators.min.js");
    	wp_enqueue_script('actual_anim', "scrollmagic-fun.js");
    }
    add_action( 'robert_means', 'robert_means' );

    I know the first 3 lines work because they are exactly the same as your example running on my localhost. I’m trying to enqueue scrollmagic-fun.js which contains your code. I’m a little unsure how to open  …. ive tried

    jQuery(document).ready(function( $ ){

    and

    $(document).ready(function(){

    and

    (function($){$(function(){

    which work in other examples that I have running (e.g )

    $(document).ready(function(){
    
    	// Init ScrollMagic
    	var controller = new ScrollMagic.Controller();
    
    	// build a scene
    	var ourScene = new ScrollMagic.Scene({
    		triggerElement: '#project01'
    	})
    	.setClassToggle('#project01', 'fade-in') // add class to project01
    	.addIndicators({
    		name: 'fade scene',
    		colorTrigger: 'red',
    		indent: 50,
    		colorStart: '#75C695'
    	}) // this requires a plugin
    	.addTo(controller);
    
    }

    and

    jQuery(document).ready(function( $ ){
    
    	// init controller
    	var controller = new ScrollMagic.Controller();
    
    	var redbox = new TimelineMax();
    	redbox.from("#animate1", 0.5, {
    		backgroundColor: "red", 
    		scale: 2.5
    	})
    
    	var scene = new ScrollMagic.Scene({
    		triggerElement: "#trigger1"
    	})
    	.addIndicators({name: "1 (duration: 0.5)"}) // add indicators (requires plugin)
    	.addTo(controller)
    	.setTween(redbox); // trigger a TweenMax.to tween
    
    });

    but fail when I try wrap your example in them … so how would you go about enqueueing your script in a separate file?

    1. Bob Means says:

      Hey Giles,

      So, let’s walk through your code. The first function ‘robert_means()’ is being added with an

      'add_action( 'robert_means', 'robert_means' );'

      The format for the ‘add_action’ method is to first give a hook where you want the function you name as the second parameter to be added. So,

      add_action( 'wp_enqueue_scripts', 'robert_means' );

      Next, within this same function, you are pointing to four different scripts. You are prepending ‘SCROLLMAGIC205’ to the first three. This will not be decoded as a proper web location unless you have defined it as a variable some place else in your functions.php file. I don’t know your directory structure, so I can’t tell what you should prepend. For your fourth script you aren’t prepending anything, so I don’t think it can be loaded at all.

      Now, with regards to wrapping your javascript, there are a couple of different ways. The one that I have found causes the least problem is:

      jQuery( document ).ready( function($){
      /* insert your code here
      })(jQuery);

      You need to start with jQuery since WordPress is running strict. The you inject the ‘$’ so that you can use that instead of typing jQuery within the function, and then you add the jQuery at the end to define the ‘$’.

      If you use the Chrome inspector tools, can you see if you are getting any errors in the console? Also, can you see if your scripts are being loaded?

      Good luck – ask any additional questions you need!

      Namaste – Bob

  6. giles says:

    Brilliant Bob – Working like a dream (mixture of all 3 I think).
    As a means of tidying up,  … I’m thinking about add_action() … right now I have a bunch of them in the root of my ScrollMagic directory (forming a “index” of all the scrolls on my site). I’m now questioning the value of working this way as I guess all of them will be evaluated on each page load ? I’m trying to think how I might improve this. Is there a way that I can bind a [shortcode] to a specific filepath? (thereby bypassing an index of scrolls?)
     
    Again, thanks for all your feedback Bob. I’m going to work on a couple of projects then post you some links so you can see the fruit of all your kind energy.

    1. Bob Means says:

      Glad I could help. I’m not sure quite what you mean about an “index” of scrolls. Do you mean that you are adding a separate script for every ScrollMagic element, or…? Going forward, it is worthwhile modifying your php to only load in the scripts that you need per page. There are a number of ways to accomplish this. I guess that is what you mean by a shortcode to a specific filepath?

  7. giles says:

    Yes exactly that how do I send a shortcode to a specific path 

    1. Bob Means says:

      Sorry I didn’t get back to you more quickly.

      If you are only bringing some javascript into the page, you can easily set it up in your functions.php. Just use

      add_shortcode( 'load_this_script', 'load_the_script' )';

      function load_the_script() {
      wp_enqueue_script( 'the_script', '/path/to/js/file.js' );
      }

      You would make up a shortcode for each of the different scripts you need to load. The 'path/to/js/' could be set to a variable of course.

      Does this help?

  8. giles says:

    Hi Bob, 
    I was just wondering if you got to a point of posting how to include GSAP in the above context?
    Thanks

  9. giles says:

    I’m asking because I have the following scroll, based on your example, which works great

    jQuery(document).ready(function($){
    
    	var controller = new ScrollMagic.Controller();
    
    	var sceneOne = new ScrollMagic.Scene({
    		triggerElement: '#triggerOne',
    		duration: 1200,
    		triggerHook: .1, // ** @0 THE TRIGGER CAN BE HIDDEN **
    	})
    	.addIndicators({
    		name: 'Block 1'
    	})
    	.setPin('#pinOne', {pushFollowers: false})
    	.addTo(controller);
    
    })(jQuery);
    

    I’m linking to the GSAP reop within ScrollMagic

    <?php
    	wp_enqueue_script('TweenMax', SCROLLMAGIC205GSAP."TweenMax.min.js");
    	wp_enqueue_script('TimelineMax', SCROLLMAGIC205GSAP."TimelineMax.min.js");
    
    	wp_enqueue_script('scroll_magic_library', SCROLLMAGIC205."ScrollMagic.min.js");
    	wp_enqueue_script('scroll_magic_js_ibrary', SCROLLMAGIC205."plugins/jquery.ScrollMagic.min.js");
    	wp_enqueue_script('scroll_magic_indicators', SCROLLMAGIC205."plugins/debug.addIndicators.min.js");
    	wp_enqueue_script('anim', SCROLLMAGIC."mine/012_pinning_01/index.js");
    ?>

    adding the tween (as I’ve seen in multiple examples)

    jQuery(document).ready(function($){
    
    	var controller = new ScrollMagic.Controller();
    
    	var sceneOne = new ScrollMagic.Scene({
    		triggerElement: '#triggerOne',
    		duration: 1200,
    		triggerHook: .1, // ** @0 THE TRIGGER CAN BE HIDDEN **
    	})
    	.setTween("#gsapText1", 0.5, {scale: 2.5})
    	.addIndicators({
    		name: 'Block 1'
    	})
    	.setPin('#pinOne', {pushFollowers: false})
    	.addTo(controller);
    
    })(jQuery);

    but I’m getting Uncaught Type Error : (intermediate value).setTween is not a function – any ideas what I might be doing wrong?
     
    Many thanks
     
     
     
     
     

    1. Bob Means says:

      You don’t seem to be loading the GSAP plugin for Scroll magic.
      Using your syntax above, I’m guessing you have to add:
      wp_enqueue_script('scroll_magic_gsap_ibrary', SCROLLMAGIC205."plugins/plugins/animation.gsap.js");

  10. giles says:

    LoL – So I seem to have been making every Newbie mistake in the book Bob, but thank you so much for your feedback. Happy to say this works just great now.
    Thank you!

    1. Bob Means says:

      LOL! You’re welcome!

  11. giles says:

    Hi Robert,
    Wondering if I can pick up on our recent conversation as I’m coming up with something that might not be so common outside of the niche of deploying ScrollMagic via WP shortcodes ? – goes like this ….
    I’m getting increasing practise with tweens, and scrolls and embedding them within a divi module … all good!. Trouble is that divi is so flexible I now want to place multiple instances of ScrollMagic (e.g. multiple shortcodes to multiple Scrolls) in my page !!! (dont you love overkill!!) and that’s where I’m hitting issues. I’m at a point where the first Scroll operates as expected (identifiers on page, scroll fires etc), the second scrolls’ SVG appears on screen but without identifiers and does not fire – so I’m guessing there is a problem with a second declaration ScrollMagic.Controller();. ???
    Here’s my Scrolls you’ll see that I’ve created separate instances which usually gets me out of trouble

    As before … you input would be brilliant … thank you 🙂
     

    1. Bob Means says:

      Hi Again,

      I kindda understand the trouble you are having, but I would have to see some of the code you are using now. Either a link or paste some code here.

      Cheers!

  12. giles says:

    Hi,
    Interesting I pasted in links using your [INSERT] trying again … 

    link for image above https://i.imgur.com/yfTFtEd.jpg
     

    link for image 2 above https://i.imgur.com/mrYK3Xa.jpg
     
    thanks
    Giles

    1. Bob Means says:

      Hmm – I might have to look at the page. Do you know if both scripts are getting loaded? I can’t tell if they are named the same or not – sort of looks like they are both named index.js?

  13. giles says:

    Thanks for your comment. You were right in that both were named the same (but in different directories), but realise this could cause issues in the DOM.
    Still have issues but stumbled over this https://ihatetomatoes.net/wp-content/uploads/2016/08/ScrollMagic-CheatsheetV2.pdf  wondering if its a matter of removing or updating the controller. Get your comments on putting the page up (will look into shortly)
     

  14. giles says:

    Hi 
    Sorry for brevity of last response (Kids on vacation calling on “Dad’s Cabs”)
    Looking at the above … start to wonder if I’m not updating the controller correctly on the second scroll??
    I’ve been working to date on my localhost, now putting the page & domain up for you to take a look at – back with you shortly ……
     
    thanks
    Giles 
     

    1. Bob Means says:

      Cool – haven’t had time to dig in properly. I’m in admin page hell – having a hard time implementing an upload option that replaces a specific file.

  15. giles says:

    sorry to hear you’re news of upload hell – do you mean “upload this …. replace that?” (happy to try help if I can in any way)
    so I’ve created the domain and published my example to http://thewebmadesimple.com/dual-scrollmagic/ realise you are in a stress point right now but would love your feedback when possible
    best 
    G
     
     

    1. Bob Means says:

      Hey Giles,

      Neat demo page! I just took a look and don’t see index2.js loading. How are you bringing the scripts into your page?

  16. giles says:

    made up that you like the demo page.
    I’ve updated it with (what I hope) answers your point. The thing I’d be inclined to repeat is that scroll2 works if that I disable scroll1 in divi … so something about scroll1 is stopping scroll 2 from loading ???

  17. giles says:

    Robert!  I’ve cracked it! Answers on the demo page! (like so many times before, the answer was sitting there looking at me … just had to make enough mistakes to discount everything else before seeing it). Check it out when you have time. Big relief … now moving on with a little more creative thinking.
    (as I mention in the post … really appreciate the chance to kick this around with you)

    1. Bob Means says:

      Ahh, gotcha! The handles for each were the same! Wow, good catch. Sorry I wasn’t a little more helpful.

  18. giles says:

    No worries – just appreciated having an outside opinion.
    Thanks
    Please shout if there is anything I can do in return & will shout you with link to my portfolio page when complete
    Cheers

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.