Forum Discussion
GSAP 3.5.1 ( latest version ) is now included in Storyline 360
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...
- StefanKhlerCommunity Member
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!!!
- MathNotermans-9Community Member
Hi Stefan, will check it out and make it work this weekend.
Regards,Math
- MathNotermans-9Community Member
Hi Stefan,
One solution for the position issue on resize i added here.
https://community.articulate.com/discussions/articulate-storyline/gsap-x-y-transformation-in-the-modern-player
- MarkRamsey-ad71Community Member
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!
- MathNotermans-9Community Member
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.
- MarkRamsey-ad71Community Member
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.
- JohnCooper-be3cCommunity Member
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 imagesvar cards = ['Jack.png','Queen.png','King.png'];//random number generatorfunction getRnd(max) {return Math.floor(Math.random()*max);}//locate card by altName and select card at randomfunction 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- MathNotermans-9Community Member
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.
- JohnCooper-be3cCommunity Member
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!
- MathNotermans-9Community Member
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);
- DolandRuizCommunity Member
Hi Math, thanks for posting this, you've helped me before with moving an element. I've now figured out how to rotate but I'm having trouble looping the animation.
The element basically rotates clockwise 12 degrees, then with a cue point I have it rotate counterclockwise -24, then +24 and that's where I want it to loop but it breaks upon timeline looping. I want it to rotate back and forth smoothly and endlessly.
Possible?
- MathNotermans-9Community Member
Hi D R,
For sure possible. Let me first explain what is causing the glitch in your animation.
For a seamless loop, start and end need to be exactly the same. As you end differently then you begin...a glitch will always be visible. So you need to end it exactly the same or change the beginning so it will fit.
Using a Storyline layer,timeline and cuepoints also makes it harder then needed. You can as easily use GSAPs buildin timelines to do this.
So lets see how to get this done perfectly.Approach 1:
Using your Storyline i changed the jump at the end of the layer to go to cuepoint #1.
Prolonged the timeline on the layer a bit, so you see it happening better...
And added a cuepoint where the animation is set to 12 degrees.As you can see when publishing, there is still a glitch when looping. I do think this is SlideLayer and Cuepoint related as Storyline is not precise enough to get this working perfectly.
Approach 2:
Jumping back to 0 is often better for Storyline timelines. And setting the green Blooddrip to 0 at cuepoint 5 shows that the loop now is seamless. But we want it to loop at 12 degrees.. How to do that ?
Approach 3:
Tried to add a Boolean variable (True/False) that will be set to True when the SlideLayers timeline ends...and then at start sets the drip to 12 degrees. At first this failed too... so i changed the trigger you had jumping back on the timeline into 2 triggers...hiding and showing the timeline at the end. This worked well and now it is looping nicely. This fix might work on your original too.
Approach 4:
Using GSAP's timeline to control all. As you can see here...this is really powerfull and easy.. Using yoyo to go back and forth.
Here you can see all approaches and the Storyline changed is attached.
https://360.articulate.com/review/content/b276fadf-093d-4578-8f3f-95daa2d47a41/review
Do hope this helps you to continue your yourney :-)- DolandRuizCommunity Member
Oh there's a "yoyo" function in gsap, nice! Yeah that solution works well. downloading the story file now. Thanks so much.
- AlexBradley-10fCommunity Member
Hey Math, I've read this thread with interest and I am very jealous of what you can do with GSAP/Javascript. I know nothing about either but would like to learn more - can you recommend a good resource to start learning more about GSAP/Javascript?
Thanks
Alex
- MathNotermans-9Community Member
Javascript sources all around the web to find and learn. But the biggest issue is figuring out what Storyline supports and what not... how to debug your code in Storyline and figure out whether something might work or not... i just gave a few weeks workshops for Ideas ,some of them no Javascript knowledge, some more advanced. I am planning advanced workshops for multiplayer games in Storyline in the summer, but thats advanced indeed. If enough people are interested i can plan 1 or 2 online workshops getting started with JS and GSAP in Storyline.
- AlexBradley-10fCommunity Member
I'm a +1 for that workshop Math!