GSAP 3.5.1 ( latest version ) is now included in Storyline 360

Nov 22, 2020

A few months ago i discovered Storyline uses the GSAP Tweening library for all its animation features. Any animation you make in Storyline somehow is triggered by GSAP under the hood of Storyline. Then i noticed that Articulate still was using TweenLite a very old and limited version of GSAP... back from 2014 :-)

Well today i discovered Storyline updated the GSAP libraries and from this day on you can animate your objects with GSAP javascript code.

For example:
Give any object in your Storyline the 'accessibility name' of "myName"
Then add a trigger to call this Javascript:

var myElement = document.querySelectorAll("[data-acc-text='myName']");
gsap.to(myElement, {duration: 2,scaleX:2,autoAlpha:0.5, x: "+=500"});
Your Element will move and fade and scale over the x-axis.

Really great news Articulate acted and updated GSAP.
I will check whats possible with the built-in GSAP code and what not and show that here...

60 Replies
Stefan K.
Math Notermans

You can animate any page element with GSAP. Images, shapes, Storyline UI-elements...even create elements from scratch. Anything is possible. Yes, you can zoom in/out of image. scaleX, scaleY or scale will do that. To achieve a zoom-effect you need to keep the 'mask' of an object static... or scale down probably..

Check starting with my posts..

I would love to start a tutorial series to help anyone with GSAP in Storyline...not sure how to post and maintain that on the community though...

Since I have no idea exactly how GSAP works, I have a favor to ask:

Could you show me with an example how zooming and moving an image at the same time works? You can find an example in the attachment.

I would then like to try to deal more precisely with the topic using your code...

Would be SUPER, if that would work!

Many thanks!

Math Notermans

Flip, MorphSVG and those plugins are not default in GSAP 3. You have to get a Greensock Club account ( i have a Shockingly Green account since years ) and then you get access to all the plugins and can add them to any project. It for sure is worth any penny and i use them on a daily base. Personally love MorphSVG, DrawSVG, ScrollTrigger and MotionPath Plugin. Use those to create quite complex animations and interactivities in Storyline.

PS. GSAP is now at version 3.6 versus the one in Storyline360 is at 3.5.1. Im not sure whether Articulate plans to keep the versions synced and updated. If not you at all times when using plugins need to watch carefully a plugin uses the proper GSAP... as Flip eg. is new with version 3.6 it might not work with the default GSAP library in Storyline360 and you might need to add the uptodate version of GSAP.

Stefan K.

Hi Math,

I have been intensively dealing with GSAP in the last few days and have to say: Great solution!

Now I have encountered a problem: When I animate elements e.g. like this:

var tl = gsap.timeline();

var icon= document.querySelectorAll("[data-acc-text='icon']");

tl.to(icon, {repeat: 10, yoyo: true, ease: "power1. inOut", duration:2, repeatDelay: 0.25, scale:1.45 });

Then I have the problem: As soon as I change the screen size form my browser or change the format on the mobile (e.g. to landscape format), the animated elements are suddenly in completely different positions or not visible at all. Is there a way to solve the problem?

 

Math Notermans

Yeah that is the position issue Owen mentioned before. Several fixes for that... if you refresh your browser after the change of size...well the positions get updated properly...but it might that thats not what you want.
Some of the other options are...
- using fixed elements in the Storyline for reference positions ( if you check my samples you find solutions for that )
- use vw (=viewportwidth) and/or vh (=viewportheight) for your animations. That is relative and thus can work properly.
- add a eventHandler for the browserchange.. JQuery has resize( ) for that... in Vanilla Javascript something like this works too..

var wrapper = document.getElementById('#wrapper');
window.addEventListener("resize", function(){
if(window.innerWidth < 768){
console.log('narrow');
}
else{
console.log('wide');
}
});

I attach a older sample that has some of the functionality needed in it. Not all, but with some puzzling and downloading some of my samples you should be able to figure it out. When time permits i mock up a new fully functional sample. At the moment no time alas.

Stefan K.

Hi Math,

I have looked at your examples, but haven't really made any progress yet. Here my previous knowledge is simply not far enough.

I looked around in the Greensock forum and got the following hint:

window.onresize = () => {

timeline.progress(0) // go back to before the timeline progress has moved
gsap.killTweensOf( '.target' ) // kill ongoing tweens

// re-init your animation
// if you initially have a function that sets up your timeline, you can just called it here
}


But unfortunately it didn't help.

Could you show me how you would proceed using the attached example?
The system use vw (=viewportwidth) and vh (=viewportheight) I have unfortunately not yet understood correctly.

Many thanks!!!

Mark Ramsey

Thanks Math for the information on GSAP.  I couldn't help but notice that on the GreenSock site, these GSAP calls first parameters are CSS pseudo-classes, but here in Storyline the first parameter are IDs. Why do you suppose the implementation is different here— or am I missing something? Thanks!

Math Notermans

Thats due to the fact that when you add elements into Storyline...you donot know what ID it might have...and Storyline uses some weird obsfuscating ( if thats the correct word ;-) ) principles that make the final image have a totally unlogical ID and name. So the easiest way to select an image you add in the Storyline-editor is using the 'acc-name'. In fact there are several other ways to get any element in Storyline. I made functions in my reusable code that gets ALL elements on screen in Storyline and puts them in arrays for text, images, video etc. etc. Also the order in the timeline ( from top to bottom ) is also a way of selecting them.. but as said without going to deep into detail...the 'acc-names' are the easiest way.

Mark Ramsey

Yes, I see that the JavaScript returns  a NodeList when we use "data-acc-name". For a single match the Nodeline has one index, and you can access the value without having to use index syntax. I've found that the only ID (lane name, not layer name) that Storyline reports correctly for GSAP purposes is when you import a graphic with a contiguous name (no spaces) , and with no special characters. For all other Storyline objects - text, shapes, etc. - Storyline creates an internal lane name (with spaces) & we have to go through & explicitly set IDs like how you outlined, by iteravely stepping through the NodeList & setting IDs.

I wonder if it would make sense to do (as much as possible) a wildcard search for all objects on a slide, then iteratively set all IDS with values like Object01, Object02... the thing is that's not a very good descriptive way of settings IDs.

Perhaps if there was a way to pull in the "IDs" that get assigned to objects by Storyline - the ones with spaces in their values - and use JavaScript to pull these substrings out. That way, the intent of how Storyline set these values would still make sense (for example, "Star 1" could become "Star1", and thereby become a legal, useful ID). It doesn't look like Storyline assigns any ID at all to these objects. I don't know what the lane name is used for, but it must be a non-accessible value used only in the Storyline interface.

Storyline uses React as a framework; I noticed that the term "data-reactid" is embedded all the way through the SVG-rendered "Star 1" innerHTML I was playing with. The generated "data-reactid" value is totally obfuscated though- I was hoping there might be a key to that value that could be used to de-obfuscated & render the "Star 1" name, but if there is one, I didn't see it. Ah well.

Math Notermans

Might help you a bit this. I added my function to get All elements in Storyline to this one.
https://360.articulate.com/review/content/a8862298-c693-4955-98f8-3f452fc73e17/review
Will add the Storyline itself here too.

What does it ?

Onscreen there are images, shapes, texts and some buttons setup. Some basic Storyline elements. When you click 'GetElements' my function that gets all elements onscreen in Storyline gets executed and shows them in the debugData window. All elements get checked and passed into appropriate arrays from which you can work with them. The red shape ( accname= 'END' ) stops the processing of the elements. All on top of it in the timeline get ignored.

I have this script in my 'generic_functions.js' so i can call it any time.

Added that the data-model-id of elements is captured too ( of shapes at least ) and in this variable..
let elId =el.getAttribute('data-model-id');
And for images the real id of the element gets captured too.
el.getElementsByTagName('image')[0].getAttribute('id');

This way you could change the ids for real helpfull ids...or as you know them now, use them in your coding.

Kind regards,
Math

Math Notermans

Hi Mark,
As you mentioned you had trouble targetting elements on a SlideLayer with GSAP.

Well basically...the SlideLayer needs to be visible.
Check this...
https://360.articulate.com/review/content/ad4e6e6c-c111-43d2-bdb9-23b843013d5f/review
And when its visible..you can use the same scripts as before to target it.

Click the button several times... SlideLayers show and hide appropriately.

Sample added...

Kind regards,
Math

PS. if you scale the screen after it loaded... the x and yPosition are off. Thats due to the Modern Player positioning issue. The yPos of the element is determined by using GSAP getProperty( ) of an element. You can fix that by calling getProperty again after a screenresize.

Math Notermans

:-) Im more active on LinkedIn. https://www.linkedin.com/in/mathnotermans/
YouTube and Vimeo i just use at times to put the videos somewhere.

Getting things to work crossdevice is not really the aim of the link you shared. That one is more about multiplayer game functionality. So what you are interested in ? Multiplayer games ? Or multidevice options....

John Cooper

Hi Math

I really appreciate the time you take to answer questions here - and you are clearly, the man to go to for advice on GSAP.

I'm new to GSAP but I like the potential it creates for developers in Storyline. I'm starting with small steps and it's looking good, I can move images, add effects etc.

A quick question though, if I may post it here? I'm assuming you can't change the 'src' property of an image in Storyline? I saw your post on dynamically loading a QR code - so I'm guessing if it had been possible to dynamically load an image from Storyline using GSAP by changing the src  you would have gone that route?

This code works in straight JavaScript:

//Create array of links to images
var cards = [
    'Jack.png',
    'Queen.png',
    'King.png'
];
//random number generator
function getRnd(max) {
  return Math.floor(Math.random()*max);
}
//locate card by altName and select card at random
function selectCard(){
var cardImage01 = document.querySelector('img[alt="card01"]')
console.log (cardImage01);
gsap.set(cardImage01, { attr: { src: cards[getRnd(3)] } });
}
Here it is used on a test page:
 
I have three image files - the Jack Queen and King of spades. Clicking the button calls a random number generator and selects the card image at random from the array "cards" which contains the URL's of the image files (they are in the root folder).
 
If I put that on a button in Storyline - it doesn't work. If I replace the set command with a to and move the image it moves it - so I know it locates the image on the screen - but I'm guessing it restricts the functions available?
 
Any advice would be appreciated... Thanks
Math Notermans

In fact you can change the src image of any 'real' image in Storyline on runtime. I do think i have samples showing that....

https://community.articulate.com/discussions/articulate-storyline/how-to-create-dynamic-texts-links-and-dynamic-images-in-storyline

The url above shows a bit of it. In the end i ended up generating the QR's dynamically as they needed added customizable querystrings.

In the end there are 2 solutions to creating / changing images with Javascript. One is using a WebObject to point to some URL ( i use Cloudinary, so you can also allow users to upload images and change them on the fly ). Then an action can change the url/src of the image/WebObject.

Another one is adding multiple images upfront to your resources and then you can switch them on an action. You need to setup a array for them... like this...
var urlArray = [
'story_content/external_files/avatar1.png',
'story_content/external_files/avatar2.png',
'story_content/external_files/avatar3.png'
];
Then you can either add them to the viewport...or change the href of an existing image on screen by one from the array.


Do share a sample and i will see if i can help better.

Do try GSAP to change the src... it will work.

John Cooper

Hi Math

Thank you for such a swift response  and your comments are (as usual) very helpful. I will post a sample of what I'm trying to do once I have it a bit further forward - your offer of help is appreciated. Basically, what I'm trying to do is enhance a 'card game' style quiz I actually developed years ago (the original was in Storyline 1!).

The learner has 60 seconds to recognise as many items as possible - in my original demo it was hazard warning signs, So there is a grid of 10 cards the timer starts when the first card is turned and the learner can turn the cards in any order they like. Once the card is turned, the learner has to enter what the hazard is. The card stays turned.

This works fine. But I have always meant to make it so the cards are randomised - say selecting 10 images for the reverse of the cards from a database of 20.

My first attempt was to store 20 URL's in an array cardURLs [] and then randomly select 10 numbers from 20 and use these to load the randomly selected image URL's needed for this pass of the quiz into another array cards[]. My thinking was to have a placeholder image on the reverse side (turned state) of each card and then use a loop to change each one with something like this:

var cardImage01 = document.querySelector('img[alt="card01"]')

gsap.set(cardImage01, { attr: { src: cards[1] } });

etc....

As you probably realise that turned out to be problematic - I couldn't get the gsap.set command working with "src" and changing the state of an image is definitely beyond me - and maybe the capabilities of gsap.

My second attempt was to just put 20 images off-screen and then randomly select 10 from 20 and move the images (with 0 opacity) to the right position so they can be made visible when the card is turned. 

I then ran into the positioning problems with a scaleable screen - something you have also added much useful guidance on here on this site. So what I'm doing now is trying to use relative positioning or percentages so I can get my 10 randomly selected images in the right place on ANY screen size. Once I do that I will definitely be back with a sample story with my next set of problems!

Math Notermans

Easiest solution would be...use almost invisible elements with the proper size and positioning. Use gsap.getProperty to get their x and y pos ( if you want to take into account possible scaling of the window after initialization you need to add eventHandlers for that ) and randomly position your elements.

I do think i made a similar setup quite some time ago. Will check for it..

var myElement = document.querySelector("[data-acc-text='someAccName']");
var elXPos = gsap.getProperty(myElement, "x", "px");
var elYPos = gsap.getProperty(myElement, "y", "px");
console.log("x: "+elXPos+" | y: "+elYPos);