Jump to content

Search In
  • More options...
Find results that contain...
Find results in...


  • Posts

  • Joined

  • Last visited

Recent Profile Visitors

1,514 profile views

romain.gr's Achievements

  1. Hi, I'm currently testing Locomotive Scroll and Gsap ScrollTrigger, I've read somewhere on the forum that horizontal scroll pin using Locomotive scroll is not that possible (is that correrct?). In fact, each sections are translated, so even though position fixed is set it's still translated, furthermore I think the width of each section is calculated (by Locomotive Scroll) before the "pushing" padding to the section is set. I played with all the pin options (pinType, pinSpacing, pinnedContainer, pinReparent) , without success, I understand in a nutshell what those stuff fixes in general but I haven't been able to make that work properly. So I found a workaround but I was hoping for a "cleaner" way to achieve what I've done. The workaround is to create a wider section (200vw), inside of it a container (100vw), absolute positioned, and translate that container on progress, that work pretty good but I feel that is a hack and I was wondering if I could do it differently. I could use a timeline that I scrub play, but I don't think it's a better solution. The section I'm talking about are the ".section--curtain" one, you need to scroll at the end, that's the 2 fixed sections with curtains opening vertically and horizontally. 1. style="transform: translate(56.3%, 0%) scale(1.1877, 1.18767); translate instead of translate3d? Why? Shouldn't be smoother if translate3d was used? 2. The image is slightly zooming, again on progress but it's not very smooth. on line 160 $('.section--curtain--horizontal').each(function(){ var thisCurtainTop = $(this).find('.curtain--top'); var thisCurtainBottom = $(this).find('.curtain--bottom'); var curtainPin = $(this).find('.curtain-pin'); var thisMedia = $(this).find('.curtain-media'); gsap.to(thisCurtainTop, { yPercent: -100, ease: 'none', scrollTrigger: { trigger: $(this), scroller: '.scroll-wrapper', horizontal: true, scrub: true, //pin: true, //pinType: 'transform', //pinSpacing: 'margin', //pinnedContainer: curtainPin, start: 'left left', end: () => {return '+=' + window.innerWidth + 'px'}, //markers: true, invalidateOnRefresh: true, onUpdate: self => { var progress = self.progress.toFixed(3) * 100; //console.log("progress:", self.progress.toFixed(3) * 100); gsap.set(thisMedia, {xPercent: progress, zPercent: 0}); gsap.to(thisMedia, 1, {scale: 1 + progress / 300}) } } }); gsap.to(thisCurtainBottom, { yPercent: 100, ease: 'none', scrollTrigger: { trigger: $(this), scroller: '.scroll-wrapper', horizontal: true, scrub: true, //pin: true, //pinType: 'transform', //pinSpacing: 'margin', //pinnedContainer: curtainPin, start: 'left left', end: () => {return '+=' + window.innerWidth + 'px'}, //markers: true, invalidateOnRefresh: true } }); }); $('.section--curtain--vertical').each(function(){ var thisCurtainLeft = $(this).find('.curtain--left'); var thisCurtainRight = $(this).find('.curtain--right'); var curtainPin = $(this).find('.curtain-pin'); var curtainS = $(this).find('.curtains'); var thisMedia = $(this).find('.curtain-media'); var thisVid = $(this).find('video')[0]; gsap.to(thisCurtainLeft, { xPercent: -100, ease: 'none', scrollTrigger: { trigger: $(this), scroller: '.scroll-wrapper', horizontal: true, scrub: true, //pin: true, //pinType: 'transform', //pinSpacing: 'margin', //pinnedContainer: curtainPin, start: 'left left', end: () => {return '+=' + window.innerWidth + 'px'}, //markers: true, invalidateOnRefresh: true, onUpdate: self => { var progress = self.progress.toFixed(3) * 100; //console.log("progress:", self.progress.toFixed(3) * 100); gsap.set(thisMedia, {xPercent: progress, zPercent: 0}); gsap.set(curtainS, {xPercent: progress, zPercent: 0}); gsap.to(thisMedia, 1, {scale: 1 + progress / 300}); }, onEnter: () => { thisVid.play(); }, onEnterBack: () => { thisVid.play(); }, onLeave: () => { thisVid.pause(); }, onLeaveBack: () => { thisVid.pause(); }, } }); gsap.to(thisCurtainRight, { xPercent: 100, ease: 'none', scrollTrigger: { trigger: $(this), scroller: '.scroll-wrapper', horizontal: true, scrub: true, //pin: true, //pinType: 'transform', //pinSpacing: 'margin', //pinnedContainer: curtainPin, start: 'left left', end: () => {return '+=' + window.innerWidth + 'px'}, //markers: true, invalidateOnRefresh: true, } }); }); I'm happy if there is some examples, somewhere, hope everything is clear. Thank you
  2. Hi OSUblake, 👍 Thank you, that's what I want, I think I was confused with snap and clamp, and with the mapRange with 5 arguments, anyway thanks for the quick answer.
  3. Hi, I'm trying to mapRange a value (with minimum and maximum returned value), I though firstly that in mapRange(inMin, inMax, outMin, outMax, valueToMap), the outMin and outMax would be the maximum value returned by the method but in fact it maps the value after the outMax and before the outMin. So in : var zoom = gsap.utils.mapRange(.27, .5, 1, 1.2, zPos); as I understand (and that s how it works), it can returns value greater than 1.2 and smaller than 1. What I want is to clamp the values, so if it's greater than 1.2, the value is 1.2, if the value is smaller than 1, the value is 1; I've been trying to pipe the mapRanged value into clamp() or snap() but I guess I'm missing something or not doing it in the right order. I've read the doc and watched the vids, I'm unable to make it works. If anyone could help me on that it would be great. In the codepen, I'm using handsfree.js which tracks head position, so I'm tracking the distance between the head and the webcam (it returns a number between 0 and 1, 1 is when your head is very close to the webcam, anyway). line 46 : var zPos = data.weboji.translation[2]; then line 51 : var zoom = gsap.utils.mapRange(.27, .5, 1, 1.2, zPos); the line 59 : gsap.to('html', { '--video-blur': blur + 'px', '--video-overlay-opacity': opacity, '--video-overlay-zoom': zoom, '--video-overlay-blur': vidOverlayBlur + 'px', duration: .25}); Can someone help me to return a value no greater than 1.2 and not smaller than 1? Thank you
  4. Hi Jack, That's definitely what I want, thank you. I knew there was a more clever to do. However, I'm getting confused about your second point: You should always create your ScrollTriggers in the order they occur on the page so that they refresh() in the proper order. Alternatively, you can influence that with the refreshPriority. In your case, you want your back-next element to have the lowest priority (default is 0, so go negative). Isn't it already the case? I created the 2 ScrollTriggers in order they appears, does it need a refreshPriority if the 2 scrollTrigger are created in the right order? As I understand, if they are created in the right order, they gonna refresh in that same order, thus the refreshPriority is unneeded for the second one. Am I right? Thank you.
  5. Hi, I'm currently working on a website using Gsap ScrollTrigger. What I'm trying to achieve is showing a "previous-next post" button when the user has reached the bottom of the page. Which is working almost ok when the page is not resized. So on the page I have 2 scrollTriggers: 1. A horizontal gallery of images (with a media match) 2. The previous-next button. 1: let slider = document.querySelector('.hor-slide--project'); setTimeout(function(){ ScrollTrigger.matchMedia({ "(min-width: 992px)": function() { gsap.to(slider, { x: () => -(slider.scrollWidth - document.documentElement.clientWidth) + 'px', ease: 'none', scrollTrigger: { trigger: '.hor-slider-container', start: 'top top', //pinReparent: true, invalidateOnRefresh: true, pin: true, // scrub: .25, scrub: 0, // end: () => '+=' + slider.offsetWidth // end: 4000, end: () => slider.scrollWidth, } }); }, }); }, 1); 2: setTimeout(function(){ ScrollTrigger.create({ trigger: '.single-project', start: 'bottom bottom+=50px', //end: 'bottom bottom', invalidateOnRefresh: true, //markers: true, onEnter: self => { gsap.to('.prev-next', 1, {y: '-100%', ease: Expo.easeOut}) }, onLeaveBack: self => { gsap.to('.prev-next', .5, {y: '0', ease: Expo.easeOut}) } }); }, 100); I'm using setTimeout because in addition to gsap scrollTrigger I'm using Barba.js, and I need to delay, but it doesn't seem to be the problem, I removed the setTimeout and the problem still occurs. For the second scrollTrigger, I have used onEnter and onLeaveBack, this might not be the best way but that's the only way I found so far (I might be part of the problem), so 50px before the bottom of the '.single-project' block reach the bottom of the viewport (start: 'bottom bottom+=50px'), show the prev-next, otherwise hide it. The problem is that when I resize the window from mobile (< 992px) to desktop (> 992px), the prev-next button is now showing to early, before I reach the bottom of the page. Step to reproduce the bug: 1. Load the page on desktop, scroll to the bottom, the prev-next is showing (great) 2. Resize the window (< 992px), the first horizontal gallery scroll is "killed", scroll to the bottom, the prev-next is still showing at the right time (amazing) 3. Re-resize the window (> 992px), the first horizontal gallery is "recreated", and now if you scroll around the page, you can see prev-next showing too early, before you reached the end of the page. I tried refresh and also invalidateOnRefresh true or false, without success, it doesn't want to work. I guess I'm doing something wrong, probably the way I try to show the prev-next, using onEnter and onLeaveBack. Must be a more clever way to do that. Thank you.
  6. Hi, I'm currently working on a website using SrollTrigger and I'm having a couple of issues. Here is the website https://www.16saintgeorges.ch/demo/ Problem 1 : At some point I'd like to scroll horizontally some kind of slider using scrub, what I'm trying to do is to calculate the hidden part of the slider and on scroll change the x value, here is the code : $('.slider').each(function(){ var $thisSlider = $(this); var $thisSliderTrigger = $thisSlider.parents('.section--slider'); gsap.to($thisSlider, { scrollTrigger : { trigger: $thisSliderTrigger, //markers: true, start: 'bottom bottom', end: '500vh', scrub: .5, pin: true, pinReparent: true, invalidateOnRefresh: true, onEnter: function(){ setHeight(); }, onLeave: function(){ setHeight(); } }, x: function(){ var sliderW = $('.slider').outerWidth(true) - $('.slider-wrapper').outerWidth(true); return '-' + sliderW + 'px' }, }); }); As you can see I use a function to get the hidden part of the slider, it seems to be pretty logic to me but for some reasons, it doesn't calculate properly the hidden part of the slider. It's always different, you'll see if you resize the page, there is something wrong going on and I can't figure out what. Problem 2: I've been using scrollTrigger to do multiple parallax effects on images, it seems to work properly on most of them but on one of them the triggers are not where they should be, don't know why , I assume it's because up in the page there is a "pinReparent: true", and I have the feeling it's breaking slightly anything below that. To see the problem you need to click on the switch button (scroll a bit in the page then on the left), If you click on the switch button, you'll end up on the other side of the website (the light side), scroll to the bottom and just before the footer there is a section with just one image, I've added the markers so you can see that it's not well placed. The code says : when the top of .split-img hits the bottom of the viewport then move the img inside to 10vw (with scrub). I've been using that trick for other part of the website and it works perfectly. here is the code for that part: gsap.to('.split-img .img-cover', { scrollTrigger : { trigger: '.split-img', markers: true, start: 'top bottom', // endTrigger: '.slider', // end: 'top top', scrub: .1, //pin: '.section--2' //invalidateOnRefresh: true }, y: '10vw', // duration: 3 }); and the area : Problem 3: On reloading the page (by clicking on the reload icon in the browser), all the scrollTrigger are broken, but when I reload the page by reentering the url and hitting enter, it's working. Not sure what's going on. Hope everything is clear. Thank you
  7. Hi, I've been trying to use ScrollToPlugin but I'm unable to install it via npm. I was on the page https://greensock.com/docs/v2/NPMUsage, but it's confusing, is the ScrollToPlugin installed when you do npm install gsap? That's how looks my import but it seems that the ScrollToPlugin is missing, and I can't figure out how to install it : import { gsap, Expo } from 'gsap/all'; import ScrollToPlugin from 'gsap/all'; gsap.registerPlugin(ScrollToPlugin); When I do gsap.to(window, { duration: 2, scrollTo: centerVidPos }); I have a console log error saying : gsap-core.js:3323 Uncaught TypeError: Failed to execute 'scrollTo' on 'Window': parameter 1 ('options') is not an object. Do I even need to register the plugin after importing it? Is there some complete documentation on the gsap website? How can I install the ScrollToPlugin via npm and how to use import/register it? Completly confused.
  8. Hi Zach, yep it makes sense, I understand a bit better the different steps, but not your new codepen, first I need to digest the first option, I think I will avoid the second version as it does the exact same thing than the first, it's adding confusion more than help . For the first option it still works only once. I'm still unable to play more than once the timeline. Thank you
  9. Hi Zach, Thank you, to be honest I find your code less readable or clear, I really don't understand how the closing nav part works. What does addPause() do if you already paused the animation when you set it (paused: true), then why repeat: -1? I don't find that very explicit, how do you make the animation closed? Here : gsap.utils.toArray('.trig-nav, .close-nav').forEach(function(btn) { btn.addEventListener("click", function(e) { e.preventDefault; if(!navTL.isActive()) { navTL.play(); } }); }); Where does it say "play the animation till the .addPause" then on clicking on the close btn, go to the end of the timeline? It's not clear. Plus I'm unable the play the animation more than once, when the nav is closed and you click on the '.trig-nav' nothing happens. Could you confirm that just adding a small offset fixes the problem on my codepen? Thank you
  10. I don't think it's related to clip-path as the whole nav block is set to display none after the close nav animation is finished, so even if it has clip-path on it, it's display none, clip-path shouldn't interfer with it as it's in display none, but maybe I'm doing a mistake thinking that. The problem is that 'opacity: 1', 'visibility: visible' and 'display: flex', are re-setted to the nav when the close nav animation is finished, I think the problem is in the onComplete callback. Anyway when clip-path is unselected in the dev console, the nav is still showing. I had a closer look and it seems that closeNav.to('.main-nav', 1, {autoAlpha: 0, display: 'none'}); Doesn't really work as it fade-out then put back again opacity: 1. I'm not really sur what's happening. Could you just have a quick look if it work as it should on modern browser. I'm getting confused now...
  11. Hi, I'm trying to do a simple navigation using gsap and it works greats, but on some browser I have a little issue, let me explain, I have 2 different animations, One to open the navigation : - Animating the clip-path value - then a fade-in stagger on the navigation items and one to close the navigation : - The whole navigation block fade-out then display not. Here is the code but you can obviously have a look at the codepen : // Nav function restartOpenNav(){ openNav.restart().pause(); } function restartCloseNav(){ closeNav.restart().pause(); } var navIsClosed = true; var openNav = gsap.timeline({paused: true, onComplete: restartCloseNav}); var closeNav = gsap.timeline({paused: true, onComplete: restartOpenNav}); openNav .set('.main-nav', {autoAlpha: 1, display: 'flex'}) .fromTo('.main-nav', 1.5, {clipPath:'polygon(0% 0%, 100% 0%, 100% 100%, 100% 0%)'}, {clipPath:'polygon(0% 0%, 0% 100%, 100% 100%, 100% 0%)', ease: Expo.easeInOut}) .from('.main-nav li', 1, {rotation: '2deg', autoAlpha: 0, y: '20px', stagger: .1, ease: Expo.easeOut}, '-=1'); closeNav.to('.main-nav', 1, {autoAlpha: 0, display: 'none'}); $('.trig-nav').on('click', function(e){ e.preventDefault; if(navIsClosed){ navIsClosed = false; //$(this).addClass('trig-nav--is-open'); openNav.play(); $('body').css('height', '100vh').css('overflow', 'hidden'); //console.log('open Nav'); } else if (!navIsClosed && !openNav.isActive()) { navIsClosed = true; //$(this).removeClass('trig-nav--is-open').blur(); closeNav.play(); $('body').attr('style', ''); //console.log('close Nav'); } }); $('.close-nav').on('click', function(e){ e.preventDefault; navIsClosed = true; // $(this).removeClass('trig-nav--is-open').blur(); closeNav.play(); $('body').attr('style', ''); // console.log('close Nav'); }); As you can see onComplete of the openNav the closeNav animation is restarted same when the closeNav is restarted. All works great except on IE11 and Safari IOS Iphone6 (at least those 2, maybe on other browsers), when the navigation in closed (the whole block fades out) and the close animation is finished the white background reappear suddently without the items, I have no ideas why it does that. Once again it works as expected on modern browser but not IE11. Did I miss something? If anyone has the occasion to try the demo on IE11 or Safari Ios on iphone 6 and can spot the problem it would be great. Thank you
  12. Hi Craig, Thanks for that
  13. Hi Zack, sounds logic Got the point for the duration, thank you again.
  14. Hi Zach, Thank you for your answer, I'm just wondering if it wouldn't be more simple to use .set() if you want to set the transform-origin (like on the first tween) and use .to() if you want to animate it? I know the answer is no, but it looks like you are adding an extra step to achieve that, if .set() is made to set and .to(), fromTo(), from() is made to animate. Cool little extra advices, about the duration, what if the whole animation is longer than 2 sec, in this case it works but in case of longer animation what's going to happen? or the duration (2) is set for each tween or for the whole timeline? Thank you