Jump to content
Search Community

Search the Community

Showing results for 'overwrite'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • GreenSock Forums
    • GSAP
    • Banner Animation
    • Jobs & Freelance
  • Flash / ActionScript Archive
    • GSAP (Flash)
    • Loading (Flash)
    • TransformManager (Flash)

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge

Categories

There are no results to display.


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Personal Website


Twitter


CodePen


Company Website


Location


Interests

Found 1,412 results

  1. Hmm.. So I tried the Attribute Plugin using this code, but it doesn't work with my SVG. let elements = document.querySelectorAll('.button'); let clickEvent = (e) => { console.log('some event content here...') console.log("----------------" + e.target.id + "----------------------") } elements.forEach((item) => { item.addEventListener("mouseenter", ()=>{ gsap.to(item, {attr: {fill: "#F2653A"}, duration: 1, overwrite: "auto" }); }); item.addEventListener("mouseout", ()=>{ gsap.to(item, {attr: {fill: "red"} ,duration: 1, overwrite: "auto" }); }); item.addEventListener('click', clickEvent) }); });
  2. Hi, I'm in this situation and I don't know how to manage the fact that MotionPath animation takes control on the translateX property when reversing the scroll ( go to the very end of the scroll animation, then reverse scrolling back, you will see the circles going to the straight right instead of doing the bending curve as they did in forward mode ). `overwrite:auto` does not seem to help, and `overwrite:true` breaks the animation. TIA.
  3. Note: This page was created for GSAP version 2. We have since released GSAP 3 with many improvements. While it is backward compatible with most GSAP 2 features, some parts may need to be updated to work properly. Please see the GSAP 3 release notes for details. Note: the ActionScript version of the GreenSock Animation Platform still works great and you're welcome to use it, but it is no longer officially supported. Our customer base made it very clear that JavaScript was the future of web-based dynamic animation, and we have been focused there for years. Please see the JavaScript Getting Started Guide for more information. Quick links Introduction Installing the code Importing Basic tweening with TweenLite Special properties Plugins Overwriting other tweens Controling tweens Which class do I use? TweenLite? TweenMax? TweenNano? Building a sequence with TimelineLite Need help? Introduction Animating with code may seem intimidating at first, but don't worry - you'll get the hang of it quickly. The GreenSock Animation Platform (GSAP) was engineered to make it simple and intuitive. For now, we'll focus on getting you up and running with the core engine, TweenLite, and then we'll discuss if and when you might want to put the other tools to work for you (like TweenMax, TimelineLite, TimelineMax, etc.). Installing the code Go to your account dashboard page and click the AS2 or AS3 link in the downloads area to download a zip file containing the entire GreenSock Animation Platform in the language you specified. Unzip the file and you'll see a folder containing several swfs, documentation, and a folder named "com" - that's the critical one. Take that "com" folder with all its contents and drop it into the same folder as your FLA file (or if you're an advanced user, set up a classpath to wherever you want). Make sure that you leave the directory structure inside the "com" folder in-tact; it has a "greensock" folder with several ActionScript files inside, along with a few subdirectories. You can throw away the swfs from the zip download and the documentation, etc. if you want. The only critical files are inside that "com" folder. When you publish your swf, Flash looks for that "com" folder, reads the code from inside of it, and embeds it into your published swf. You do NOT need to put the "com" folder on your web server. Once the swf is created, it is completely independent and has no dependencies on the class files because they have been embedded into the compressed swf. Your FLA file has the dependencies, not the swf. There's a great ActiveTuts article here about using 3rd party tools in your Flash projects and it covers some of the more advanced installation/configuration options. Importing In order for Flash to understand what you mean when you type "TweenLite" (or "TweenMax" or any of the GreenSock classes), you must tell it where to find the class file(s). That's what an import statement does. It acts as a pointer that tells Flash where it should look. After all, there could be a completely different "TweenLite" class that another author created, and you need a way to tell Flash which one you're talking about. Typically you put your import statement at the top of the frame or the custom class you created. And, yes, just like any class, you must add the import statement to all frames or classes that contain code referencing it. This does not add extra kb to your file every time you import it. Flash is smart enough to embed it once and all the import statements just act as a "pointer" to the embedded class. To import just the TweenLite class, do: import com.greensock.TweenLite; To import TweenLite and TweenMax, do: import com.greensock.TweenLite; import com.greensock.TweenMax; To import all of the classes in the com.greensock package (don't worry, Flash will only embed the classes that you actually use in your code), do: import com.greensock.*; You'll probably also want to import the easing classes as well (we'll talk more about them later), so this is code that you should get used to putting at the top of your frames or class files because it covers almost everything you'd need and it's shorter than typing out each class every time: import com.greensock.*; import com.greensock.easing.*; Basic tweening with TweenLite Each tween you create needs a target (the object you want to tween), the duration of the tween (typically described in seconds), and the properties that you want to tween, along with their corresponding end values. Let's say, for example, you have a MovieClip named "mc" and you'd like to tween its x property to a value of 100 (sliding it across the screen) over the course of 1.5 seconds. You can use TweenLite's to() method to do it: TweenLite.to(mc, 1.5, {x:100}); The first parameter is the target, the second is the duration, and the third is an object with one or more properties that correspond to your target object's properties. Since it's a to() tween, you're telling TweenLite to tween from whatever the x property happens to be at the time the tween begins (now in this case), to a value of 100. If you want to also tween the y property to 200 and the alpha property to 0.5, you'd do: TweenLite.to(mc, 1.5, {x:100, y:200, alpha:0.5}); There is no limit to the number of properties you can tween. And TweenLite can tween any numeric property of any object, not just a predetermined list of DisplayObject/MovieClip properties. Since there's an AS2 version as well, you can simply change the property names to reflect their AS2 equivalents, like: TweenLite.to(mc, 1.5, {_x:100, _y:200, _alpha:50}); Here's an interactive demo that allows you to build tweens yourself and see the corresponding code at the bottom: There is also a very useful from() method that allows you to define the starting values in the tween and go backwards. So the current values will be used as the end values, and the ones you define in the tween will be the starting values. This makes it easy to, for example, set things up on the stage where you'd like the objects to end, and then animate them into place. Let's say your mc object's y property is at 200 and alpha is at 1, and you'd like to have it "drop" into place from above while fading in over the course of 1.5 seconds, you could do: TweenLite.from(mc, 1.5, {y:0, alpha:0}); If you prefer a more object-oriented approach and/or would like to store references to your tweens in variables so that you can control them later (for example, pause(), resume(), reverse(), restart()), you can create a tween like this (which is identical to a to() tween): var myTween:TweenLite = new TweenLite(mc, 1, {x:100, y:200, alpha:0.5}); Special properties A special property is a reserved keyword that TweenLite recognizes and handles differently than it would a normal property. One example is delay which allows you to delay a tween from starting until a certain number of seconds has elapsed. For example, this tween will wait 2 seconds before beginning: TweenLite.to(mc, 1, {x:100, delay:2}); TweenLite recognizes several special properties that are quite useful, like onComplete, ease, overwrite, paused, useFrames, immediateRender, onStart, onUpdate, onCompleteParams, and more. Please read the full documentation for details. Two of the most common special properties you'll likely use are ease and onComplete. To alter the rate of change during a tween, you can choose from many different easing equations from either the com.greensock.easing package or Flash's own easing classes or Robert Penner's. The interactive demo above allows you to chose different equations and see how they affect the tween. The onComplete special property gives you a way to call any function when the tween completes, making it simple to create a chain of events. Here is a tween that uses the Elastic.easeOut ease, delays its start time by 0.5 seconds, and calls myFunction() when it completes: TweenLite.to(mc, 1.5, {x:100, ease:Elastic.easeOut, delay:0.5, onComplete:myFunction}); function myFunction():void { trace("tween finished"); } Plugins Think of plugins like special properties that are dynamically added to TweenLite (and/or TweenMax), giving it extra abilities that it doesn't normally have by default. Each plugin is associated with a property name and it takes responsibility for handling that property. For example, the FrameLabelPlugin is associated with the frameLabel property name so if it is activated it will intercept the frameLabel property in the following tween and manage it uniquely: TweenLite.to(mc, 1, {frameLabel:"myLabel"}); If the FrameLabelPlugin wasn't activated, TweenLite would act as though you were trying to literally tween the mc.frameLabel property (and there is no such thing). Activating a plugin requires a single line of code and you only need to do it once in your application, so it's pretty easy. Simply pass an Array containing the names of all the plugins you'd like to activate to the TweenPlugin.activate() method, like this: import com.greensock.plugins.*; TweenPlugin.activate([FrameLabelPlugin, ColorTransformPlugin, TintPlugin]); To make it even easier, I created the Plugin Explorer which writes the code for you. All you need to do is select the plugins and copy/paste the code from the bottom of the tool. It also displays interactive examples of each plugin and the associated code so that it's easy to see the correct syntax. TweenLite does not activate any plugins by default, but TweenMax does. When a plugin is activated, it affects both TweenLite and TweenMax. Overwriting other tweens An often overlooked aspect of tweening is how (and if and when) tweens overwrite other tweens of the same object. For example, let's say you have a button with ROLL_OVER and ROLL_OUT handlers that tween its alpha higher on ROLL_OVER and lower on ROLL_OUT. To further complicate things, let's say the ROLL_OVER tween lasts 2 seconds and the ROLL_OUT tween lasts 1 second. What should happen if the user rolls over/out/over/out quickly? See the problem? If tweens are allowed to run without any kind of overwriting, they'll build up and fight with each other (one trying to tween the alpha higher, and the other lower). In this example, when the user rolls over, a 2-second tween would start increasing the alpha to 1, but if the user rolled off 0.2 seconds later, another tween would begin, causing the alpha to decrease. When that tween finishes 1 second later, the ROLL_OVER tween is still going (since it had a duration of 2 seconds), so the alpha would suddenly jump up and finish off at a value of 1 even though the user rolled out! Don't worry. We've got you covered. By default, whenever a TweenLite instance renders for the first time (after any delay), it analyzes all other active tweens of the same target and checks for individual overlapping properties. If it finds any, it kills the offending overlaps (again, only the individual properties). This overwrite mode is called "auto" and it is typically the most intuitive. However, there may be times when you want the new tween to kill all other tweens of the same object regardless of their start times or overlapping properties. That is what the "all" overwrite mode is for. And to skip overwriting altogether, you can define an overwrite mode of "none". There are several other modes to choose from too, so check out the full docs for details. You define an overwrite mode with the overwrite special property like this: //overwrites all tweens of mc immediately TweenLite.to(mc, 1, {x:50, overwrite:"all"}); //doesn't overwrite anything (allows conflicts) TweenLite.to(mc, 1, {x:50, overwrite:"none"}); //overwrites only individual overlapping properties on concurrent tweens of mcmyElement (this is the default, so you typically don't need to specify any overwrite in this scenario) TweenLite.to(mc, 1, {x:50, overwrite:"auto"}); //set the default overwrite mode to "all" instead of "auto" TweenLite.defaultOverwrite = "all"; Of course you can manually kill all the tweens of a particular object using the TweenLite.killTweensOf() method, but the nice thing about defining overwrite modes is that the overwriting doesn't kick in until it's necessary (when the tween renders for the first time) which is essential when working with complex sequences. Controlling tweens Once a tween is created, you may want to pause(), resume(), reverse(), play(), restart(), invalidate(), or kill() it. It's pretty easy, actually: var myTween:TweenLite = new TweenLite(mc, 1, {x:100, y:100}); //pause myTween.pause(); //resume (honors direction - reversed or not) myTween.resume(); //reverse (always goes back towards the beginning) myTween.reverse(); //play() (always goes forwards) myTween.play(); //restart myTween.restart(); //invalidate (clears out any starting values that were recorded and forces the tween to re-initialize on the next render) myTween.invalidate(); //kill the tween immediately myTween.kill(); //kill all tweens of the mc object TweenLite.killTweensOf(mc); TweenMax has some additional static methods for getting all the tweens of a particular object, pausing them all, resuming, getting tweens of objects that are children of a certain DisplayObject, and more (see documentation for details). Which class do I use? TweenLite? TweenMax? TweenNano? If you can afford the file size (roughly 23kb with the default plugins), just use TweenMax. It is the most full-featured tweening engine and it automatically handles activating a bunch of useful plugins by default, so it makes things very easy. If, however, you're concerned about file size and want precise control over which plugins get activated, TweenLite is for you. It's amazingly capable for its size and has all the essentials crammed into about 8kb. It is really the core of the whole platform and has become incredibly popular. If you simply must shave off another 6k and are willing to sacrifice quite a few features (most notably lack of support for plugins and insertion into TimelineLite/Max instances), use the ridiculously small 2k TweenNano. I would strongly recommend sticking with TweenLite or TweenMax if you can, though, because they offer much more flexibility than TweenNano. All of the engines use exactly the same syntax, so these lines will produce identical results: TweenNano.to(mc, 1.5, {x:100, y:200, onComplete:myFunction, ease:Strong.easeOut}); TweenLite.to(mc, 1.5, {x:100, y:200, onComplete:myFunction, ease:Strong.easeOut}); TweenMax.to(mc, 1.5, {x:100, y:200, onComplete:myFunction, ease:Strong.easeOut}); Keep in mind that TweenMax extends TweenLite, so it does everything TweenLite does, plus more. And the plugins that are activated by default in TweenMax can also be activated in TweenLite (the only exception being roundProps), so with a couple of extra lines of code at the start of your application, TweenLite can have many of the same capabilities as TweenMax (activating plugins increases the file size beyond 4.7k obviously). There are several features that are only available in TweenMax, though, so check the documentation. Sequencing and grouping tweens with TimelineLite Unlike most other scripted animation tools, sequencing in GSAP is much more flexible than building a queue of tweens that run one-after-the-other. You have complete control over the relative timing of each tween - they can overlap as much as you want. And you can control entire sequences as a whole, reverse smoothly anytime, jump to any point, adjust the timeScale(), etc. and everything renders in the proper order. Watch this video for a visual demo showing how TimelineLite can save you a lot of time. Although the video uses the HTML5/JavaScript version of GSAP, the same concepts apply to ActionScript. Of course you could sequence tweens by using the delay special property on all your tweens, but that can get complicated when you build a long sequence and then later want to change the timing of something early in the sequence (you'd have to adjust all the delay values in tweens after that). Plus it would be a pain to control the whole sequence, like to pause() or resume() or reverse() the group on-the-fly. Sequencing is much easier with TimelineLite and its big brother, TimelineMax. Let's jump into some sample code: //create a TimelineLite instance var tl = new TimelineLite(); //append a to() tween tl.to(mc, 1, {x:50}); //add another sequenced tween (by default, tweens are added to the end of the timeline which makes sequencing simple) tl.to(mc, 1, {height:300p, ease:Elastic.easeOut}); //offset the next tween by 0.75 seconds so there's a gap between the end of the previous tween and this new one tl.to(mc, 1, {alpha:0.5}, "+=0.75"); //overlap the next tween with the previous one by 0.5 seconds (notice the negative offset at the end) tl.to(mc, 1, {rotation:360}, "-=0.5"); //animate 3 MovieClips (mc1, mc2, and mc3) to a rotation of 60 degrees, and stagger their start times by 0.2 seconds tl.staggerTo([mc1, mc2, mc3], 1, {rotation:60}, 0.2); //then call myFunction() tl.call(myFunction); //now we can control the entire sequence with the standard methods like these: tl.pause(); tl.resume(); tl.restart(); tl.reverse(); tl.play(); //jump to exactly 2.5 seconds into the animation tl.seek(2.5); //slow down playback to 10% of the normal speed tl.timeScale(0.1); //add a label named "myLabel" at exactly 3 seconds: tl.add("myLabel", 3); //add a tween that starts at "myLabel" tl.add( TweenLite.to(mc, 1, {scale:0.5}), "myLabel"); //jump to "myLabel" and play from there: tl.play("myLabel"); Think of a timeline (as in a TimelineLite or TimelineMax instance) like a collection of tweens that are positioned at specific places on that timeline. It controls their playback. Timelines can be nested inside other timelines as deeply as you want. This is a very powerful concept because it allows you to control entire sequences in a modular way. Imagine 100 characters individually animating into place in a staggered fashion (100 tweens). They could all be grouped into a TimelineLite instance and then controled as a whole (using common methods like pause(), resume(), reverse(), restart(), etc.). In fact, you could create functions that return animations wrapped in a TimelineLite so that you can easily build a larger, more complex animation in a modular way. A central concept to grasp is that every tween is inserted into a timeline. By default, it's the root timeline inside the engine. When a timeline is playing, its virtual playhead advances. If you reverse() a timeline, the playhead travels in the opposite direction back towards its beginning. As the timeline's playhead encounters tweens, it plays them accordingly. For example, if the playhead is positioned halfway through a tween, that tween will render as though it is 50% finished. If the timeline's timeScale() is set to 0.5, that would cause the playhead to travel at half speed. Consequently, any tweens it encounters would also appear to progress at half speed. Once you get the hang of how timelines work, they can revolutionize your animation workflow. Just like tweens, timelines play immediately by default but you can pause them initially using pause() or by setting paused:true in the vars parameter of the constructor. There are quite a few methods available in the timeline classes that give you precise control, and we'd encourage you to look through the docs to see what's available. If you can think of something you'd like to do, chances are there's a way to do it. Just like the way TweenMax extends TweenLite, TimelineMax extends TimelineLite, using identical syntax and adding several useful (but non-essential) features like AS3 event dispatching, repeat(), repeatDelay(), getActive(), getLabelAfter(), getLabelBefore(), currentLabel(), and more. Please refer to the TimelineMax documentation for details. Here's an interactive demo of TimelineMax: Need help? Feel free to post your question on the forums. Keep in mind that you'll increase your chances of getting a prompt answer if you provide a brief explanation and include a simplified FLA file (and any class files) that clearly demonstrates the problem.
  4. Hey, I'm new to GSAP and absolutely loving it! I'm currently working on my first commercial Gatsby React site and while experimenting with the useLayoutEffect hook I came across an issue. managed to rectify it and now I was just wondering if I could get a little context. When I started setting up my animations I was nesting each animation into a separate useLayoutEffect hook like so: const h2Ref = useRef(null); const contentRef = useRef(null); useLayoutEffect(() => { const el = h2Ref.current; gsap.set(el, { y: -30, opacity: 0 }); gsap.to(el, { y:0, opacity:1, delay:.25 }); gsap.to(el, { y: -50, opacity: 0, immediateRender: false, overwrite: 'auto', scrollTrigger: { trigger: el, start: "top+=100px center", scrub: .5, } }); }, []) useLayoutEffect(() => { const el2 = contentRef.current; gsap.set(el2, { y: -30, opacity: 0 }); gsap.to(el2, { y:0, opacity:1, delay:.25 }); gsap.to(el2, { y: -30, opacity: 0, immediateRender: false, overwrite: 'auto', scrollTrigger: { trigger: el2, start: "top+=100px center", scrub: .5, } }); }, []) Everything works fine just like this. As an experiment, I had a shift around and dropped several animations into a single useLayoutEffect hook to see if I could clean up my code a little bit. Like this: const h2Ref = useRef(null); const contentRef = useRef(null); useLayoutEffect(() => { const el = h2Ref.current; gsap.set(el, { y: -30, opacity: 0 }); gsap.to(el, { y:0, opacity:1, delay:.25 }); gsap.to(el, { y: -50, opacity: 0, immediateRender: false, overwrite: 'auto', scrollTrigger: { trigger: el, start: "top+=100px center", scrub: .5, } }); const el2 = contentRef.current; gsap.set(el2, { y: -30, opacity: 0 }); gsap.to(el2, { y:0, opacity:1, delay:.25 }); gsap.to(el2, { y: -30, opacity: 0, immediateRender: false, overwrite: 'auto', scrollTrigger: { trigger: el2, start: "top+=100px center", scrub: .5, } }); }, []) When I did that, I was ending up with masses of white space appearing at the bottom of the page whenever I changed to a new route. So really my question is more out of curiosity, why did this happen? Is this something to do with how hooks work or how GSAP works? Just hoping to get a little enlightened Many thanks, and thanks for such an awesome animation tool!
  5. Hey Cassie Thanks for your swift response, per usual. I'm doing great, thanks for asking. As I said, the actual code is a lot more abstract, since the fade-in animations are set in a completely different script, which is loaded on page load. The animations are set through data-attributes on the elements. Much like a scroll animations library like Animate On Scroll. I can't just overwrite or reset the animations, since I don't really know what the animations _are_. I'll have to experiment, and see if I can work the swopScroller function into the animations library. I'll report back.
  6. Heya! So the best approach here is likely just using tweens. Tweens are more dynamic and ideal for stuff like this. They'll take whatever the property value at that particular point in time is, no matter what it is, and tween to the property value you've specified. https://codepen.io/GreenSock/pen/LYXoKGX?editors=0110 Timelines are great for control, but it can get fiddly if you're animating the same elements in multiple timelines. Sometimes using overwrite:auto and immediateRender:false can help get the behaviour you're after, there's also an invalidate() method that you can call on a timeline that flushes out stored starting values, that can also be helpful too. https://codepen.io/GreenSock/pen/OJggZgK But yeah, I'd likely go for tweens for anything more dynamic and event driven! Hope this helps ☺️
  7. Hi, i'm not exactly sure what effect you are going for. FWIW it seems a bit disorienting to move something down to a y:200 while scrolling up. That aside I would avoid creating 2 timelines initially time that control the same properties of the same thing. To avoid conflicts I would suggest creating these animations fresh when you need them inside your callbacks sort of like: ScrollTrigger.create({ trigger: ".top-page", start: "top-=100 top", end: "top+=200 top", markers: true, onEnter: () => {let tl1 = gsap.timeline({}) .fromTo("header", { y: 0, overwrite: 'auto' },{ duration: 2, y: 200, ease: CustomEase.create("custom", "0.5, 0, 0, 1"), overwrite: 'auto' })}, onLeaveBack: () => {let tl2 = gsap.timeline({}) .to("header", { scale: 1.2, y: 200, }) .set("header", {y: 200,scale: 1,},"hide") .to("header", {opacity:1},"start2") .to(("header"), { y: 0, duration: 1, ease: "power4.out" }, "start2")} }); I would also remove locomotive scroll until you know things are working fine without it. Hopefully this set up will allow you to remove some of redundancy between the 2 animations like tweening and setting y:200 multiple times. If you need more help please try to simplify the animations as much as possible in a fork of the original pen.
  8. Hi, I am trying to repeat my timeline. The timeline plays perfect, but just one time. Please, can somebody please take a look at my code to see if there is any problems. import com.greensock.*; import com.greensock.easing.* mcText._alpha = 0; mcButton._alpha = 0; mcLegal._alpha = 0; mcPlaneicon._alpha = 0; mcPlaneicon2._alpha = 0; mcPlaneicon3._alpha = 0; mcPlaneicon4._alpha = 0; mcAmsterdamicon._alpha = 0; mcParisicon._alpha = 0; mcParis._alpha = 0; mcAmsterdam._alpha = 0; mcPriceamsterdam._alpha = 0; mcPriceparis._alpha = 0; var tl = new TimelineMax({repeat:2}); tl.add( TweenLite.to(mcAmsterdam, 0.3, {_alpha:90, delay:1, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcAmsterdamicon, 0.3, {_alpha:90, delay:1.4, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPlaneicon2, 0.3, {_alpha:90, delay:1.8, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPriceamsterdam, 0.3, {_alpha:90, delay:2.2, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcLegal, 0.5, {_alpha:100, delay:2.2, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcAmsterdamicon, 0.3, {_alpha:0, delay:5, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcAmsterdam, 0.3, {_alpha:0, delay:5, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPriceamsterdam, 0.3, {_alpha:0, delay:5, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPlaneicon2, 0.3, {_alpha:0, delay:5, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcBg, 0.3, {_x:-1920, delay:5.2, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcParis, 0.3, {_alpha:90, delay:5.8, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcParisicon, 0.3, {_alpha:90, delay:6.2, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPlaneicon3, 0.3, {_alpha:90, delay:6.6, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPriceparis, 0.3, {_alpha:90, delay:7, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcParisicon, 0.3, {_alpha:0, delay:10.2, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcParis, 0.3, {_alpha:0, delay:10.2, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPriceparis, 0.3, {_alpha:0, delay:10.2, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPlaneicon3, 0.3, {_alpha:0, delay:10.2, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcLegal, 0.5, {_alpha:0, delay:10.2, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcText, 0.3, {_alpha:100, delay:10.7, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPlaneicon4, 0.3, {_alpha:90, delay:11.1, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcButton, 0.3, {_alpha:90, delay:11.5, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcButton, 0.3, {_xscale:110,_yscale:110, delay:13, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcButton, 0.3, {_xscale:100,_yscale:100, delay:13.3, ease:Sine.easeIn, overwrite:false}) ); tl.add( TweenLite.to(mcButton, 0.3, {_xscale:110,_yscale:110, delay:14, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcButton, 0.3, {_xscale:100,_yscale:100, delay:14.3, ease:Sine.easeIn, overwrite:false}) ); tl.add( TweenLite.to(mcButton, 0.3, {_xscale:110,_yscale:110, delay:15, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcButton, 0.3, {_xscale:100,_yscale:100, delay:15.3, ease:Sine.easeIn, overwrite:false}) ); tl.add( TweenLite.to(mcBg, 0.3, {_x:0, delay:16, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcText, 0.3, {_alpha:0, delay:16, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcPlaneicon4, 0.3, {_alpha:0, delay:16, ease:Sine.easeOut, overwrite:false}) ); tl.add( TweenLite.to(mcButton, 0.3, {_alpha:0, delay:16, ease:Sine.easeOut, overwrite:false}) );
  9. Welcome to the forums, @Olly Grunt. Yeah, that's not a bug - that's just a logic issue on your code. You've got overwrite set to "auto", so each tween will wait to execute any overwriting until the very first time it renders at which point it will look for any other CURRENTLY ACTIVE tweens of the same properties of the same targets and kill just those parts. But you've set up your staggers and delays in a way that makes it pretty easy to create a scenario where the old/stale tweens haven't even started yet when the new tweens render for the first time, so overwriting doesn't occur. Simple solution: set overwrite: true instead of overwrite: "auto". When you set overwrite to true, that IMMEDIATELY kills all animations of the same targets regardless of which properties are getting animated or if those other animations have started yet. There are other solutions too, like you could create a variable to store the current animation (onEnter or onLeave, whichever was last to fire) and simply .kill() that before you create your new tween. That's basically like a manual overwrite system Good luck!
  10. I have a long page with multiple scroll based simple animations, some of them use the same class name to run different places. At some point it seems the trigger position gets off for all the page and animations at the bottom of the screen are happening in the wrong place, If I open console in the browser the animations are back at the correct position. (function () { gsap.registerPlugin(ScrollTrigger); gsap.defaults({ease: "power4.out"}); ScrollTrigger.matchMedia({ // desktop "(min-width: 800px)": function () { mySplitText = new SplitText(".corona-hero-section h2", {type: "words,chars"}); chars = mySplitText.chars; //an array of all the divs that wrap each character gsap.set(".corona-hero-section h2", {perspective: 400}); var tl = gsap.timeline(); tl .from(".logo", {duration: 2, autoAlpha: 0, y: -60, stagger: 0.3}, 1) .from(chars, { duration: 0.8, opacity: 0, scale: 0, y: 80, rotationX: 180, transformOrigin: "0% 50% -50", ease: "back", stagger: 0.01 }, "+=0"); tl.restart(); scheneChange(5, 140, '.viewer', '.scene', false, 'bottom bottom'); scheneChange(4, 138, '.viewer2', '.scene2', false, 'bottom bottom'); scheneChange(1, 122, '.viewer4', '.scene4', false, 'bottom bottom'); scheneChange(3, 146, '.viewer5', '.scene5', false, 'bottom bottom'); scheneChange(2, 139, '.viewer6', '.scene6', false, 'bottom bottom'); animateImageReveal(); animateStaggerIcons('.icon-list', '.icon-list__item'); animateStaggerIcons('.icon-num', '.icon-num__item'); animateStaggerIcons('.icon-top', '.icon-top__item'); aminateStaggerLi('.map-wrap__item'); aminateStaggerLi('.program-list-wrap li'); }, // mobile "(max-width: 799px)": function () { // Icons and text animation gsap.utils.toArray('.icon-list__item').forEach((section) => { const tls = gsap.timeline({ scrollTrigger: { trigger: section, scrub: true, start: "top center", // markers: true, end: "+=20%", }, }); const delay = 0.5; const textIcon = section.querySelectorAll(".icon-list__icon"); const textTitle = section.querySelectorAll(".icon-list__title"); const textText = section.querySelector(".icon-list__text"); const textNum = section.querySelector(".icon-list__num"); tls .from(textNum, {duration: 3, autoAlpha: 0, y: -60, stagger: delay}, 0) .from(textIcon, {duration: 1, autoAlpha: 0, y: -60, stagger: delay}, 0.3) .from(textTitle, {duration: 1, autoAlpha: 0, y: -60, stagger: delay}, 0.6) .from(textText, {duration: 1, autoAlpha: 0, y: -60, stagger: delay}, 0.9) }); scheneChange(5, 140, '.viewer', '.scene'); scheneChange(4, 138, '.viewer2', '.scene2'); scheneChange(1, 122, '.viewer4', '.scene4', false); scheneChange(3, 146, '.viewer5', '.scene5'); scheneChange(2, 139, '.viewer6', '.scene6', false); scheneImageReveal(); animateIconNum(); scheneChange(1, 163.5, '.viewer3', '.scene3'); }, "all": function () { animateTitleSection(); morphImg('.scene3'); } }); function scheneChange(frames, offset, classToAnimate, action, pin = true, start = "center center") { var frame_count = frames, offset_value = offset; gsap.to(classToAnimate, { backgroundPosition: (-offset_value * frame_count * 2) + "px 50%", ease: "steps(" + frame_count + ")", // use a stepped ease for the sprite sheet scrollTrigger: { trigger: action, start: start, end: "+=" + (frame_count * offset_value), pin: pin, // markers: true, scrub: true, } }); } function morphImg(trigger) { const tls3 = gsap.timeline({ scrollTrigger: { trigger: trigger, scrub: true, start: "top center", // markers: true, end: "+=40%", }, }); tls3 .to('#inIsrael', {duration: 2, fill: "#9191a0", morphSVG: "#inMaoz"}, 0) .to('#israelText', {duration: 2, morphSVG: "#maozText"}, 0) .to('#israelUnderText', {duration: 2, morphSVG: "#maozUnderText"}, 0.2) } function animateIconNum() { const sections = gsap.utils.toArray('.icon-num__item'); sections.forEach((section) => { const tls = gsap.timeline({ scrollTrigger: { trigger: section, scrub: true, start: "top center", // markers: true, end: "+=20%", }, }); const delay = 0.5; const textIcon = section.querySelectorAll(".icon-num__icon"); const textTitle = section.querySelectorAll(".icon-num__title"); const textText = section.querySelector(".icon-num__text"); // const textNum = section.querySelector(".icon-list__num"); tls .from(textText, {duration: 3, autoAlpha: 0, y: -60, stagger: delay}, 0) .from(textIcon, {duration: 1, autoAlpha: 0, y: -60, stagger: delay}, 0.3) .from(textTitle, {duration: 1, autoAlpha: 0, y: -60, stagger: delay}, 0.6) // .from(textText, {duration: 1, autoAlpha: 0, y: -60, stagger: delay}, 0.9) }); } function animateTitleSection() { const sections = gsap.utils.toArray(".corona-header"); sections.forEach((section) => { const splitTimeline = gsap.timeline({ scrollTrigger: { trigger: section, scrub: true, end: "+=70%", // onToggle: self => gsap.to(".split-text", {opacity: self.isActive ? 1 : 0}), toggleActions: "restart pause restart none", //markers: true } }); const split = new SplitText(section); splitTimeline .to(section, {duration: 1, backgroundColor: '#fff', ease: "none"}, 0) .to(section, {duration: 1, backgroundColor: '#222241', ease: "none"}, 1) .from(split.chars, { duration: 2, opacity: 0, x: "random(-500, 500)", y: "random(-500, 500)", z: "random(-500, 500)", scale: .1, yoyo: true, stagger: 0.02 }); }); } function animateImageReveal() { let revealContainers = gsap.utils.toArray(".reveal"); revealContainers.forEach((container) => { let tlImage = gsap.timeline({ scrollTrigger: { trigger: container, scrub: true, start: "top 100%", end: "bottom 70%", } }); tlImage.from(container, {duration: 0.3, autoAlpha: 0, y: -60, stagger: 0.3}, 1); }); } function scheneImageReveal() { let revealContainers = gsap.utils.toArray(".reveal"); revealContainers.forEach((container) => { let image = container.querySelector('img'); let tlImage = gsap.timeline({ scrollTrigger: { trigger: container, start: "20% 70%", toggleActions: "restart none none reset", } }); tlImage.set(container, {autoAlpha: 1}); tlImage.from(container, 1.5, { xPercent: -100, ease: Power2.out }); tlImage.from(image, 1.5, { xPercent: 100, // scale: 0.7, delay: -1.5, ease: Power2.out }); }); } function aminateStaggerLi(trigger) { gsap.set(trigger, {y: 100}); ScrollTrigger.batch(trigger, { onEnter: batch => gsap.to(batch, {opacity: 1, y: 0, stagger: {each: 0.15, grid: [1, 3]}, overwrite: true}), onLeave: batch => gsap.set(batch, {opacity: 0, y: -100, overwrite: true}), onEnterBack: batch => gsap.to(batch, {opacity: 1, y: 0, stagger: 0.15, overwrite: true}), onLeaveBack: batch => gsap.set(batch, {opacity: 0, y: 100, overwrite: true}) }); ScrollTrigger.addEventListener("refreshInit", () => gsap.set(trigger, {y: 0})); } function animateStaggerIcons(list, listItem) { gsap.utils.toArray(list).forEach(section => { const elems = section.querySelectorAll(listItem); // Set things up gsap.set(elems, {y: 50, opacity: 0}); ScrollTrigger.create({ trigger: section, start: 'top 60%', onEnter: () => gsap.to(elems, { y: 0, opacity: 1, duration: 1, stagger: 0.2, delay: 0.3, ease: 'power3.out', overwrite: 'auto' }), onLeaveBack: () => gsap.to(elems, { y: 50, opacity: 0, duration: 1, stagger: 0.2, delay: 0.3, ease: 'power3.out', overwrite: 'auto' }) }); }) } })();
  11. I saw a few problems: You're setting the width/height in the same gsap.set() call as the transformOrigin, and in this very rare edge case that's actually a problem because it just so happens that in the for...in loop through the properties, transformOrigin happens BEFORE the width/height. So when it tries to calculate the percentage-based origin offsets, your <rect> literally has no width or height at all, thus it gets positioned in its upper left corner. The solution: set the width/height FIRST. You could just separate those out into their own gsap.set() that you put first. You're creating conflicting animations. If you click again before the first set of animations completely finishes their 4 repeats, you'll be creating new ones that are also fighting with the old ones for control of the same elements. Make sure you kill() the old animations before you create new ones. Or you can just leverage the overwrite feature (overwrite: true here). Just so you know, the smoothOrigin does absolutely nothing in this line: gsap.timeline({ smoothOrigin: true, yoyo: false, repeat: 4 }); Timelines don't have a property like that. Maybe you intended to pass that down as a default for all child tweens?: gsap.timeline({ defaults: {smoothOrigin: true}, yoyo: false, repeat: 4 }); https://codepen.io/GreenSock/pen/NWBaLOd?editors=0010 Does that clear things up?
  12. Hi @aileen-r and welcome to the GreenSock forums! The reason for the different speed is beacuse you're setting the timescale of each animation on the mouse leave event to be 2 and is never reset to 1, that's why is faster after the first mouse over event. If you want to keep the same speed on both events just remove that and make the duration of the animation half the current time: reactions.forEach(reaction => { const action = gsap.to( reaction, { scale: 1.2, margin: '0 30px 0 20px', duration: 0.25, ease: 'power2.inOut', overwrite: 'true', paused: true } ); reaction.addEventListener("mouseenter", function() { action.play(0); }); reaction.addEventListener("mouseleave", function() { action.reverse(); }); }); Now if you still want the leave animation to be faster, then keep the timescale setter on the mouse leave, but add one to the mouse enter as well: reactions.forEach(reaction => { const action = gsap.to( reaction, { scale: 1.2, margin: '0 30px 0 20px', duration: 0.25, ease: 'power2.inOut', overwrite: 'true', paused: true } ); reaction.addEventListener("mouseenter", function() { action.timeScale(1).play(0); }); reaction.addEventListener("mouseleave", function() { action.timeScale(2).reverse(); }); }); You can learn more about timeScale here: https://greensock.com/docs/v3/GSAP/Tween/timeScale() Finally if you want to prevent the bounce effect in the reactions container, that's a bit more tricky since you need to get the current active element check in the array, see if there are elements before and move all those a specific amount of pixels and do the same for the elements after the active one. Finally when leaving the reactions container tween all the reactions back to their original x position. Avoid using margins for this if you can for that scenario. But I must say as a user I would be totally fine with the way things are, just speed up the animation and keep the timescale, no need to change it IMHO. Hopefully this helps. Let us know if you have more questions. Happy Tweening!
  13. The live example you have and the demo you provided don't use either Draggable or the Inertia Plugin, so you can set the draggable option to false: let carousel = buildCarousel(items, { radiusX: 250, radiusY: 210, activeAngle: -90, draggable: false, onClick(element, self) { self.to(element, {duration: 1, ease: "power1.inOut"}, "short"); }, onActivate(element, self) { element.classList.add("active"); }, onDeactivate(element, self) { element.classList.remove("active"); }, // when a drag or animation starts (via the Carousel's to()/next()/previous() methods) onStart(element, self) { gsap.to(descriptions[items.indexOf(element)], {autoAlpha: 0, duration: 0.25, overwrite: "auto"}); }, onStop(element, self) { gsap.to(descriptions[items.indexOf(element)], {autoAlpha: 1, overwrite: "auto"}); } }); If you want to use the example with the red circles you can just offset the start and endpoints by minus 25%: gsap.set(items, { motionPath: { path: circlePath, align: circlePath, alignOrigin: [0.5, 0.5], start: -0.25, end: i => (i / items.length) - 0.25, }, scale: 0.9 }); The rest of the code should remain the same. Yet another option with the latter example (red dots) is to set the start point of the path to the top of the circle. Check this article by @PointC https://www.motiontricks.com/cut-your-path-start-points-in-adobe-illustrator/ Hopefully this helps. Happy Tweening!
  14. Welcome to the GreenSock forum @blueblueblueblue For ScrollTrigger to get new values after invalidating the old you will need to use function based values for what you're tweening on, or else the invalidation on refresh won't do much for you in the first place. x: () => -1 * width, // instead of x: -1 * width, Secondly, you could try changing the ScrollTrigger event that you overwrite your width variable on, from "refreshInit" to "revert". That does seem to work better for me then. https://codepen.io/akapowl/pen/JjevVGG Personally, I don't actually see the need for an extra function to call on resize when you have only a few values that need re-evalution anyway, so I would actually ommit that function altogether in your case and just get the values on the tween itself directly; then you also wouldn't have to worry about any events anymore. I would also suggest moving away from getBoundingClientRect().width for this, because you technically only add one extra step for the browser to get to the value you need (and getBoundinClientRect() values might not always return what you think they do in other scenarios); when you can instead use something like element.scrollWidth instead. I hope that will help get you on the right track. https://codepen.io/akapowl/pen/vYQjMyZ
  15. Well, CodePen has been down for a while so I can't provide a forked demo but here's how I'd do the JS: let n = 15; let parallax = []; // we'll store the animations in here. let clamp = gsap.utils.clamp(0, 1); let currentX = 0; let snap = gsap.utils.pipe(gsap.utils.snap(450), gsap.utils.clamp(n * -450, 0)); // Set #slides width for draggable bounds gsap.set('#slides', {width:n*450}); // Populate slide boxes for (var i=0; i<n; i++){ var box = document.createElement('div'), img = new Image(), link = document.createElement('a'); gsap.set(box, { width:400, height:600, overflow:'hidden', position:'absolute', top:50, left:i*450, attr:{ class:'box b'+i }, background:'#333' }); gsap.set(img, { position:'absolute', left:-300,//-i*50, attr:{src:'https://picsum.photos/id/'+(i+10)+'/700/600/'} }); parallax[i] = gsap.to(img, {x: 300, ease: "none", paused: true}); gsap.set(link, { position:'absolute', textAlign:'center', width:105, height:70, paddingTop:'7px', top:490, left:-25, rotation:90, fontSize:'45px', color:'#000', background:'#fff', mixBlendMode:'lighten', textDecoration:'none', innerHTML:'<span style="font-size:20px">IMG </span>'+(i+1), attr:{ class:'imgLink', href:'https://picsum.photos/id/'+(i+10)+'/700/600/', target:'_blank' }, }); box.appendChild(img); box.appendChild(link); slides.appendChild(box); } // Make #slides draggable Draggable.create('#slides', { type:'x', bounds: {left: innerWidth/2, width:1}, zIndexBoost: false, onDrag:updateParallax, inertia: true, onRelease: function() { currentX = this.endX }, onThrowUpdate: updateParallax, snap: snap }) function updateParallax() { // parallax should start from the right edge of the screen and we know that the #slides starts with its left edge centered, thus we add innerWidth/2 let x = gsap.getProperty('#slides', 'x') + window.innerWidth / 2, // convert the position in the viewport (right edge of viewport to -400 because that's when the right edge of the element would go off-screen to the left) into a progress value where it's 0 at the right edge and 1 when it reaches the left edge normalize = gsap.utils.mapRange(window.innerWidth, -400, 0, 1); // apply the clamped value to each animation parallax.forEach((animation, i) => animation.progress(clamp(normalize(x + i * 450)))); } updateParallax(); // Update draggable bounds onResize window.addEventListener('resize', ()=>{ Draggable.get("#slides").applyBounds({left: innerWidth/2, width:1}) }); // Previous & next buttons $('#prev, #next').on('click', function(e) { let nextX = snap(currentX + (e.currentTarget.id === "next" ? -450 : 450)); if (nextX !== currentX) { gsap.to("#slides", {x: nextX, duration: 0.3, onUpdate: updateParallax, overwrite: true}) currentX = nextX; } }); $('#prev, #next').on('mouseenter', (e)=>{ gsap.to('#'+e.currentTarget.id + ' circle', {attr:{r:22}, ease:'expo', overwrite: true}) }); $('#prev, #next').on('mouseleave', (e)=>{ gsap.to('#'+e.currentTarget.id + ' circle', {attr:{r:20}, ease:'expo', overwrite: true}) }); // Img Link rollover/out behavior $('.imgLink').on('mouseenter', (e)=>{ gsap.to(e.currentTarget, {x:10, duration:0.3, ease:'power3', overwrite: true}) }); $('.imgLink').on('mouseleave', (e)=>{ gsap.to(e.currentTarget, {x:0, duration:0.3, ease:'power4.inOut', overwrite: true}) }); The general idea is: You only want the parallax effect to exist while each individual element is inside the viewport (not the entire movement of the #slides). I created a simple linear animation of x from 0 to 300 for EACH element. Paused. Dumped them into an Array. The updateParallax() function loops through each one and sets the progress() according to its position (which we know because they're 450px apart). It's all based on the viewport so that progress would be 0 when it's on the far right edge of the screen and 1 when the element's right edge reaches the left edge of the viewport. I also made the following improvements: I applied inertia with snapping directly on the draggable so it's super smooth and users can flick it. The logic in the next/previous buttons allows users to click the buttons quickly and it still works (instead of ignoring clicks while animation is running). Sorry, that's my pet peeve when the interface ignores user clicks. I hope that helps, Tom!
  16. Because in my case, I want when end scrollTrigger animation of box1, box2 must be next to child2 but currently it is overwrite each other.
  17. Hello there, Marko, welcome to the GSAP forum. Does the codepen show the same issue for you - because for me it doesn't. I'm not familiar with Elementor at all, but your video very much looks like there are conflicting CSS transitions applied to the element that is being changed inline by ScrollTrigger; so when ScrollTrigger changes e.g. the position to/from fixed on the pinned element, it will transition when the inline styles get applied. I'd suggest digging through your CSS and looking for transitions that are being applied by Elementor - and if they might affect any element that is being targeted by GSAP / ScrollTrigger in any way, remove them / overwrite them so they don't have an effect on that element any more. I hope that will help resolve the issue for you.
  18. Hi @Fabian W and welcome to the GreenSock forums! First thanks for being a Club GreenSock member and supporting GSAP! ? What you are looking for is the invalidateOnRefresh configuration option in the ScrollTrigger configuration object: Boolean - If true, the animation associated with the ScrollTrigger will have its invalidate() method called whenever a refresh() occurs (typically on resize). This flushes out any internally-recorded starting values. https://greensock.com/docs/v3/GSAP/Tween/invalidate() This seems to work the way you intend: let t1 = gsap.to(c1, { scrollTrigger: { scrub: true, start: () => 0, end: () => 'max', markers: true, invalidateOnRefresh: true,// <- HERE }, y: () => document.body.clientHeight - c1.clientHeight, ease: 'none', overwrite: 'auto' }); let t2 = gsap.to(c2, { scrollTrigger: { scrub: true, start: () => 0, end: () => 'max', markers: true, invalidateOnRefresh: true,// <- HERE }, y: () => document.body.clientHeight - c2.clientHeight, ease: 'none', overwrite: 'auto' }); Hopefully this helps. If you have more questions let us know. Happy Tweening!
  19. For the slide I had used a snippet of code of a codepen demo with scrolltrigger and scrollTo: let sections = gsap.utils.toArray(".scroller-sections"), scrollTween; function goToSection(i) { scrollTween = gsap.to(window, { scrollTo: {y: i * window.innerHeight, autoKill: false}, duration: 1, ease: "expo.out", onComplete: () => { scrollTween = null }, overwrite: true }); } sections.forEach((section, i) => { ScrollTrigger.create({ trigger: section, start: "top bottom-=1", end: "bottom top+=1", onToggle: self => self.isActive && !scrollTween && goToSection(i) }); }); the problems are for the animations that are different for each slide and the elements (inside svg) if you see here https://punctum.studio/preview/sitaricerca/ are positioned with transform or matrix and y or x doesn't work well.
  20. Your overall timeline has a scrub: true, this makes it animate on the users scroll position, but you overwrite this scrub (TIL you can overwrite ScrollTrigger properties within a tween!? Edit: No you can't!) within the frame animation and set it to 0.5, this makes it lag behind 0.5 seconds, which will result in it still playing when the next animation is already in view. You could do a few things to resolve this: Set the scrub the same for all animations on the timeline. Have a delay of 0.5 on the next animation either with the position parameter or a delay: 0.5 on the tween Hope it helps and happy tweening!
  21. Hi, OK, thanks for that information, I can see the confusion now. There is no issue at all here, everything is working as expected. The difference between desktop and a touch device are these event handlers: container.addEventListener("mouseenter", () => { reversedOnPause = tl.timeScale() < 0; isOver = true; gsap.to(tl, {timeScale: 0, duration: 1, overwrite: true}); container.classList.add("paused"); }); container.addEventListener("mouseleave", () => { isOver = false; gsap.to(tl, {timeScale: reversedOnPause ? -1 : 1, duration: 1, overwrite: true}); container.classList.remove("paused"); }); So when there is a touch start event in a device it acts as a mouse enter, but the touch end doesn't act like a mouse leave, you actually have to touch elsewhere, outside the boundaries of the container element, to trigger the mouse leave. If you remove those event listeners or add a different logic for touch events it should work as expected. Happy Tweening!
  22. What do I mean by the title is something done like this... but this is different because it doesn't have a scrollbar because it relies in scrollObserver...so I don't understand how can I make work the scrollTrigger alongside inside the scrollObserver. https://codesandbox.io/s/nervous-resonance-xpjvdf?file=/src/OshiNoko.jsx You can check in my code inside...which what I am trying to do is that I'm trying to spread the scale:2 in the text that is closing in the middle so it looks cool...and then once it pass by the middle it will slowly scale:1 or lower the scale...The scale should be dependent if it's closing in the middle of the item...but it doesn't plus it calls many times the markers which kinda sucks and also I don't know why when I'm trying to onUp or onDown it seems the text a bit laggy when transition? what I am missing here? This is the codes. const gotoSection = (index, isScrollingDown) => { let target = isScrollingDown ? swipePanels[currentIndex] : swipePanels[index]; let currentYPercent = gsap.getProperty(".list-of-items", "yPercent"); gsap.to(target, { scale: isScrollingDown ? 2 : 1, // duration:1.25, ease: "power2.inOut", scrollTrigger: { trigger: target, scrub: 1, start: "top center", end: "bottom center", markers: true } }); gsap.to(".list-of-items", { yPercent: limitYPercent(currentYPercent, isScrollingDown ? +1 : -1), duration: 0, // overwrite:true, ease: "none" }); currentIndex = index; }; // ScrollTrigger.create({ // trigger:'.wrapper', // scrub:true, // start: "top center", // end: "bottom center", // markers:true, // onEnter:() => { // let target = swipePanels[currentIndex]; // gsap.to(target,{ // scale:2, // }) // }, // onLeave:() => { // let target = swipePanels[currentIndex]; // gsap.to(target,{ // scale:1, // }) // } // }) instanceObserver = ScrollTrigger.observe({ target: ".wrapper", wheelSpeed: -1, onUp: () => { gotoSection(currentIndex + 1, true); }, onDown: () => { gotoSection(currentIndex - 1, false); }, tolerance: 10, preventDefault: true, allowClicks: true });
  23. Hi @dhdbtkd welcome to the forum! By default ScrollTrigger selects the the body (or HTML) as the scroll element, eg the element that normally scrolls. If you want to overwrite that, you'll have to define what element is the scroller in your case scroller: ".myScroller", for it to work. (I gave your element a class of myScroller) String | Element - By default, the scroller is the viewport itself, but if you'd like to add a ScrollTrigger to a scrollable <div>, for example, just define that as the scroller. You can use selector text like "#elementID" or the element itself. Read more on the docs https://greensock.com/docs/v3/Plugins/ScrollTrigger https://codepen.io/mvaneijgen/pen/JjZEyVo Personally I wouldn't overwrite the normal browser behavior if you don't have a clear use case for it. In your case you can just set the header and footer to position: fixed; and have the browser behave like it normally does, but that is just my opinion. Hope it helps and happy tweening!
  24. The default of overwrite: false is the most CPU-friendly option...it assumes you're responsible with the way you create your animations (not creating conflicts). 2nd fastest is overwrite: true because that doesn't have to care about which properties - it just obliterates any active tweens of the same object. We don't plan on changing any of those overwrite options if that's what you're asking. It won't cause a conflict, no - the whole purpose is to avoid conflicts But I think it'd be rather strange to do physics animations with overwrite: "auto" but it's not wrong or anything. Frankly, you're not going to notice a performance difference unless you really push things hard and have hundreds/thousands of things initiating animations simultaneously. And remember, overwriting logic only runs ONCE for a tween (when it renders for the first time). It's not like it's constantly draining resources looking for things to overwrite. As you probably know, we put a lot of effort into making GSAP highly performant. I may be misunderstanding but no, I don't see any reason to use isTweening() in that scenario. Here's some very basic pseudo code: // this replaces your setTimeout() logic. Just use a delayedCall - remember, it's just a tween with an onComplete, so you can pause()/restart() it. let hideControlsDelay = gsap.delayedCall(1, hideControls).pause(); function onPointerEnter() { hideControlsDelay.pause(); // don't hide controls when the pointer is over them gsap.to(controls, {autoAlpha: 1, overwrite: true}); // animate controls in } function onPointerLeave() { hideControlsDelay.restart(true); } function hideControls() { gsap.to(controls, {autoAlpha: 0, overwrite: true}); // animate controls out } hitArea.addEventListener('mouseenter', onPointerEnter); hitArea.addEventListener('mouseleave', onPointerLeave); I guess I don't really understand why isTweening() would be helpful. Maybe I'm missing something. Anyway, does that help?
  25. @GreenSock Thanks, I just use your code and it works. I guess I should use fromTo tween and also overwrite property, because I also have hover animation. Thanks for your help!
×
×
  • Create New...