8
\$\begingroup\$

I am using Scroll Magic for the first time and I am in general very new to JavaScript/jQuery. I got it to work but it is very messy and repetitive code.

Here is the fiddle. This is the messy code (I just gave this as an example, in my project there is about 50 scenes after each other in this manner!):

var controller = new ScrollMagic.Controller();

var tween1 = TweenMax.to('#animation1', 0.7, {
    opacity: 0
  });
var scene1 = new ScrollMagic.Scene({
    offset: 200,
    reverse: true
})
.setTween(tween1)
.addTo(controller)
.addIndicators();

var tween2 = TweenMax.to('#animation2', 0.7, {
    opacity: 1
  });
var scene2 = new ScrollMagic.Scene({
    triggerHook: 'onEnter',
    offset: 500,
    reverse: true
})
.setTween(tween2)
.addTo(controller)
.addIndicators();

Is there any way I can make this code more DRY?

\$\endgroup\$
4
  • \$\begingroup\$ https://jsfiddle.net/axnnmoy9/ my first guess \$\endgroup\$
    – cske
    Commented May 29, 2015 at 15:20
  • \$\begingroup\$ I keep getting "setTween is not a function" when I edit the JsFiddle. \$\endgroup\$ Commented May 29, 2015 at 16:44
  • \$\begingroup\$ I'm actually getting that error when I simply run the JsFiddle. It looks like ScreenMagic.Scene doesn't have a setTween method at all. I verified that in the browser with Firefox. \$\endgroup\$ Commented May 29, 2015 at 16:57
  • \$\begingroup\$ Thx for your code Greg, im newbie on scrollmagic, i try to copy and paste your code but i don't understood how can triggher the animation \$\endgroup\$
    – Frank
    Commented Mar 4, 2017 at 10:32

1 Answer 1

6
\$\begingroup\$

I'm posting this with an assumption that the JsFiddle is missing something since it throws a "setTween is not a function" error. But given your code...

What you really want is a factory object, which gets provided some config data. Perhaps something in this format:

{
    "tween": {
        "duration": 0.7,
        "options": {
            "opacity": 0
        }
    },
    "scene": {
        "options": {
            "triggerHook": "onEnter",
            "offset": 500,
            "reverse": true
        }
    }
}

Let's just call this "animation factory" AnimationFactory:

function AnimationFactory() {
    this.controller = new ScrollMagic.Controller();
    this.scenes = [];
}

AnimationFactory.prototype = {

    controller: null,

    scenes: null,

    constructor: AnimationFactory,

    createAnimation: function(element, args) {
        element = typeof element === "string"
            ? document.getElementById(element)
            : element;

        var tween = TweenMax.to(element, args.tween.duration, args.tween.options),
            scene = new ScrollMagic.Scene(args.scene.options);

        scene.setTween(tween)
        scene.addTo(this.controller)
        scene.addIndicators();

        this.scenes.push(scene);

        return this;
    }

};

Then to use it:

var animationFactory = new AnimationFactory();

window.onload = function() {
    animationFactory
        .createAnimation("animation1", {
            tween: {
                duration: 0.7,
                options: {
                    opacity: 1
                }
            },
            scene: {
                options: {
                    offset: 200,
                    reverse: true
                }
            }
        })
        .createAnimation( ... );
};

Not you've got a nice clean API to use. But you said there are 50 of them on the page, and I imagine that number could change. Additionally, you have a disconnect between the animations and the elements that require them. Why not marry the two in wedded bliss using HTML5 custom data attributes?

function AnimationFactory() {
    this.controller = new ScrollMagic.Controller();
    this.scenes = [];
}

AnimationFactory.prototype = {

    controller: null,

    scenes: null,

    constructor: AnimationFactory,

    createAnimation: function(element, args) {
        ...
    },

    createAnimations: function(element) {
        var tween = null,
            scene = null,
            elements = element.querySelectorAll("[data-animation]"),
            i = 0,
            args;

        for (i; i < elements.length; i++) {
            args = JSON.parse(elements[i].getAttribute("data-animation"));
            this.createAnimation(elements[i], args);
        }
    }

};

And to use this:

var animationFactory = new AnimationFactory();

window.onload = function() {
    animationFactory.createAnimations(document.body);
};

And a little HTML for good measure:

<h1 data-animation='{
    "tween": {
        "duration": 0.7,
        "options": {
            "opacity": 1
        }
    },
    "scene": {
        "options": {
            "offset": 200,
            "reverse": true
        }
    }
}'>Animation 1</h1>

<h1 data-animation='{
    "tween": {
        "duration": 0.7,
        "options": {
            "opacity": 0
        }
    },
    "scene": {
        "options": {
            "triggerHook": "onEnter",
            "offset": 500,
            "reverse": true
        }
    }
}'>Animation 2</h1>

This gives you unlimited potential. The configs for creating the animations are embedded right in the markup. If different pages had different animations, no need to keep different JavaScript files around. Every page can be different.

Since the AnimationFactory encapsulates all of the animations, you can tweak the work flow behind the scenes if you run into performance problems, like staggering the creation of the Tweens so you don't have 50 opacity tweens run all at once on the page (which I image might draw enough electricity from the CPU to power a small Tesla).

If you AJAX in some HTML, then insert it into a DIV after page load, the AnimationFactory#createAnimations method can be called, and pass in the HTML element that just had its innerHTML replaced and you'll get animations after AJAX, not just on page load.

// xhr is an XMLHttpRequest object that just got some HTML from the server
var div = $("#someDiv").html(xhr.responseText);

animationFactory.createAnimations(div[0]);
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Wow!! Thank you so much for this detailed answer! You helped me tremendously!! \$\endgroup\$
    – Schwesi
    Commented May 31, 2015 at 8:57
  • 1
    \$\begingroup\$ Indeed - it's worth breaking the no-"thanks! me too!"-rule to say, well, "thanks! me too!". This deserved more than an upvote. \$\endgroup\$
    – drew moore
    Commented Jun 20, 2016 at 23:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.