Potential Bug in plugins/animation.gsap.js
Version
-
ScrollMagic: 2.0.8 -
Gsap: 3.5.1
I am using ScrollMagic with React and webpack. Tricky configurations are needed to get the code compiled with webpack but I think the main problem lies in the source code itself.
Issue
I appreciate all the efforts made by the development team to accommodate different versions of gsap in animation.gsap.js. Since the latest version of gsap wraps TweenMax, TimelineMax, TweenLite, TimelineLite, etc. into the gsap object, the current version of animation.gsap.js doesn't work with the lastest gsap@^3.5.1.
All the following code snippets are taken from scrollmagic/uncompressed/plugins/animation.gsap.js.
The error is related to the function Scene.setTween. The following is the error message shown in the console when setTween is called:
TypeError: Tween.to is not a function (animation.gsap.js: 255)
Backtracking where the variable Tween is initialized, I found the following chunk of code which caused the problem:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['ScrollMagic', 'gsap', 'TweenMax', 'TimelineMax'], factory);
} else if (typeof exports === 'object') {
// CommonJS
// Loads whole gsap package onto global scope.
var gsap = require("gsap/dist/gsap") || require("gsap");
// TweenMax/TimelineMax will be global in v2. In v3, they will be on the gsap object
factory(require('scrollmagic'), gsap, TweenMax || gsap, TimelineMax || gsap);
} else {
// Browser globals
factory(root.ScrollMagic || (root.jQuery && root.jQuery.ScrollMagic), root.gsap, root.gsap || root.TweenMax || root.TweenLite, root.gsap || root.TimelineMax || root.TimelineLite);
}
}(this, function (ScrollMagic, Gsap, Tween, Timeline) { ... }
(lines 28-43 in animation.gsap.js)
This is basically where the gsap module is loaded. In the latest version of gsap, objects like TweenMax and TimelineMax are wrapped as a member of the gsap module so the gsap object is not equivalent to either TweenMax or TimelineMax. This is why when Tween.to is called later, it is not a function (gsap object doesn't have a key called to).
To solve the issue, I modified the above code as below:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['ScrollMagic', 'gsap', 'TweenMax', 'TimelineMax'], factory);
} else if (typeof exports === 'object') {
// CommonJS
// Loads whole gsap package onto global scope.
var gsap = require("gsap/dist/gsap") || require("gsap");
// TweenMax/TimelineMax will be global in v2. In v3, they will be on the gsap object
factory(require('scrollmagic'), gsap, TweenMax || gsap.TweenMax, TimelineMax || gsap.TimelineMax);
} else {
// Browser globals
factory(root.ScrollMagic || (root.jQuery && root.jQuery.ScrollMagic), root.gsap, root.TweenMax || root.TweenLite || root.gsap.TweenMax || root.gsap.TweenLite, root.TimelineMax || root.TimelineLite || root.gsap.TimelineMax || root.gsap.TimelineLite);
}
}(this, function (ScrollMagic, Gsap, Tween, Timeline) { ... }
After fixing this, there is another bug in line 275 of animation.gsap.js:
if (parseFloat(TweenLite.version) >= 1.14) { ... }
Here, TweenLite is not defined before so you will get a undefined error in the console. To solve it, just replace TweenLite with Tween.
How to reproduce the issue
- Load the scripts for
ScrollMagicandgsapof the above version in the page. - Initialize a controller, create a scene with
setTween, and add the scene to the controller. - Load the page in the browser (I'm using Chrome) and you should see the error.
Part of the code is pasted below for reference:
const controller = new ScrollMagic.Controller();
const scene = new ScrollMagic.Scene({
triggerElement: '#demo',
duration: '100%',
reverse: true
}).setTween('#animate-element', {
// some css properties
}).addTo(controller);
I'm having a similar problem. Here is the code and error msg.
Uncaught TypeError: Cannot read property 'to' of undefined
at O.Scene.f.setTween (animation.gsap.js:1)
at HTMLDocument.
$(function() { // wait for document ready // init var controller = new ScrollMagic.Controller({ globalSceneOptions: { triggerHook: 'onEnter', } });
// get all images
var images = document.querySelectorAll(".image");
// create scene for every image
for (var i = 0; i < images.length; i++) {
new ScrollMagic.Scene({
triggerElement: images[i]
})
.setTween(images[i], 0.5, {scale: 1.1})
.addIndicators()
.addTo(controller);
}
});