Jump to content
GreenSock

Search the Community

Showing results for tags 'gsap'.

  • 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

  1. Hello everyone, I have a problem which I had for multiple projects already. Whenever I use from in a timeline, combined with Scrolltrigger, it doesn't work. Using a timeline without scrolltrigger it seems to work fine. The codepen I added is an example. GSAP Example From (codepen.io) I recreated the same example with .to instead of .from and you can see it works fine. GSAP Example To (codepen.io) Can anyone help me with this problem? Thanks in advance.
  2. Hello! I want to move the purple block along the path I've draw in the SVG. This is one section of my website which uses gsap all over the page. Rest of the page can be easily selected using the ref but when I use MotionPath, It throws Invalid scope and Target not found error. Here's the codesandbox: https://codesandbox.io/s/gsap-motionpath-react-bkscsf?file=/src/App.js Thank You!
  3. Hello! I am new to using GSAP so I apologise if I follow an incorrect format. I'm currently creating an interaction map that is made out of SVG that uses zoom and pan. I have used multiple solutions (specifically this one) from other topics related to my idea and tried code from CodePen created by other users. At the same time, I am using p5.js which I noticed that this library can be used with. The issue I'm facing is when I try to implement the zoom/pan interaction from one of the examples, it causes an error when I try to interact with the SVG: "Uncaught TypeError: point.matrixTransform is not a function". I was constantly searching for the exact cause until I have removed the "setup()" and "draw()" functions which causes the interaction to work. Nothing else could have interefered as I have created a completely new project that only contained the SVG and the code of the interaction without any of the p5 functions which also worked. I tried to search if other people had a similar issue to mine, but it seems like I'm the first. I am very confused why this happens, does anybody know why? I need both of those functions to do the fundamental things with p5.
  4. Hello Gsap community, i'm trying to create a Website, where when I scroll through it, the Time of the Video which is permanently in the Background is connected to the Scroll. While i'am scrolling and passing certain points in the Video, different HTML Elements should appear / disappear. So for example from Second 2-6 from the video i want to display elements which explain this part of the video and so on. The Elements should not be pinned tho, because the Video itself has a scroll through like effect aswell. I unfortunatly don't have any working prototype.
  5. Hi GSAP Guys, I am trying to get this to work. When you scroll down the number counter activates when section comes in to view, then starts the counting up of the numbers. But this isn't working, I know something is wrong but can't figure it out.
  6. Hi, Is there any way to target another element for adding class while the trigger elements enters and exits viewport. Like a target prop or something. Or just add animation to another set of images. In the below code I am trying to add "active" class to the target element ".anim-phone-image" but i guess there is no such property 😅. Please help me out useEffect(() => { const scrollTexts = gsap.utils.toArray('.anim-text'); scrollTexts.forEach((item, index) => { tl.current = gsap.timeline({ scrollTrigger: { trigger: item, scrub: true, start: 'top 30%', // start when top of trigger target hits 50% point of viewport end: 'bottom 10%', toggleClass: `active-${index}`, markers: true, target: '.anim-phone-image', }, }); }); }, []);
  7. Hi! I've made an SVG image with several different objects. The interaction would be this: - If you hover an element there would be a small effect on this element. (a scale, rotate and move depending on the mouse position.) - If you hover out the element get back to the original position. Currently, I have a problem with the targeting, the effect animates all the elements in the SVG. ...And some with the mouse position tracking:) How can I solve this? Thanks for the help in advance
  8. Hi ! On my portfolio website that I'm building with Nuxt.js, I want that when the user clicks on a project, the page is scrolled until the top of the project image is aligned with the word "Réalisations" - about 250px from the top of the page. I was inspired by this official gsap example : https://codepen.io/GreenSock/pen/LZOMKY As you can see on my website , when you click on the image and the content is scrolled up, the image does not stop at the right place - but in the opposite direction it works correctly.... any idea what may cause this issue ? Thank you for your help ! ScrollTo script triggered when the user click on a project : scrollToRealisation(event) { let target = event.target.closest(".realisation") this.$gsap.to(window, {duration: 0.5, scrollTo: {y: target, offsetY:250}}); }, Here is the whole code of the page component : <template> <div id="realisations"> <div v-for="item in orderRealisations" :key="item.titre" class="realisation"> <figure @click="scrollToRealisation($event)"> <img :src="require('/assets/imgs/projets/'+item.img)" :alt="item.titre"> </figure> <div class="content"> <div class="contentWrapper"> <header> <h3>{{ item.titre }}</h3> <h5 class="client-year">{{ item.client }} - {{ item.annee }}</h5> </header> <p class="description"> {{ item.description }} </p> <footer> <a class="projectLink" :href="item.url" target="_blank">Consulter le site</a> </footer> </div> </div> </div> </div> </template> <script> import realisationData from '~/static/data.json' export default { name: 'realisations', data() { return { sections: [] } }, asyncData ({ params }) { return { realisationData } }, mounted() { this.sections = this.$gsap.utils.toArray('.realisation'); this.animateOnScroll() }, computed: { orderRealisations() { return this.realisationData.sort((a, b) => b.annee.localeCompare(a.annee)); } }, methods: { //ScrollTo scrollToRealisation(event) { let target = event.target.closest(".realisation") this.$gsap.to(window, {duration: 0.5, scrollTo: {y: target, offsetY:250}}); }, animateOnScroll() { const gsap = this.$gsap this.sections.forEach((section) => { let image = section.getElementsByTagName('figure') let content = section.getElementsByClassName('contentWrapper') this.$ScrollTrigger.saveStyles(".realisation figure, .realisation .contentWrapper"); this.$ScrollTrigger.matchMedia({ "(min-width: 769px)": function() { let imageTl = gsap .timeline({ scrollTrigger: { trigger: section, start: 'top 60%', scrub: 2, end: 'bottom 0px', } }) imageTl.addLabel("animationstart") .to(image, {width: '30vw'}) .to(image, {width: '20vw'}) let contentTl = gsap.timeline({ scrollTrigger: { trigger: section, start: 'top 40%', scrub: false, //in: true, //markers: true, end: 'bottom 35%', toggleActions: "play reverse play reverse" } }) contentTl.addLabel("contentanimation") .fromTo(content, {x:-300, opacity:0,}, {x:0, opacity:1 }, '-=0.8') }, // mobile "(max-width: 768px)": function() { let contentMobileTl = gsap.timeline({ scrollTrigger: { trigger: section, start: 'top 40%', scrub: false, //markers: true, end: 'bottom 35%', toggleActions: "play reverse play reverse" } }) contentMobileTl.addLabel("contentanimation") .fromTo(content, {y:-200, marginTop: 0, height: 0, opacity:0,}, {y:0, height: 'auto',marginTop: 20, marginBottom: 40, opacity:1 }, '-=0.8') } }) }) } } } </script> <style lang="scss"> #realisations { padding-bottom: 60vh; padding-top: 225px; @media all and (max-width: 768px) { padding-top: 0; } .realisation { display: flex; margin-bottom: 3em; @media all and (max-width: 768px) { display: block; flex-direction: column; margin-bottom: 1em; } figure { margin: 0; width: 20vw; cursor: pointer; img { width: 100%; box-shadow: rgba(255, 255, 255, 0.1) 0px 1px 1px 0px inset, rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px; } @media all and (max-width: 768px) { width: 100%; } } .content { max-width: 500px; overflow:hidden; margin-left: 40px; @media all and (max-width: 768px) { margin-left: 0; height: auto; } } .contentWrapper { height: 100%; display: flex; flex-direction: column; justify-content: center; flex-wrap: wrap; header { h3, h5 { margin: 0; } h3 { font-size: 1.4em; margin-bottom: 0em; @media all and (max-width: 768px) { font-size: 1em; } } h5 { font-weight: 400; } } p { font-size: 0.9em; line-height: 1.2; } footer { // margin-top: auto; a { color: #fff; text-decoration: underline; font-size: 0.7em; } } } } } </style>
  9. Hello everyone! I started using scrolltrigger to make a nice scrolling website for a client. In the header section, I want the animation tu run on scrub but not on backwards scrub, so I killed it onLeave. The problem is that when the animation is killed a large black space is left above my header section. I tried making it the same as I have it on CodePen but I failed.. hope it's understandable this way. Thank you in advance!
  10. Hello. I have the following working properly in a hardcoded Wordpress template, however, it seems that whenever I view the site and I'm not logged in, the Scrolltrigger events do not work/fire. I've replicated the site in Codepen but cannot replicate the issues there: https://codepen.io/neontrenton/pen/vYdOppv Does anyone have any idea why this might be happening? It seems like a Wordpress-specific issue (I have no cache/cache plugins on the site) but I don't even know where to begin troubleshooting this. Here are the scripts I'm loading into the site: <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js"></script> <script src="/wp-content/themes/canvas/js/plugins/ScrollTrigger.min.js"></script> Here is an example of a ScrollTrigger event that DOES work: gsap.utils.toArray(".color-reveal").forEach((color) => { gsap.to(".color-reveal", { scrollTrigger: { trigger: color, toggleClass: "active", scrub: true, start: "top center", end: "+=100", }, }); }); Here is the specific ScrollTrigger events that DO NOT work if you aren't logged in: let containers = gsap.utils.toArray(".treatment-1"); containers.forEach((container) => { let tl = gsap.timeline({ scrollTrigger: { trigger: container, scrub: true, pin: true, start: "center center", end: "bottom top", markers: false, }, defaults: { ease: "none" } }); tl.to(container.querySelector("img"), { scale: 1, x: -500 }); tl.to(container.querySelector(".treatment__headline"), { autoAlpha: 1 }); }); // Treatment Two Animation let containerstwo = gsap.utils.toArray(".treatment-2"); containerstwo.forEach((container) => { let tl = gsap.timeline({ scrollTrigger: { trigger: container, scrub: true, pin: true, start: "center center", end: "bottom top", markers: false, }, defaults: { ease: "none" } }); tl.to(container.querySelector("img"), { scale: 1, x: 450 }); tl.to(container.querySelector(".treatment__headline"), { autoAlpha: 1 }); }); // Treatment Three Animation let containersthree = gsap.utils.toArray(".treatment-3"); containersthree.forEach((container) => { let tl = gsap.timeline({ scrollTrigger: { trigger: container, scrub: true, pin: true, start: "center center", end: "bottom top", markers: false, }, defaults: { ease: "none" } }); tl.to(container.querySelector("img"), { scale: 1, x: -500 }); tl.to(container.querySelector(".treatment__headline"), { autoAlpha: 1 }); });
  11. I'm coming to this having started from that support question. Many years ago I'd built a site based on this very old tutorial. Then, a few months ago I got some assistance on this in order to create a simple horizontal scroll, which worked pretty nicely. Today I've started to try and combine the scripts from support question responses, and the horizontal scroll pen, in the one I've included here (somehow I don't think that sentence makes any sense except in my own head) in order to emulate the behavior from that old tutorial. In any case, I'm not sure why it's not really working the way I think it should. I'm still fairly new to gsap, so I apologize in advance for not seeing things that should probably be obvious.
  12. Hi, how can i create anchor links for a horizontal scrolling? My version is not working correctly: $(".menu-item").on("click", function (event) { event.preventDefault(); var id = $(this).attr('href'); var left = $(id).offset().left; $('body,html').animate({ scrollTop: left }, 1500); });
  13. Hello, I've created a projects slider with interactive draggable projects menu. The snipped runs bi-directionally - if you scroll through projects it uses scrollTrigger to update the projects menu, and in reverse - if you click items in the menu, it scrolls the website to the chosen project. The problem is with the draggable projects menu, it works great on desktop and on android firefox, however in Android Chrome and on native Samsung Browser the links don't always work. It changes colour, as if the hover event fired, however touch/click doesn't register. I've tried adding Draggable setting of minimumMovement up to 20, but it doesn't help, and once I touch the link it visibly moves a few pixels so it seems like the dragging overpowers the click/tap. Please check out the codepen example in Android Chrome in landscape mode. (Sorry for the messy code, it had to be extracted from a bigger code). Thank you in advance
  14. Hello , i am sorry to make full slider circular , i found a good example on this amazing forum , but i don't have too much experience to make it alone and i need to under stand it , so i need some one to help me all i want the same code pen but i want to rotate circle with drag mouse and the circle have a infint rotate then if i have a buttons like pre and next but for category , like if i press a fruit button the number change to fruit i know that is bad but i need some help thank you
  15. I am trying to make something like thisI want to set specific position of x if value of drag on x axis is greater than defined number everything is working just fine but without transition or I can say without bounceEffect . Additionally I also want to reset position of draggable element on focusLost
  16. GreenSock

    Draggable

    #container { margin:0; padding:0; font-family: Signika Negative, Asap, sans-serif; font-weight: 300; font-size: 17px; line-height: 150%; } #container h1 { font-family: Signika Negative, Asap, sans-serif; font-weight: 300; font-size: 48px; margin: 10px 0 0 0; padding: 0; line-height: 115%; text-shadow: 1px 1px 0 white; } #container h2 { font-family: Signika Negative, Asap, sans-serif; font-weight: normal; font-size:30px; color: #111; margin: 18px 0 0 0; padding: 0; line-height:115%; } #container p { line-height: 150%; color:#555; margin: 0 0 10px 0; } #container a { color:#71b200; } #container .normalBullets code { font-size: inherit; color: inherit; font-weight: normal; line-height: inherit; font-family: inherit; } #container .normalBullets li strong { font-size: 110%; } #container .normalBullets li { margin-bottom:8px; } #container .blackBG h1, #container .darkBG h1 { color: #ddd; text-shadow: none; } #container .blackBG p { color: #999; } #container .section { width: 100%; text-align: center; position: relative; padding: 20px; } /* .block was causing conflict with wp theme --- renamed below */ #container .customblock { padding: 10px; text-align: left; position: relative; } #container .blackBG { background-color: black; } #container .lightBG { background-color: #e4e4e4; } #container .subtleDark { color: #999; text-shadow: none; } #container .blackBG p strong { color:#ddd; font-weight: normal; } #container .controls { background-color: #222; border: 1px solid #555; color: #bbb; font-size: 18px; } #container .controls ul { list-style: none; padding: 0; margin: 0; } #container .controls li { display: inline-block; padding: 8px 0 8px 10px; margin:0; } /** CODE **/ #container .code { width: 100%; border: 1px solid #555; padding: 0; margin: 20px 0; } #container .code pre.prettyprint { margin:0; overflow: auto; } #container .codeTitle { color: #aaa; background-color: #111; padding: 8px; font-size:18px; border-bottom: 1px solid #555; } #container code, #scroller code { color: black; font-size: 16px; } #container .blackBG code, #container .darkBG code { /* carl removed color: #ccc; */ } #container pre { font-size: 1.1em; padding:8px; background-color:#333; color:white; border: 1px solid #777; } /** TOSS **/ #container .box { background-color: #91e600; text-align: center; font-family: Asap, Avenir, Arial, sans-serif; width: 196px; height: 100px; line-height: 100px; overflow: hidden; color: black; position: absolute; top:0; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; } /** BUTTONS **/ #container .button { display:inline-block; border-radius:8px; border-bottom-width: 2px; box-shadow: inset 0px 1px 0px rgba(255,255,255,0.6), 0px 3px 6px 0px rgba(0, 0, 0, 0.6); cursor:pointer; text-align: center; font-family: Signika Negative, Asap, Avenir, Arial, sans-serif; position:relative; margin: 4px; color:black; } #container .largeButton { padding: 12px 24px; font-size: 20px; margin: 12px 8px; min-width:110px; } .greenGradient { border: 1px solid #6d9a22; background-color: #699a18; background: linear-gradient(to bottom, #8cce1e 0%,#699a18 52%,#639314 53%,#76b016 100%); /* W3C */ text-shadow: 1px 1px 2px #384d16; color:#fff; text-decoration: none; } /** EXPANDABLE POINTS (FAQ) **/ .expPoint, .expList li { list-style: none; line-height: normal; margin: 0 0 0 8px; padding: 6px 4px 4px 24px; position:relative; border: 1px solid rgba(204,204,204,0); font-size: 110%; color: #111; font-weight: normal; } .expPoint, .expContent { font-family: Signika Negative, Asap, sans-serif; font-weight: 300; line-height: 140%; } .expPoint:hover, .expList li:hover { background-color:white; border: 1px solid rgb(216,216,216); } .expContent { height: 0; overflow: hidden; color: #444; margin: 2px 0 0 0; padding-top: 0; font-size:16px; } .expMore { color: #71b200; text-decoration: underline; font-size:0.8em; } .arrow-right { width: 0; height: 0; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 6px solid #999; display:inline-block; margin: -4px 8px 0 -14px; vertical-align: middle; opacity:0.8; } .tableCellDesktop { display: table-cell; } .tableCellDesktop img { left: 120px; } @media screen and (max-width: 860px) { .tableCellDesktop { display: block; } .tableCellDesktop img { left: 0px; } } Features Touch enabled - works great on tablets, phones, and desktop browsers. Incredibly smooth - GPU-accelerated and requestAnimationFrame-driven for ultimate performance. Compared to other options out there, Draggable just feels far more natural and fluid, particularly when imposing bounds and momentum. Momentum-based animation - if you have InertiaPlugin loaded, you can simply set inertia: true in the config object and it'll automatically apply natural, momentum-based movement after the mouse/touch is released, causing the object to glide gracefully to a stop. You can even control the amount of resistance, maximum or minimum duration, etc. Complex snapping made easy - snap to points within a certain radius (see example), or feed in an array of values and it'll select the closest one, or implement your own custom logic in a function. Ultimate flexibility. You can have things live-snap (while dragging) or only on release (even with momentum applied, thanks to InertiaPlugin)! Impose bounds - tell a draggable element to stay within the bounds of another DOM element (a container) as in bounds:"#container" or define bounds as coordinates like bounds:{top:100, left:0, width:1000, height:800} or specific maximum/minimum values like bounds:{minRotation:0, maxRotation:270}. Sense overlaps with hitTest() - see if one element is overlapping another and even set a tolerance threshold (like at least 20 pixels or 25% of either element's total surface area) using the super-flexible Draggable.hitTest() method. Feed it a mouse event and it'll tell you if the mouse is over the element. See http://codepen.io/GreenSock/pen/GFBvn for a simple example. Define a trigger element - maybe you want only a certain area to trigger the dragging (like the top bar of a window) - it's as simple as trigger:"#topBar", for example. Drag position, rotation, or scroll - lots of drag types to choose from: "x,y" | "top,left" | "rotation" | "scroll" | "x" | "y" | "top" | "left" | "scrollTop" | "scrollLeft" Lock movement along a certain axis - set lockAxis:true and Draggable will watch the direction the user starts to drag and then restrict it to that axis. Or if you only want to allow vertical or horizontal movement, that's easy too using the type ("top", "y" or "scrollTop" only allow vertical movement; "x", "left", or "scrollLeft" only allow horizontal movement). Rotation honors transform origin - by default, spinnable elements will rotate around their center, but you can set transformOrigin to something else to make the pivot point be elsewhere. For example, if you call gsap.set(yourElement, {transformOrigin:"top left"}) before dragging, it will rotate around its top left corner. Or use % or px. Whatever is set in the element's css will be honored. Rich callback system and event dispatching - you can use any of the following callbacks: onPress, onDragStart, onDrag, onDragEnd, onRelease,, onLockAxis, and onClick. Inside the callbacks, "this" refers to the Draggable instance itself, so you can easily access its "target" or bounds, etc. If you prefer event listeners instead, Draggable dispatches events too so you can do things likeyourDraggable.addEventListener("dragend", yourFunc); Works great with SVG Even works in transformed containers! Got a Draggable inside a rotated/scaled container? No problem. No other tool handles this properly that we've seen. Auto-scrolling, even in multiple containers - set autoScroll:1 for normal-speed auto scrolling, or autoScroll:2 would scroll twice as fast, etc. The closer you move toward the edge, the faster scrolling gets. See a demo here (added in version 0.12.0) Sense clicks when the element moves less than 3 pixels - a common challenge is figuring out when a user is trying to click/tap an object rather than drag it, so if the mouse/touch moves less than 3 pixels from its starting position, it will be interpreted as a "click" and the onClick callback will be called (and a "click" event dispatched) without actually moving the element. You can define a different threshold using minimumMovement config property, like minimumMovement:6 for 6 pixels. All major browsers are supported including IE9+. IE8 lacks hitTest() support. Demos Draggable Showcase Draggable How-To Demos See full documentation here. To get InertiaPlugin (for the momentum-based features), join Club GreenSock today. You'll be glad you did. If not, we'll gladly issue a full refund. To learn how to include Draggable and InertiaPlugin into your project, see the GSAP install docs.
  17. Are you guilty of any of the most common mistakes people make in their ScrollTrigger code? Nesting ScrollTriggers inside multiple timeline tweens Creating to() logic issues Using one ScrollTrigger or animation for multiple "sections" Forgetting to use function-based start/end values for things that are dependent on viewport sizing Start animation mid-viewport, but reset it offscreen Creating ScrollTriggers out of order Loading new content but not refreshing Why does my "scrub" animation jump on initial load? Or my non-scrub animation start playing? Tip: How to make scrub animations take longer Navigating back to a page causes ScrollTrigger to break Note: There's also a separate article that covers the most common GSAP mistakes. Debugging tip: In many cases, the issue isn't directly related to ScrollTrigger, so it's helpful to get things working without ScrollTrigger/any scroll effects and then, once everything else is working, hook things up to ScrollTrigger. Nesting ScrollTriggers inside multiple timeline tweens A very common mistake is applying ScrollTrigger to multiple tweens that are nested inside a timeline. Logic-wise, that can't work. When you nest an animation in a timeline, that means the playhead of the parent timeline is what controls the playhead of the child animations (they all must be synchronized otherwise it wouldn't make any sense). When you add a ScrollTrigger with scrub, you're basically saying "I want the playhead of this animation to be controlled by the scrollbar position"...you can't have both. For example, what if the parent timeline is playing forward but the user also is scrolling backwards? See the problem? It can't go forward and backward at the same time, and you wouldn't want the playhead to get out of sync with the parent timeline's. Or what if the parent timeline is paused but the user is scrolling? So definitely avoid putting ScrollTriggers on nested animations. Instead, either keep those tweens independent (don't nest them in a timeline) -OR- just apply a single ScrollTrigger to the parent timeline itself to hook the entire animation as a whole to the scroll position. Creating to() logic issues If you want to animate the same properties of the same element in multiple ScrollTriggers, it’s common to create logic issues like this: gsap.to('h1', { x: 100, scrollTrigger: { trigger: 'h1', start: 'top bottom', end: 'center center', scrub: true } }); gsap.to('h1', { x: 200, scrollTrigger: { trigger: 'h1', start: 'center center', end: 'bottom top', scrub: true } }); Did you catch the mistake? You might think that it will animate the x value to 100 and then directly to 200 when the second ScrollTrigger starts. However if you scroll through the page you’ll see that it animates to 100 then jumps back to 0 (the starting x value) then animates to 200. This is because the starting values of ScrollTriggers are cached when the ScrollTrigger is created. See the Pen ScrollTrigger to() logic issue by GreenSock (@GreenSock) on CodePen. To work around this either use set immediateRender: false (like this demo shows) or use .fromTo()s for the later tweens (like this demo shows) or set a ScrollTrigger on a timeline and put the tweens in that timelines instead (like this demo shows). Using one ScrollTrigger or animation for multiple "sections" If you want to apply the same effect to multiple sections/elements so that they animate when they come into view, for example, it's common for people to try to use a single tween which targets all the elements but that ends up animating them all at once. For example: See the Pen ScrollTrigger generic target issue by GreenSock (@GreenSock) on CodePen. Since each of the elements would get triggered at a different scroll position, and of course their animations would be distinct, just do a simple loop instead, like this: See the Pen ScrollTrigger generic target issue - fixed with scoping by GreenSock (@GreenSock) on CodePen. Forgetting to use function-based start/end values for things that are dependent on viewport sizing For example, let's say you've got a start or end value that references the height of an element which may change if/when the viewport resizes. ScrollTrigger will refresh() automatically when the viewport resizes, but if you hard-coded your value when the ScrollTrigger was created that won't get updated...unless you use a function-based value. end: `+=${elem.offsetHeight}` // won't be updated on refresh end: () => `+=${elem.offsetHeight}` // will be updated Additionally, if you want the animation values to update, make sure the ones you want to update are function-based values and set invalidateOnRefresh: true in the ScrollTrigger. Start animation mid-viewport, but reset it offscreen For example try scrolling down then back up in this demo: See the Pen ScrollTrigger reset issue by GreenSock (@GreenSock) on CodePen. Notice that we want the animation to start mid-screen, but when scrolling backwards we want it to reset at a completely different place (when the element goes offscreen). The solution is to use two ScrollTriggers - one for the playing and one for the resetting once the element is off screen. See the Pen ScrollTrigger reset issue - fixed with two ScrollTriggers by GreenSock (@GreenSock) on CodePen. Creating ScrollTriggers out of order If you have any ScrollTriggers that pin elements (with the default pinSpacing: true) then the order in which the ScrollTriggers are created is important. This is because any ScrollTriggers after the ScrollTrigger with pinning need to compensate for the extra distance that the pinning adds. You can see an example of how this sort of thing might happen in the pen below. Notice that the third box's animation runs before it's actually in the viewport. See the Pen ScrollTrigger creation order issue by GreenSock (@GreenSock) on CodePen. To fix this you can either create the ScrollTriggers in the order in which they are reached when scrolling or use ScrollTrigger's refreshPriority property to tell certain ScrollTriggers to calculate their positions sooner (the higher the refreshPriority the sooner the positions will be calculated). The demo below creates the ScrollTriggers in their proper order. See the Pen ScrollTrigger creation order issue - fixed by GreenSock (@GreenSock) on CodePen. Loading new content but not refreshing All ScrollTriggers get setup as soon as it's reasonably safe to do so, usually once all content is loaded. However if you're loading images that don't have a width or height attribute correctly set or you are loading content dynamically (via AJAX/fetch/etc.) and that content affects the layout of the page you usually need to refresh ScrollTrigger so it updates the positions of the ScrollTriggers. You can do that easily by calling ScrollTrigger.refresh() in the callback for your method that is loading the image or new content. Why does my "scrub" animation jump on initial load? Or my non-scrub animation start playing? Most likely the ScrollTrigger’s start value is before the starting scroll position. This usually happens when the start is something like "top bottom" (the default start value) and the element is at the very top of the page. If you don’t want this to happen simply adjust the start value to one that’s after a scroll position of 0. Tip: How to make "scrub" animations take longer The duration of a "scrub" animation will always be forced to fit exactly between the start and end of the ScrollTrigger position, so increasing the duration value won't do anything if the start and end of the ScrollTrigger stay the same. To make the animation longer, just push the end value down further. For example, instead of end: "+=300", make it "+=600" and the animation will take twice as long. If you want to add blank space between parts of a scrubbed animation, just use empty tweens as the docs cover. Navigating back to a page causes ScrollTrigger to break If you have a single-page application (SPA; i.e. a framework such as React or Vue, a page-transition library like Highway.js, Swup, or Barba.js, or something similar) and you use ScrollTrigger you might run into some issues when you navigate back to a page that you've visited already. Usually this is because SPAs don't automatically destroy and re-create your ScrollTriggers so you need to do that yourself when navigating between pages or components. To do that, you should kill off any relevant ScrollTriggers in whatever tool you're using's unmount or equivalent callback. Then make sure to re-create any necessary ScrollTriggers in the new component/page's mount or equivalent callback. In some cases when the targets and such still exist but the measurements are incorrect you might just need to call ScrollTrigger.refresh(). If you need help in your particular situation, please make a minimal demo and then create a new thread in our forums along with the demo and an explanation of what's going wrong. Still need some help? The GreenSock forums are the best place to get your questions answered. We love helping people develop their animation superpowers.
  18. I'm trying to recreate The Weeknd website Scrolling effect, I have written a working gsap code but I just cant crate that IMAGE CLIP EFFECT when u scroll enough that Image will pass by u. in my code, it's just scrolling and zoom but on Weeknd website, it has the effect of zooming and the image will pass by u, I'm trying to create that effect but right now I don't know how to hide an image when it becomes full zoom.
  19. Hey @OSUblake Could you help me out one more time please? Here is my repository : https://github.com/Sameer-mishra1/Dbait-website As you can see in a desktop, when you scroll all the way to the bottom, the Download button doesn't seem to be working. When I inspected it a bit, I realised that there's some kind of overlap between the animation view and the button and that's why nothing happens on clicking the button. I am not able to figure out the exact reason though, please let me know what you think could be the plausible reason. https://drive.google.com/file/d/1iOcjNoqBP9sOtw6aJxG47Ldlgib293xj/view?usp=sharing
  20. Hi, here is how I can make the header appear only when the scroll moves up. I think smooth scroll is preventing this because of its own page calculation. I can see it when I go to the top of the page. I want to see the header when I scroll up. Like the example here; https://aydindoganvakfi.org.tr/
  21. i have this scrolltriggger animation var tl = gsap.timeline({ scrollTrigger:{ trigger:".steps ", start:"top center", end:"+=4000px", scrub:5, pin:true, } }); tl.from(".step_img",{duration:3, y:440 ,scale:1.5}) tl.to(".step_img",{duration:3,x:260 }) i want to change the x to 0 in mobile viewport or disable this line in mobile viewport tl.to(".step_img",{duration:3,x:260 })
  22. Are you working with React and looking to really advance your GSAP animation skills? You're in the right place. This guide contains advanced techniques and some handy tips from expert animators in our community. This is not a tutorial, so feel free to dip in and out as you learn. Think of it as a collection of recommended techniques and best practices to use in your projects. Why GSAP? Animating with GSAP gives you unprecedented levels of control and flexibility. You can reach for GSAP to animate everything — from simple DOM transitions to SVG, three.js, canvas or WebGL — your imagination is the limit. More importantly, you can rely on us. We obsess about performance, optimizations and browser compatibility so that you can focus on the fun stuff. We've actively maintained and refined our tools for over a decade and there are no plans to stop. Lastly, if you ever get stuck, our friendly forum community is there to help. Going forward we will assume a comfortable understanding of both GSAP and React. If you're starting out we highly recommend reading our foundational article first - First Steps & Handy Techniques.. Quick Links Component Communication Passing down a timeline prop Passing down callback to build a timeline React Context Imperative Communication Creating Reusable Animations registerEffect() Exit Animations Custom Hooks useSelector useArrayRef useStateRef useIsomorphicLayoutEffect Online Playgrounds Get started quickly by forking one of these starter templates: CodePen CodeSandbox CodeSandbox + Bonus Plugins Component Communication In the last article, we covered creating our first animation, and how to create and control timelines within a React component. But there are times where you may need to share a timeline across multiple components or construct animations from elements that exist in different components. In order to achieve this, we need a way to communicate between our components. There are 2 basic approaches to this. a parent component can send down props, e.g. a timeline a parent component can pass down a callback for the child to call, which could add animations to a timeline. Passing down a timeline prop Note that we are using useState instead of useRef with the timeline. This is to ensure the timeline will be available when the child renders for the first time. function Box({ children, timeline, index }) { const el = useRef(); // add 'left 100px' animation to timeline useEffect(() => { timeline.to(el.current, { x: -100 }, index * 0.1); }, [timeline]); return <div className="box" ref={el}>{children}</div>; } function Circle({ children, timeline, index, rotation }) { const el = useRef(); // add 'right 100px, rotate 360deg' animation to timeline useEffect(() => { timeline.to(el.current, { rotate: rotation, x: 100 }, index * 0.1); }, [timeline, rotation]); return <div className="circle" ref={el}>{children}</div>; } function App() { const [tl, setTl] = useState(() => gsap.timeline()); return ( <div className="app"> <Box timeline={tl} index={0}>Box</Box> <Circle timeline={tl} rotation={360} index={1}>Circle</Circle> </div> ); } See the Pen React Tutorial 3a by GreenSock (@GreenSock) on CodePen. Passing down a callback to build a timeline function Box({ children, addAnimation, index }) { const el = useRef(); // return a 'left 100px' tween useEffect(() => { const animation = gsap.to(el.current, { x: -100 }); addAnimation(animation, index); return () => animation.progress(0).kill(); }, [addAnimation, index]); return <div className="box" ref={el}>{children}</div>; } function Circle({ children, addAnimation, index, rotation }) { const el = useRef(); // return a 'right 100px, rotate 360deg' tween useEffect(() => { const animation = gsap.to(el.current, { rotate: rotation, x: 100 }); addAnimation(animation, index); return () => animation.progress(0).kill(); }, [addAnimation, index, rotation]); return <div className="circle" ref={el}>{children}</div>; } function App() { // define a timeline const [tl, setTl] = useState(() => gsap.timeline()); // pass a callback to child elements, this will add animations to the timeline const addAnimation = useCallback((animation, index) => { tl.add(animation, index * 0.1); }, [tl]); return ( <div className="app"> <Box addAnimation={addAnimation} index={0}>Box</Box> <Circle addAnimation={addAnimation} index={1} rotation="360">Circle</Circle> </div> ); } See the Pen Passing down a callback to build a timeline. by GreenSock (@GreenSock) on CodePen. React Context Passing down props or callbacks might not be ideal for every situation. The component you're trying to communicate with may be deeply nested inside other components, or in a completely different tree. For situations like this, you can use React's Context. Whatever value your Context Provider provides will be available to any child component that uses the useContext hook. const SelectedContext = createContext(); function Box({ children, id }) { const el = useRef(); const selected = useContext(SelectedContext); useEffect(() => { gsap.to(el.current, { // animate x by 200 if the box ID matches the selected context value x: selected === id ? 200 : 0 }); }, [selected, id]); return <div className="box" ref={el}>{children}</div>; } function App() { // Any component can read the value passed to the provider, no matter how deeply nested. // In this example, we're passing "2" as the current value. return ( <SelectedContext.Provider value="2"> <Box id="1">Box 1</Box> <Box id="2">Box 2</Box> <Box id="3">Box 3</Box> </SelectedContext.Provider> ); } See the Pen React Tutorial 3c by GreenSock (@GreenSock) on CodePen. Imperative Communication Passing around props or using Context works well in most situations, but using those mechanisms cause re-renders, which could hurt performance if you're constantly changing a value, like something based on the mouse position. To bypass React’s rendering phase, we can use the useImperativeHandle hook, and create an API for our component. const Circle = forwardRef((props, ref) => { const el = useRef(); useImperativeHandle(ref, () => { // return our API return { moveTo(x, y) { gsap.to(el.current, { x, y }); } }; }, []); return <div className="circle" ref={el}></div>; }); Whatever value the imperative hook returns will be forwarded as a ref function App() { const circleRef = useRef(); useEffect(() => { // doesn't trigger a render! circleRef.current.moveTo(300, 100); }, []); return ( <div className="app"> <Circle ref={circleRef} /> </div> ); } See the Pen React Tutorial 3d by GreenSock (@GreenSock) on CodePen. Creating reusable animations Creating reusable animations is a great way to keep your code clean while reducing your app’s file size. The simplest way to do this would be to call a function to create an animation. function fadeIn(target, vars) { return gsap.from(target, { opacity: 0, ...vars }); } function App() { const box = useRef(); useLayoutEffect(() => { const animation = fadeIn(box.current, { x: 100 }); }, []); return <div className="box" ref={box}>Hello</div>; } For a more declarative approach, you can create a component to handle the animation. function FadeIn({ children, vars }) { const el = useRef(); useLayoutEffect(() => { gsap.from(el.current.children, { opacity: 0, ...vars }); }, []); return <span ref={el}>{children}</span>; } function App() { return ( <FadeIn vars={{ x: 100 }}> <div className="box">Box</div> </FadeIn> ); } See the Pen React Reusable 1 by GreenSock (@GreenSock) on CodePen. If you want to use a React Fragment or animate a function component, you should pass in a ref for the target(s). Using gsap.effects GSAP provides a way to create reusable animations with registerEffect() function GsapEffect({ children, targetRef, effect, vars }) { useLayoutEffect(() => { if (gsap.effects[effect]) { gsap.effects[effect](targetRef.current, vars); } }, [effect]); return <>{children}</>; } function App() { const box = useRef(); return ( <GsapEffect targetRef={box} effect="spin"> <Box ref={box}>Hello</Box> </GsapEffect> ); } See the Pen React Reusable 6 by GreenSock (@GreenSock) on CodePen. Exit animations To animate elements that are exiting the DOM, we need to delay when React removes the element. We can do this by changing the component’s state after the animation has completed. function App() { const boxRef = useRef(); const [active, setActive] = useState(true); const remove = () => { gsap.to(boxRef.current, { opacity: 0, onComplete: () => setActive(false) }); }; return ( <div> <button onClick={remove}>Remove</button> { active ? <div ref={boxRef}>Box</div> : null } </div> ); } See the Pen React fade out 1 by GreenSock (@GreenSock) on CodePen. The same approach can be used when rendering elements from an array. function App() { const [items, setItems] = useState([ { id: 0 }, { id: 1 }, { id: 2 } ]); const removeItem = (value) => { setItems(prev => prev.filter(item => item !== value)); } const remove = (item, target) => { gsap.to(target, { opacity: 0, onComplete: () => removeItem(item) }); }; return ( <div> {items.map((item) => ( <div key={item.id} onClick={(e) => remove(item, e.currentTarget)}> Click Me </div> ))} </div> ); } See the Pen React fade out 2 by GreenSock (@GreenSock) on CodePen. However - you may have noticed the layout shift - this is typical of exit animations. The Flip plugin can be used to smooth this out. In this demo, we’re tapping into Flip’s onEnter and onLeave to define our animations. To trigger onLeave, we have to set display: none on the elements we want to animate out. See the Pen React Flip 2 by GreenSock (@GreenSock) on CodePen. Custom Hooks If you find yourself reusing the same logic over and over again, there’s a good chance you can extract that logic into a custom hook. Building your own Hooks lets you extract component logic into reusable functions. Let's take another look at registerEffect() with a custom hook function useGsapEffect(target, effect, vars) { const [animation, setAnimation] = useState(); useLayoutEffect(() => { setAnimation(gsap.effects[effect](target.current, vars)); }, [effect]); return animation; } function App() { const box = useRef(); const animation = useGsapEffect(box, "spin"); return <Box ref={box}>Hello</Box>; } See the Pen React Reusable 7 by GreenSock (@GreenSock) on CodePen. Here are some custom hooks we've written that we think you may find useful: useSelector Memoises GSAP’s selector utility. see demo on codepen function useSelector() { const ref = useRef(); const q = useMemo(() => gsap.utils.selector(ref), [ref]); return [q, ref]; } Usage: function App() { const [q, ref] = useSelector(); useEffect(() => { gsap.to(q(".box"), { x: 200 }); }, []); return ( <div ref={ref}> <div className="box">Hello</div> </div> ); } useArrayRef Adds refs to an array. see demo on codepen function useArrayRef() { const refs = useRef([]); refs.current = []; return [refs, (ref) => ref && refs.current.push(ref)]; } Usage: function App() { const [refs, setRef] = useArrayRef(); useEffect(() => { gsap.to(refs.current, { x: 200 }); }, []); return ( <div> <div className="box" ref={setRef}>Box 1</div> <div className="box" ref={setRef}>Box 2</div> <div className="box" ref={setRef}>Box 3</div> </div> ); } useStateRef This hook helps solve the problem of accessing stale values in your callbacks. It works exactly like useState, but returns a third value, a ref with the current state. see demo on codepen function useStateRef(defaultValue) { const [state, setState] = useState(defaultValue); const ref = useRef(state); const dispatch = useCallback((value) => { ref.current = typeof value === "function" ? value(ref.current) : value; setState(ref.current); }, []); return [state, dispatch, ref]; } Usage: const [count, setCount, countRef] = useStateRef(5); const [gsapCount, setGsapCount] = useState(0); useEffect(() => { gsap.to(box.current, { x: 200, repeat: -1, onRepeat: () => setGsapCount(countRef.current) }); }, []); useIsomorphicLayoutEffect You might see a warning if you use server-side rendering (SSR) with useLayoutEffect. You can get around this by conditionally using useEffect during server rendering. This hook will return useLayoutEffect when the code is running in the browser, and useEffect on the server. caveat: Any "from" state that doesn't match the server-side rendered HTML/CSS content will still suffer from a flash of unstyled content while the JavaScript is being parsed, run and hydrated. read more about useLayoutEffect and server rendering see demo on codepen const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect; Usage: function App() { const box = useRef(); useIsomorphicLayoutEffect(() => { gsap.from(box.current, { opacity: 0 }); }, []); return ( <div> <div className="box" ref={box}>Hello</div> </div> ); } If there is anything you'd like to see included in this article, or if you have any feedback, please leave a comment below so that we can smooth out the learning curve for future animators. Good luck with your React projects and happy tweening!
  23. Hello There, I got a problem with my animation when I use the scrollTrigger for the second element. Also , I can't see the markers! any explanation ? Thanks
  24. "What was that method name again?" "Is the 2nd parameter supposed to be a number or a boolean?" Even seasoned GSAP users need reminders with such a rich API. The GSAP cheatsheet is a big help, but auto-completion and code hinting right in your editor can really boost your productivity. Here's how: This is all made possible by TypeScript declaration files which describe the GSAP methods and properties that are available and the data types they expect. We recommend that you use Visual Studio Code (VS Code) or WebStorm as they were the easiest editors to get it working in of the editors that we tried. If you're using modules... It's super simple to get auto-completion and hinting working in a modules environment: Install GSAP (via npm, yarn, whatever). For more information about how to do that, check out the module installation video. Import GSAP into your file(s). For more information about how to do that, see the GSAP Install Helper. That's it! In editors that support TypeScript declarations auto-completion and hinting should work automatically. If you're using plain JS files... Enabling auto-completion and hinting with regular JS files (in editors that support it) takes a few more steps: Make sure you're logged into GreenSock.com. Download the GSAP ZIP file from the GreenSock homepage or your account dashboard. Open up the ZIP file. Open up the "npm-install-this" directory. Open up the .tgz file inside of that directory. Copy the /types directory. Paste the /types directory into your project's root directory. Create an empty jsconfig.json file in your project's root directory. This will cause your editor to look for TypeScript declaration files. Sometimes it's necessary to restart your editor before it starts working. That's it! In editors that support TypeScript declarations auto-completion and hinting should work at this point. Some editors may require that you add support for TypeScript through third party packages. For example there's a TypeScript package for Sublime and one for Atom. You may need to install an additional package like these to get auto-completion and hinting working with GSAP in your editor. That's why we recommend Visual Studio Code (VS Code) or WebStorm. Before GSAP 3, GSAP didn’t have official Typescript declaration files so people commonly used ones from DefinitelyTyped also known as @types. Now that GSAP 3 does have official Typescript declaration files, you should NOT use the @types declarations. They are old and will not work with GSAP 3. If you find errors in the Typescript declaration files, find that some method is missing, or doesn’t have a good description, please let us know! You can create a post in the GreenSock forums or you can create an issue on GitHub. As always, if you have questions feel free to post in our forums.
×