Jump to content
Search Community

romain.gr

Premium
  • Posts

    88
  • Joined

  • Last visited

Everything posted by romain.gr

  1. Hi Jack, Thanks for your answer, it definitely works now but I have no idea how it has been done : triggers.forEach((t) => t.start <= window.pageYOffset && t.vars.onEnter && t.vars.onEnter()); I understand nothing about this line, it's way too abstract for me, t.start, t.vars.onEnter and t.vars.onEnter(), what's that? Could you break down this line, and explain each bits? So I can understand what it says exactly. Plus is there some documentation somewhere (on gsap forum, on the web or on the gsap website) explaining this whole issue, I guess I'm not the only one wanting to do that. I checked the ScrollTrigger.isInViewport(), but it only check if the item is in view (or if x% of the item is in view), it would be great if we could pass start: 'top 90%', to it. It doesn't work in my case cause I want the class to be added when my item touches 90% of the viewport and not when my item is 90% (or 10% or whatever) in the viewport. I'm I wrong? Thank you.
  2. Hi, I have a little problem with the ScrollTrigger.refresh() function, actually it doesn't work as I was expecting to. As I mention in the title, when I use it, it doesn't affect items that has already passed the scroller-start marker. So on the demo, I'm filtering a list of items, then I refresh the scrollTrigger and only the items after the start marker works. Here is my scrollTrigger using create (on page load it works perfectly, the class is added to the items passed the start 'top 90%') : $('.list-item').each(function(){ var $this = $(this); ScrollTrigger.create({ trigger: $this, markers: true, start: 'top 90%', onEnter: function(){ console.log($this); $this.addClass('list-item--is-active'); }, }); }); Then I'm filtering my item using Mixitup (not sure this info is relevant) : mixer = mixitup('.list', { selectors: { target: '.list-item' }, animation: { duration: 0 }, controls: { live: true, toggleDefault: 'all', toggleLogic: 'or' }, callbacks: { onMixEnd: function(state){} } }); var listOfFilters = []; $('.filters button').on('click', function(e){ var thisFilter = $(this).data('filter-item'); $(this).blur().toggleClass('btn--active'); $('.list-item').removeClass('list-item--is-active'); if(listOfFilters.includes(thisFilter)){ const index = listOfFilters.indexOf(thisFilter); listOfFilters.splice(index, 1); } else { listOfFilters.push(thisFilter); } gsap.to('.list', {autoAlpha: 0, onComplete: function(){ if(listOfFilters.length){ mixer.filter(listOfFilters.toString()); } else { mixer.show(); } gsap.to('.list', {autoAlpha: 1}); ScrollTrigger.refresh(); // setTimeout(function(){ // ScrollTrigger.refresh(); // gsap.to('.list', {autoAlpha: 1}); // }, 1000) }}); }); So on click on filter button (line 38 on codepen), I remove the .list-item--is-active class from all my .list-item (line 43); Then on line 52 I use gsap to hide my list -> on the complete : make the filtering -> then show the list and refresh the scrollTrigger. The refresh should add the .list-item--is-active to the element at the top of the list, like it does on page load, but it doesn't, why? So on the demo : - load the page, - scroll to the bottom - scroll back to top - then start filtering and unfiltering the list using the top buttons You'll see that it doesn't work very well. I'd like to know why and is it possible to make it work? Thank you.
  3. Hi, I'm trying to make something super basic but I can't find anything in the snap documentation that does that. Is it possible to choose the amount of scroll between each block (eg: in percentage) before the snapping happens? Here in my example, as soon as the first block touches the top of the viewport , it snaps directly to the second block/slide. Overall, I find the snapping feeling not very natural or, and it's possible I'm not using it as I should. On a side note, there is always a delay when the user stop scrolling and the snapping, even if it's only few milliseconds, you can feel it and it's not nice, and this feeling is even more present when coupled with a smooth scroll. I've seen snapping on websites that feels right, I'm wondering if they are using snap for that, cause I've been playing with it for long long time and I never end up with something that feels right. Anyway, in my case I would like : 1) not snapping directly to the second slide, but wait that I scrolled 50vh and then snap. 2) avoid that weird delay explained before. 3) to stay on each block/slide a certain amount of time (of scroll?, this is related to point 1) before switching to the next one (in both direction), I tried multiple stategies here, make the next blocks bigger and add padding-top (so you need to scroll more before accessing the next slide), add delay in the tweens, nothing works. I'm pretty sure I'm not using the right strategy here, maybe snap is not the tool for what I want to do, and I can't find anything on the forum that fit my use case, tho it sounds like something very basic (make that smooth). So if there is a post on this forum or a codepen example that would be great. Thank you.
  4. Hi @GreenSock, Thanks for your quick answer and sorry for my late reply, I had to switch to something else. Anyway, I tried and actually the new version of observer works as expected, I don't really understand the other version, but I'll check about that delayedCall and pause(), at least for another use. Thanks again ;)
  5. Hi, I'm trying to implement Gsap Observer, but I find the documentation unclear on one little point. So the doc says : onChange : Function - function to call when there is movement on either the y-axis (vertically) or the x-axis (horizontally). It will keep calling the function as long as the movement continues (subject to any tolerance threshold). I understand that when there is a mouse movement (type: 'pointer'), it should be triggered, but nothing happens, the onChange function is only triggered if I click and then move. That's not what the documentation says. It says "when there is movement" only. Actually I would like to catch the velocity when the cursor moves (and only moves), apply the velocity to something, and then, when the cursor stops, do something else. So I used onMove, it works great, It seems to be design to do exactly what I want. The problem is that the onStop event doesn't trigger when the mouse stops to move. The onStop event, in my case, only works with onChange, but as I said previously, the onChange event only trigger when I drag (click and move). Is there a onStopMove event? Observer.create({ target: '.first-screen', type: 'pointer', onChange: (e)=>{ //only works when mouse pressed var vel = e.velocityX * Math.sign(e.velocityX); gsap.to(document.querySelector('video'), {playbackRate: vel /1000}); }, onMove: (e)=>{ //triggered when mouse mouse hover '.first-screen' var vel = e.velocityX * Math.sign(e.velocityX); gsap.to(document.querySelector('video'), {playbackRate: vel /1000}); }, onStop: (e)=>{ // this only triggers when onChange stops, I WANT THIS TO WORK WHEN THE CURSOR STOPS MOVING console.log('STOP'); gsap.to(document.querySelector('video'), {playbackRate: 1}); }, }); Thank you. I made a codepen, it's very confusing: https://codepen.io/romaingranai/pen/abPqyza The onMove and onChange trigger when I move the cursor over the red square, but the onStop only triggers, when I click, move and then release.
  6. Hi mvaneijgen, Great, Thank you, that works perfectly! For the Blur/velocity issue, I did some simple stuff that seems to work (so far). I have added a onLeave and onLeaveBack which tween the svg attr to 0, when left and also checking the velocity "power", if not enough velocity do nothing otherwise blur the stuff. Here is the code : servicesCarousel(); function servicesCarousel(){ var numOfItem = $('.services__carousel__cell').length; gsap.to('.services__carousel', { scrollTrigger: { trigger: $('.services__carousel').parents('.section'), start: 'top top', end: () => {return '+=' + window.innerHeight * 1.5}, scrub: .0001, pin: true, // markers: true, snap: { snapTo: 1 / (numOfItem - 1), // snapTo: [0, .25, .5, .75, 1], duration: {min: 0.3, max: 1}, // directional: true, ease: Expo.easeOut, }, onLeave: () => { gsap.to('#test', .25, { attr: { stdDeviation: '0 0'} }); }, onLeaveBack: () => { gsap.to('#test', .25, { attr: { stdDeviation: '0 0'} }); }, onUpdate: function(self){ var direction = self.direction; var velocity = self.getVelocity() * direction; if(velocity > 300){ // console.log('MORE THAN 300') gsap.to('#test', .25, { attr: { stdDeviation: '0 ' + velocity / 150 } }); } else { gsap.to('#test', .25, { attr: { stdDeviation: '0 0' } }); } }, onSnapComplete: function(){ gsap.to('#test', .25, { attr: { stdDeviation: '0 0'} }); } }, '--rotate-x': 288 + 'deg', ease: 'none', }); }; And the updated pen with your advices : https://codepen.io/romaingranai/pen/jOXaKXb?editors=0010 Thanks again !
  7. Hi, I've been trying again and again without any success, I must be missing something. I have a simple scrollTrigger animation, my first question is: - How to make snap to work? I followed the example in the documentation which say 1 / (sections - 1) But it never stops at 25% (50%, 75%, ...), never, I have 5 "slides", it never snaps to the center of the item. What 's my mistake? Question number 2 : Is there something like onStopUpdate or onStopScroll (or any other technique)? Because I'm using onUpdate to get the scroll velocity and then use the value to tweek a svg filter. The problem is that even after stop scrolling, sometime the value stays at, let say 20 (see picture), and the filter is still applied to the element. As far as I understand, the velocity should go from 0 to something then back to 0 when stopping to scroll, if I log the velocity I never see that, It's not very smooth. I know I could use onSnapComplete but, first, I'm unable to use snap properly, and second it won't solve my problem in case I don't use snap, and third, it's seem more like a hack rather than a true solution. function servicesCarousel(){ var numOfItem = $('.services__carousel__cell').length; gsap.to('.services__carousel', { scrollTrigger: { trigger: $('.services__carousel').parents('.section'), start: 'top top', end: () => {return '+=' + window.innerHeight * 1.5}, scrub: .0001, pin: true, // markers: true, snap: { snapTo: 1 / (numOfItem - 1), // snapTo: [0, .25, .5, .75, 1], duration: {min: 0.3, max: 1}, // directional: true, ease: Expo.easeOut, }, onUpdate: function(self){ var velocity = self.getVelocity(); var velocity = velocity < 0 ? velocity * -1 : velocity; // var vel = gsap.quickTo('#test', 'stdDeviation', {duration: 0.4, ease: 'power3'}); // vel('0 ' + velocity / 100); gsap.to('#test', .5, { attr: { stdDeviation: '0 ' + velocity / 100 } }); }, // onSnapComplete: function(){ // gsap.to('#test', .5, { attr: { stdDeviation: '0 0'} }); // } }, '--rotate-x': 288 + 'deg' }); }; Thank you!
  8. Hi @Rodrigo, Thank you for your detailed answer! All clear. I finally managed to do it but without using the snap functionality, cause I needed to scroll up and down. The solution was based on another example found on the forum by @ZachSaucier (the codepen : https://codepen.io/GreenSock/pen/NWxNEwY , and the thread: Here is the (a) solution : let panels = gsap.utils.toArray('.block-scroll'), scrollTween; function goToSection(i) { scrollTween = gsap.to(window, { scrollTo: {y: i * innerHeight, autoKill: false}, duration: 1, ease: Power3. easeOut, onComplete: () => scrollTween = null, overwrite: true }); } panels.forEach((panel, i) => { ScrollTrigger.create({ //markers: true, trigger: panel, start: 'top 75%', end: 'top 25%', onEnter: self => { self.isActive && !scrollTween && goToSection(i+1); console.log(panel); }, onEnterBack: self => { self.isActive && !scrollTween && goToSection(i); } }); }); Anyway, thanks for your quick answer.
  9. Hi, I'm implementing a very simple example of snap based on this demo https://codepen.io/GreenSock/pen/KKpLdWW. The problem is that if directional is set to false I have to scroll half the screen to be snapped to the next section (which is way too long), if directional is set to true, as soon as I scroll up or down I'm snapped to the previous or next section (which is way too short), the ideal would be to decide how much you've been scrolled before the snapping triggers (eg: 25% of the screen). I've been reading the doc and few other posts on this forum without really finding an exact answer or if it's at least doable. I start to think that I shouldn't start from the gsap demo and rather think differently, so is there a way to achieve that? Is there another way to approach this? the js code for the problem starts at line 215. Thank you.
  10. Thank you for all your answers, @OSUblake, I had a quick look at your pen, it works very well for your used case, when only a word is selected, when the 'circle' stay always almost the same (i mean, not too spread, cause obviously without the vector-effect="non-scaling-stroke" the path stroke width is bigger on the edges) and I might use your example for similar kind of work. But I might have an idea, I could create multiple paths, all having a different width, and call them depending on how many words my span contains, let say that I create 10 different path in Illustrator from small to wide, and insert them in the dom based on the number of letter contains in a span. That might be the solution, cause I want to keep some king of flexibility (giving the client the possibility to surround any word in a sentence in the back-end administration) that vector-effect="non-scaling-stroke" gave me but obviously without the down side of that technique. I'll post the result here when I'm working on it again. Obviously I did not ignore the warning in the console, I was just wondering if, somehow, it was possible to (re)calculate the length of that path manually by dark mathematic functions, as suggested by @Cassie Thank you for your help and directions.
  11. Hi @Cassie, I actually stumble across that thread on stack overflow during my researches, tried to integrate the solution to my case, without success. Thank you for trying ;).
  12. Hey @mvaneijgen, it's used to "scale" the path, if you scale it without vector-effect="non-scaling-stroke", the path will look squashed, by using it it resize the path but not the stroke (the first codepen has been updated, 2 exact same path). That way it would be super flexible and I could create, let say 20 different paths (using the same logic : a 100/100 svg parent, thus the path points are expressed in %, that way the path could adapts to any word length, that actually works if the path is not animated, + it's responsive). But more I think about it and more I'm wondering how can the stroke 'thickness' can affect the length of the path.
  13. Hi @mvaneijgen, thanks for you answer, but your example is missing a few things. 1. I'm using illustrator with the pen, the d attribute of the path looks pretty much the same as yours. 2. you remove everything in your example, the shape needs to surround some text, and adapt itself to the word it surrounds. 3. the vector-effect="non-scaling-stroke" and preserveAspectRatio="none" are missing from the path and the svg element, and for sure those attributes are causing the miscalculation of the path length. I quickly remade your example to include all those parameters and I have the same issue, without changing your path: https://codepen.io/romaingranai/pen/jOYVaOp Thank you
  14. Hi, I've trying to achieve something and I'have read plenty of gsap forum threads on the subject without finding the solution. On the codepen you can see that some words are surrounded by a svg path stroke, I'm trying to animate that line, like a line drawn by a pen around a word. But it looks like the path length is not correctly calculated by GSAP (?), I first thought about a browser inconsistency (as read in other threads), but it's doing the same on all browsers. The problem is fixed by removing preserveAspectRatio="none" on the svg element, but obviously it's not what I try to do. You can see the end line is drawn at the same time as the start line. Not really sure if it's doable as I really checked all around the forum, if someone has another way to achieve this and keeping the flexibility of resizing the path to the word width and height. Thank you
  15. Any clues? What could be the problem? are my ScrollTriggers created in the wrong order? Could anyone redirect me to any post on the forum? It drives me crazy!
  16. I've been searching around an answer without success, I thought first it was an issue with the order I create the scrollTrigger but apparently not as creating them in different order doesn't fix the issue. So what I'm trying to achieve is: 1: logo animation then when the animation is finished, and the block unpinned 2: when the logo touch the top of the viewport => do something So I have 2 scrollTrigger created first one that scrub the logo and then the second one that check when the logo is touching the top of the screen, unfortunately the second one starts way too earlier (you can see the markers). logoAnim = gsap.timeline(); ScrollTrigger.create({ animation: logoAnim, trigger: '.header', pin: true, start: 'top top', end: ()=>{ '+=' + window.innerHeight / 2 }, scrub: .5 }); logoAnim .addLabel('letter-dis') .to('.letter--is-leaving--severin', {autoAlpha: 0, xPercent: -10, ease: Expo.easeOut, stagger: { amount: 1, from: 'end' }}, 'letter-dis') .to('.letter--is-leaving--anja', {autoAlpha: 0, xPercent: 10, ease: Expo.easeOut, stagger: { amount: 1, from: 'start' }}, 'letter-dis') .fromTo('.letter--plus', {width: '5em'}, {width: '1em', duration: 1.5}, 'letter-dis'); ScrollTrigger.create({ trigger: 'h1', start: 'top top', end: '+=5000000000', markers: true, onEnter: () => { $('.top-bar').addClass('top-bar--visible'); $('h1').addClass('invisible'); }, onLeaveBack: () => { $('.top-bar').removeClass('top-bar--visible'); $('h1').removeClass('invisible'); } }); What is happening? Thank you The documentation says about the refresh Priority : number - it's VERY unlikely that you'd need to define a refreshPriority as long as you create your ScrollTriggers in the order they'd happen on the page (top-to-bottom or left-to-right)...which we strongly recommend doing. Otherwise, use refreshPriority to influence the order in which ScrollTriggers get refreshed to ensure that the pinning distance gets added to the start/end values of subsequent ScrollTriggers further down the page (that's why order matters). See the sort() method for details. A ScrollTrigger with refreshPriority: 1 will get refreshed earlier than one with refreshPriority: 0 (the default). You're welcome to use negative numbers too, and you can assign the same number to multiple ScrollTriggers. I conclude that if ScrollTrigger are created in the order they appears on the page, the pinning distance of the first is being added to the start/end of subsequent Scrolltriggers, which should be my case. But no pinning distance is added to my second scroll trigger! Why? I', getting super confused.
  17. Hi @Akapowl, Thanks for the help, the only solution that worked in my situation is this thread, cause the 2 other ones doesn't really fix the issue of having multiple scrollTrigger later. So the solution is to refresh all scrollTrigger. Thank you!
  18. Hi, As the title says, I'd like to play a scrubbed/pinned animation only once and as soon as it finished clear up the inline css from the pinned item and remove the pin spacer parent from the dom, so when you scroll up again no animation occurs and I don't have have to scroll '3 windows.innerHeight' to reach the previous section. I actually managed to do it BUT : - I'm wondering if it's the proper way to do? Maybe there is a built in solution? - Is it going to cause issues if I have other scrollTrigger down in the page? I thought that just Killing the anim would do the job but I might not read the doc properly altought it says : "Kills the ScrollTrigger instance, immediately unpinning and restoring any pin-related changes made to the DOM by ScrollTrigger and removing all scroll-related listeners, etc." But actually it's not working that way, it does "cancel" it, but it doesn't clear up the pinned item and doesn't delete the pin-spacer. So I killed the dropCard anim, and clearProps on both the section and the pinSpacer. So here is the code : let dropCard = gsap.timeline({ onComplete: ()=> { // When the whole anim is finished initDrag(); // init draggable items dropCard.kill(); // Kill THIS ScrollTrigger gsap.set('.section--drop-card', {clearProps: 'all'}); // Clear up this section from added CSS from GSAP gsap.set($('.section--drop-card').parent('.pin-spacer'), {clearProps: 'all'}) // Clear up this section PIN PARENT from added CSS from GSAP }, // yes, we can add it to an entire timeline! scrollTrigger: { trigger: '.section--drop-card', pin: true, // pin the trigger element while active start: "top top", // when the top of the trigger hits the top of the viewport end: () => { return "+=" + window.innerHeight * 3}, // end after scrolling 500px beyond the start scrub: 1, // smooth scrubbing, takes 1 second to "catch up" to the scrollbar markers: true } }); dropCard .from('.card', {scale: 2, 'border-width': '0.05vw', rotateX: (i)=>rotationX[i], rotateY: (i)=>rotationY[i], duration: 1, ease: Power3.easeIn, stagger: .2}, 'start') .from('.card', {autoAlpha: 0, duration: .5, ease: Power3.easeIn, stagger: .2}, 'start'); Thank you.
  19. Yes, I guess we would need to find the longer staggered tween then find the other stagger and use the amount property to split the animation. I don't have example right now, and in my project I'm not planning using multiple staggers, but I'll definitely make a little demo in the next couple of days to test, try to fix that and share it here for help or to show how it might be done! Thank you again.
  20. @OSUblake Got it now, it starts at the same time but obviously the stagger takes longer. And the way to sync all tweens to start and end at the same time, is to use the duration attribute on simple tweens. But what if we have multiple stagger in the same timeline? Sorry for earlier answer, my bad, it was a long long day. I re-red it and tried, it clearly answers my question, so thanks for it and the detailed answer later.
  21. No duration has been set, I'm using scrub and I've set the end trigger value to twice the screen width. I don't really want to create 2 different timelines and 2 scrollTrigger, I d like to use only one timeline and one scrollTrigger which include a simple tween and a stagger animation. Is it because I have stagger: .25? As i understand the duration is related to the scroll distance (in this case the end), the duration is how fast you scroll (?). Is it possible to achieve the result of animation 2 (2nd screen in the codepen) but only using one timeline and one scrollTrigger?
  22. Hi, There is something that doesn't make sens to me, I'm animating a timeline with stagger and other tweens, all must be "played" in the same time, I'm using for that the delay label : "sameTime" $('.section--title-1').each(function(){ var thisSpan = $(this).find('span'); var thisSquare = $(this).find('.square'); let titleTL = gsap.timeline({ scrollTrigger: { trigger: $(this), scrub: true, pin: true, pinSpacing: true, start: 'left left', end: () => {return '+=' + window.innerWidth * 2 + 'px'}, //markers: true, invalidateOnRefresh: true, refreshPriority: 1 } }); titleTL .to(thisSpan, {autoAlpha: 1, y: 0, ease: Power1.easeInOut, stagger: .25}, 'sameTime') .to(thisSquare, {x: -window.innerWidth + 100 + 'px'}, 'sameTime'); }); So the desired behaviour would be : the end of the square tween will happen at the same time as the end of the staggered animation, but that's not the case, the square tween finished before the span animation. I've found a workaround which is creating 2 different timelines, one for the stagger and one for the square animation : $('.section--title-2').each(function(){ var thisSpan = $(this).find('span'); var thisSquare = $(this).find('.square'); let titleTL = gsap.timeline({ scrollTrigger: { trigger: $(this), scrub: true, pin: true, pinSpacing: true, start: 'left left', end: () => {return '+=' + window.innerWidth * 2 + 'px'}, //markers: true, invalidateOnRefresh: true, refreshPriority: 1 } }); let squareTL = gsap.timeline({ scrollTrigger: { trigger: $(this), scrub: true, pin: true, pinSpacing: true, start: 'left left', end: () => {return '+=' + window.innerWidth * 2 + 'px'}, //markers: true, invalidateOnRefresh: true, refreshPriority: 1 } }); titleTL .to(thisSpan, {autoAlpha: 1, y: 0, ease: Power1.easeInOut, stagger: .25}, 'sameTime'); squareTL .to(thisSquare, {x: -window.innerWidth + 100 + 'px'}, 'sameTime'); }); The second version works great but I found that it's : 1: not very logic 2: a bit ugly Is there a way to make the first way working as I'm expecting or there's something I don't understand? Thank you
  23. Hi, Just wanted to say that if anyone experiences wider scroll than the content using GSAP and Locomotive scroll, here is 2 reasons why it can happen : 1. An unclosed tag somewhere, couldn't believe I missed it! And very strangely 2. When you leave a markers: true in your scrollTrigger. So more you have markers to debug, more wide will be the "further-scroll". Just remove the markers: true.
  24. Hi @akapowl I've made some changes, following your advices and that works very nicely! Thank you. Except for one thing, it scrolls further the last slide/section. It seems the scroll distance is not correctly calculated. It drives me nut for hours and it's not the first time I have that issue with Loco scroll, and for info nothing is depending on images size (so no prob with image not loaded), as everything is size in css in vw,...! I literally copy/past and adapt your code adapted it with my content. It's definitely a Loco scroll issue so I'll ask there if nobody has an idea on what s going here. Thank you! https://codepen.io/romaingranai/pen/vYJmPqO
  25. Hi akapowl, Great, I think I got it, it's quite simple, instead of translating each sections using data-scroll-section attribute, you translate the whole content, and then you can pin it when you want. That's perfect, I actually thought that the only way to use Loco Scroll was to was adding those attributes to each sections, my bad. Thanks, for the quick explanation, makes total sens. I'm going to have a look at that rn. Thanks
×
×
  • Create New...