Share Posted August 31, 2021 I hope it's okay to ask for help here, even though my problem has nothing to do with an actual GSAP issue. I'm working on this horizontal slider, and right now the parallax image movement (line 89) is just based on the draggable element's total progress. That works fine when the carousel has just a few images, but when there are many images, the amount of movement that is visible as each box crosses the stage becomes too small. And lots of the image is never actually seen. At first I thought this would be pretty simple, but I've been fruitlessly struggling to figure out an equation/setup that will make any number of slides have the same amount of parallax motion, and still be responsive to the full browser width. See the Pen 44639788dd72f4e26b8c8b52cb096480?editors=0110 by creativeocean (@creativeocean) on CodePen 1 Link to comment Share on other sites More sharing options...
Solution Solution Share Posted August 31, 2021 Well, CodePen has been down for a while so I can't provide a forked demo but here's how I'd do the JS: let n = 15; let parallax = []; // we'll store the animations in here. let clamp = gsap.utils.clamp(0, 1); let currentX = 0; let snap = gsap.utils.pipe(gsap.utils.snap(450), gsap.utils.clamp(n * -450, 0)); // Set #slides width for draggable bounds gsap.set('#slides', {width:n*450}); // Populate slide boxes for (var i=0; i<n; i++){ var box = document.createElement('div'), img = new Image(), link = document.createElement('a'); gsap.set(box, { width:400, height:600, overflow:'hidden', position:'absolute', top:50, left:i*450, attr:{ class:'box b'+i }, background:'#333' }); gsap.set(img, { position:'absolute', left:-300,//-i*50, attr:{src:'https://picsum.photos/id/'+(i+10)+'/700/600/'} }); parallax[i] = gsap.to(img, {x: 300, ease: "none", paused: true}); gsap.set(link, { position:'absolute', textAlign:'center', width:105, height:70, paddingTop:'7px', top:490, left:-25, rotation:90, fontSize:'45px', color:'#000', background:'#fff', mixBlendMode:'lighten', textDecoration:'none', innerHTML:'<span style="font-size:20px">IMG </span>'+(i+1), attr:{ class:'imgLink', href:'https://picsum.photos/id/'+(i+10)+'/700/600/', target:'_blank' }, }); box.appendChild(img); box.appendChild(link); slides.appendChild(box); } // Make #slides draggable Draggable.create('#slides', { type:'x', bounds: {left: innerWidth/2, width:1}, zIndexBoost: false, onDrag:updateParallax, inertia: true, onRelease: function() { currentX = this.endX }, onThrowUpdate: updateParallax, snap: snap }) function updateParallax() { // parallax should start from the right edge of the screen and we know that the #slides starts with its left edge centered, thus we add innerWidth/2 let x = gsap.getProperty('#slides', 'x') + window.innerWidth / 2, // convert the position in the viewport (right edge of viewport to -400 because that's when the right edge of the element would go off-screen to the left) into a progress value where it's 0 at the right edge and 1 when it reaches the left edge normalize = gsap.utils.mapRange(window.innerWidth, -400, 0, 1); // apply the clamped value to each animation parallax.forEach((animation, i) => animation.progress(clamp(normalize(x + i * 450)))); } updateParallax(); // Update draggable bounds onResize window.addEventListener('resize', ()=>{ Draggable.get("#slides").applyBounds({left: innerWidth/2, width:1}) }); // Previous & next buttons $('#prev, #next').on('click', function(e) { let nextX = snap(currentX + (e.currentTarget.id === "next" ? -450 : 450)); if (nextX !== currentX) { gsap.to("#slides", {x: nextX, duration: 0.3, onUpdate: updateParallax, overwrite: true}) currentX = nextX; } }); $('#prev, #next').on('mouseenter', (e)=>{ gsap.to('#'+e.currentTarget.id + ' circle', {attr:{r:22}, ease:'expo', overwrite: true}) }); $('#prev, #next').on('mouseleave', (e)=>{ gsap.to('#'+e.currentTarget.id + ' circle', {attr:{r:20}, ease:'expo', overwrite: true}) }); // Img Link rollover/out behavior $('.imgLink').on('mouseenter', (e)=>{ gsap.to(e.currentTarget, {x:10, duration:0.3, ease:'power3', overwrite: true}) }); $('.imgLink').on('mouseleave', (e)=>{ gsap.to(e.currentTarget, {x:0, duration:0.3, ease:'power4.inOut', overwrite: true}) }); The general idea is: You only want the parallax effect to exist while each individual element is inside the viewport (not the entire movement of the #slides). I created a simple linear animation of x from 0 to 300 for EACH element. Paused. Dumped them into an Array. The updateParallax() function loops through each one and sets the progress() according to its position (which we know because they're 450px apart). It's all based on the viewport so that progress would be 0 when it's on the far right edge of the screen and 1 when the element's right edge reaches the left edge of the viewport. I also made the following improvements: I applied inertia with snapping directly on the draggable so it's super smooth and users can flick it. The logic in the next/previous buttons allows users to click the buttons quickly and it still works (instead of ignoring clicks while animation is running). Sorry, that's my pet peeve when the interface ignores user clicks. I hope that helps, Tom! 6 Link to comment Share on other sites More sharing options...
Author Share Posted September 1, 2021 Wow! Jack, I didn't expect this much support and improvement. There's a lot to review/digest/learn here, but I'm very grateful for the help and guidance. Codepen is now back online and I've forked the previous pen (and made it public), now using your JS: See the Pen OJgNyVm?editors=0010 by creativeocean (@creativeocean) on CodePen Turned out pretty slick, if I do say so myself! Go team! 5 Link to comment Share on other sites More sharing options...
Share Posted September 1, 2021 Happy to help, @creativeocean. It's the least I could do for all the cool stuff you did for our home page (and for those who don't know, Tom is the mind behind the animations at the top of the home page). 🙌 2 Link to comment Share on other sites More sharing options...
Share Posted September 1, 2021 Really cool Tom and great assist by Jack. 👍 4 hours ago, GreenSock said: The logic in the next/previous buttons allows users to click the buttons quickly and it still works (instead of ignoring clicks while animation is running). Sorry, that's my pet peeve when the interface ignores user clicks. I had to laugh at this. I made a slider once that ignored clicks while the animation was running and Jack gave me an assist on part of it and said the same thing. "I want the buttons to work no matter what." Busted. 🤣 3 Link to comment Share on other sites More sharing options...
Share Posted September 1, 2021 Ha. Sorry, maybe it's just a personal pet peeve of mind that nobody else really cares about. If I click 5 times quickly, I want it to advance 5 chunks, not 1. Nobody has ever accused me of being picky, stubborn, or opinionated...ever. 🤥 5 Link to comment Share on other sites More sharing options...
Share Posted September 1, 2021 I was actually glad you did that. Most of my slider type work since then listens for clicks even if there's action on the screen. 3 Link to comment Share on other sites More sharing options...
Share Posted September 2, 2021 This is a wonderful thread and a great result. Nice job gang. 2 Link to comment Share on other sites More sharing options...
Share Posted September 30, 2021 amazing work. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now