Jump to content
Search Community

ScrollTrigger controlled scrub through and array of elements in a forEach Loop need to all initiate at the same time.

Sean Ravenhill test
Moderator Tag

Go to solution Solved by Cassie,

Recommended Posts

Hello good people of GSAP,

I come to you in desperation and with a brain that has become scrambled egg!!!
Please someone, anyone out there!

Help me Obi-wan Kenobi! You are my only hope!!! 😋

What I'm trying to achieve here in this Codepen is that there is no delay between the card animations. They all need to fire at the same time and be scrubbed by the ScrollTrigger which is setup where the main timeline duration is from when the top of the viewport enters the top of the section element and ends when the top of the viewport hits the bottom of the section element.

Now, I think I would be able to get the math and logic correct, if I was working with a timeline that is fired once and then runs through and that my cards were static in terms of the project build and I could just use durations.

However, this particular animation is proving troublesome for me to solve, in that it needs to be dynamic as in my current project I'm using VueJS to dynamically pull in the cards, which will be generated and injected from a CMS (Wordpress with ACF - Advanced Custom Fields) and then are styled and positioned in the layout using TailwindCSS.

Here is my logic below, in a "Pseudo code" numbered list:
 

  1. I have an Array of elements.
  2. ScrollTrigger controls the animation timelines as I scroll from the top to the bottom of the the section element.
  3. 0% - Each card in that array needs to dynamically calculate where it is, relative to the centre of the viewport. This is also the case for the following keyframes after.
  4. 50% - Each card animates on the x-axis to where the centre of the card and the centre of the viewport intersect.
  5. 75% - In that last half (left half of viewport) the cards need to start alternating on their animation path on the y-axis and begin to rotate at random values from -360 to 360 degrees.
  6. 100% - Each card needs to have exited to the left from the viewport but it's right most edge will be flush to the outer left edge of the viewport.

 

PS, I see @Carl is active on here sometimes... Thank you Sir, for your amazing endeavour with Creative Code Club. My lifetime membership purchase got me this far... I'm sure once I've finished all the courses, this will no longer be something I can't resolve on my own... but... DEADLINES are Looming. I'm certainly going to devour the rest of you awesome material because... it is so easy to follow and understand, and SO MUCH FUN to see what we can create with code! Deeper down the rabbit hole I shall dive!

PPS, I also see @Cassie is the GSAP Super Boss Level 99 Hero... long time old friend 😉 fancy meeting you here! And that both our lives have led us down the path to becoming coders! Best decision of my life to leave behind the graphic design and advertising world and enter into logical creative problem solving with objective outcomes and goals 😃, it looks like you are loving it too and shooting the lights out! That is so awesome to see!

 

PPPS, I have a tendency to overcomplicate my logic and code (I am but still a Padawan Coder)... if you can show me a better way. Please do! I love to learn from the Jedi Coders!
 

Please feel free to review the code here before you jump into the Codepen:
 

function viewportWidth() {
    return window.innerWidth;
}

function randomRotate() {
    return Math.round(gsap.utils.random(-270, 270));
}

function animateTitle() {
    const topTitle = document.getElementById('topTitle')
    const bottomTitle = document.getElementById('bottomTitle')
    
    let tl = gsap.timeline()
    tl.add('start')
        .to(topTitle, { x: -viewportWidth(), y: -viewportWidth() }, 'start')
        .to(bottomTitle, { x: -viewportWidth(), y: viewportWidth(), autoAlpha: 1 }, 'start')
    return tl
}

function animateCards() {
    const cards = document.querySelectorAll('#projects #card')
    let tl = gsap.timeline()
    let cardStart = []
    let halfCardWidth = []
    let viewportWidth = []
    let viewportCenter = []
    let cardToCenter = []
    let cardToLeftQuarter = []
    let cardOffScreen = []
    let alternate = []
    const yDirection = [-(window.innerWidth / 1.5), window.innerWidth / 1.5]

    cards.forEach(function(card, index) {
        halfCardWidth.push(document.getElementById('card').offsetWidth / 2)
        viewportWidth.push(window.innerWidth)
        viewportCenter.push(window.innerWidth / 2)
        cardStart.push(card.getBoundingClientRect().left + (window.scrollX || document.documentElement.scrollLeft || 0))
        cardToCenter.push(cardStart[index] - viewportCenter[index] + halfCardWidth[index])
        cardToLeftQuarter.push(cardToCenter[index] + (viewportCenter[index] / 2))
        cardOffScreen.push( ( cardToCenter[index] + ( viewportCenter[index] / 2 ) )  + (viewportCenter[index] / 2) + halfCardWidth[index] )
        alternate.push(index % 2)
        tl.to(card, {
            keyframes:{
                '0%': {x: 0},
                '50%': {x: -cardToCenter[index], y: 0, rotation: 0, ease: "linear", autoAlpha: 1},
                '75': {x: -(cardToLeftQuarter[index]), y:yDirection[alternate[index]] / 2, rotation: randomRotate(), ease: "linear", autoAlpha: 1},
                '100%': {x: -cardOffScreen[index], y:yDirection[alternate[index]] ,autoAlpha: 0}
            }
        })
        return tl
    })
    return tl
}

let masterTimeLine = gsap.timeline({
    scrollTrigger: {
        trigger: document.getElementById('project-carousel'),
        start: 'top top',
        end: 'bottom top',
        scrub: true,
        pin: true,
        markers: {fontSize: "18px", fontWeight: "bold"},
    }
})

masterTimeLine.add('start')
    .add(animateTitle(), 'start')
    .add(animateCards(), 'start')

 

See the Pen wvxwryG by seanravenhill (@seanravenhill) on CodePen

  • Like 1
Link to comment
Share on other sites

Fancy seeing your name pop up! 

I was like.... that's not a common name 👀 Lovely to see you in here! Where are you living nowadays?

 

Quote

What I'm trying to achieve here in this Codepen is that there is no delay between the card animations. They all need to fire at the same time


So this you can do pretty simply with a position parameter - but I'm not certain that's exactly what you're after?

See the Pen OJwJZMQ?editors=0010 by GreenSock (@GreenSock) on CodePen


I've given your demo a refactor, maybe we can use this one to brainstorm about how you want them to behave? It's a little clearer.

See the Pen ZEjEoWd?editors=1010 by GreenSock (@GreenSock) on CodePen

  • Like 3
Link to comment
Share on other sites

@Cassie Indeed! The world truly works in mysterious ways!

I've been back in RSA living in Cape Town since 2013.

Just had a look through both your pens... 🤯! Especially the second one. That is such a purrrty and elegant way to approach it!
(I over complicated things... didn't I? 🤪)

Okay, so both your versions are super close to what I am trying to achieve.

Let me try explain it a little differently. If you can image a Train or a Conveyor-belt. Those cards need to move in-sync with each other, but that spacing between them needs to remain the same (Essentially that space is the Gap or Stagger if you want to call it that)... And then they shoot off alternatively in the second part of the animation *Once the centre of each card pass the left side of the centre of the view port.

Hope that makes it a bit more clear 😁

PS, can we DM on here? Would be awesome to get your deets and reconnect. I've been off all social media save for LinkedIn since 2016. 

and THANK YOU SO MUCH!!!!

For your assistance. You are my Nomi Sunrider or Yaddle 😁  https://screenrant.com/star-wars-disney-female-jedi-powerful/

Link to comment
Share on other sites

Hi Sean,

Glad to hear my courses have helped and that @Cassie was able to clean it up and do all the hard stuff.

Might not be understanding perfectly but perhaps this gets you a little closer

 

See the Pen ExpxRwb?editors=1010 by snorkltv (@snorkltv) on CodePen

 

I basically gave each card its own timeline and then added that to the main timeline at incremented position values.

 

However, with your layout it will be difficult to maintain equal spacing throughout the animation as your layout has each box starting in a row which means some boxes are starting farther away than others and thus would need longer durations. 

 

Right now each card's animation is the same duration which means some boxes move faster (cover more distance over the same time).

 

I would suggest starting each box in the same position, but I didn't know how to edit your layout and css.

 

In face this kind of reminded me of my slime conveyor belt lesson which is basically taking the same approach 

 

See the Pen abyBbXZ by snorkltv (@snorkltv) on CodePen

 

 

Also I noticed that you are using ids like #card multiple times which probably isn't good. I would suggest using classes.

 

Anyway, hopefully this helps get you closer.

  • Like 2
Link to comment
Share on other sites

@Carl Thank you so much for your response and edits to the code.

Between yourself and @Cassie you have both gotten me very close to the intended end result.
 

Quote

Also I noticed that you are using ids like #card multiple times which probably isn't good. I would suggest using classes.

Thank you for pointing that out, I completely overlooked that it's no "no bueno" to have multiple instance of the same id!
 

Quote

I would suggest starting each box in the same position, but I didn't know how to edit your layout and css.

That would be the magic that is Tailwind. I would highly recommend trying it out, it goes in the face of "old school" thinking that flooding your elements with classes, styles or other attributes, is not good practise. But once you see the benefit of it, you'll be hard pressed to go back to writing custom css. 

It also works an absolute treat with component based workflows and libraries such as Vue.js

Thank's for the breadcrumbs and signal posts so far 😁!

🤞 I can take what you have both helped me with and fine tune it to what it is that I'm trying to explain.

  • Like 1
Link to comment
Share on other sites

@Carl My pleasure, I hope you find it helps you in future projects and speeds up your workflow.

So I was being very foolish. I actually have an example of what I'm trying to achieve.

This is precisely what I am trying to recreate.

https://tympanus.net/Development/HorizontalSmoothScrollLayout/index4.html

This it what happens when you are into hour 14 of a coding session 🤪... could have saved myself a good 7 hours of that if I had just asked the right questions and shown the right examples. Ah well... we live, we learn.

I did give the code another bash with the awesome pointers you provide me. So close 🥺

See the Pen MWBWBar by seanravenhill (@seanravenhill) on CodePen



... okay... I gotta get some rest to hit it hard again tomorrow. Wash, Rinse, Repeat!
 

Link to comment
Share on other sites

On 12/19/2022 at 8:42 PM, Sean Ravenhill said:

PS, can we DM on here? Would be awesome to get your deets and reconnect. I've been off all social media save for LinkedIn since 2016.

Yeah sure! I've mainly been on twitter... but I imagine I'll be running away to LinkedIn soon. Twitter feels increasingly volatile.

Codewise - Just FYI, for future demos in the forums, tailwind makes things a little harder for people to debug and edit, it's another abstraction that not everyone knows - so try to strip back to just vanilla CSS/JS where possible to ease the load on people who want to help!

 

On 12/19/2022 at 8:42 PM, Sean Ravenhill said:

Just had a look through both your pens... 🤯! Especially the second one. That is such a purrrty and elegant way to approach it!
(I over complicated things... didn't I? 🤪)

💚 Your logic was pretty sound, you just didn't have the GSAP / viewport unit cheat-codes!

The best bit of advice I ever got from anyone is not to worry about doing it the right way. Just get it to work first, even if it's convoluted, then you can clean it up if you need to.

  • Like 1
Link to comment
Share on other sites

2 hours ago, Cassie said:

Codewise - Just FYI, for future demos in the forums, tailwind makes things a little harder for people to debug and edit, it's another abstraction that not everyone knows - so try to strip back to just vanilla CSS/JS where possible to ease the load on people who want to help!

Will do for the future! Thanks for the tip.

Link to comment
Share on other sites

@Cassie

I've taken your solution above and tweaked it a bit for what I believe are my needs.

All that I have left to figure out is how to get that each of those triggers now to move in unison with the rest of the animation.

I've been cracking at it for about 2 hours now, and much confess I need some help across the finish line.

See the Pen VwBwgJN by seanravenhill (@seanravenhill) on CodePen

Edited by Sean Ravenhill
Needed to be more clear in my explination
Link to comment
Share on other sites

Nope this isn't going to work.

In order to use container animation you need to have a horizontally scrolling container animation. 'scrollTween' - That's where the magic happens. 🥳

This seems like a kind of hybrid approach between the two different demos? Why have you chosen to do that? Help me understand. Are you limited to these exact styles and markup?

  • Like 1
Link to comment
Share on other sites

13 minutes ago, Cassie said:

This seems like a kind of hybrid approach between the two different demos? Why have you chosen to do that? Help me understand. Are you limited to these exact styles and markup?


Yes very much so, limited to having to stick with Tailwind and that Markup.

The combination of Tailwind and Vue.js is the front-end Tech Stack.

*The benefit is that you work in a component build system ie. Vue.js and React.js

And then with this CSS framework, you avoid merge conflicts, and as it's not a custom style.css teams are able to quickly and easily pick up a component and work on, again without their own biases or approaches to writing CSS.

Link to comment
Share on other sites

Basically, you just have to bump along those projects by 50vw and you're golden. 

Tailwind doesn't have vw though* - or at least it only lets you do full width... I popped some divs in to show you the general idea.
 https://tailwindcss.com/docs/width#viewport-width 

You can seemingly extend your config to add vw to tailwind?

https://stackoverflow.com/questions/65759571/from-css-to-tailwind-concersion-of-vw-and-50-for-full-with-image-inside-a-f

  • Like 1
Link to comment
Share on other sites

@Cassie I would have agreed with you on CSS frameworks. BootStrap, Zurb, Tailwind etc. before...

But when I built this site https://www.becausexm.com/ all with custom css...

I truly appreciated why CSS frameworks are a good idea.

I would liken it to using GSAP as you framework to handle animations.

Of course you can write own custom code... but once a project hits a certain scale, and the maintenance goes beyond the scope of a team of max 3 people (Thumb sucked that number... but this is what I think) it becomes a nightmare... overwrites, bugs etc.

Maybe give Tailwind another go? I think you'll be pleasantly surprised when it clicks.

Link to comment
Share on other sites

1 hour ago, Cassie said:

Basically, you just have to bump along those projects by 50vw and you're golden. 

Tailwind doesn't have vw though* - or at least it only lets you do full width... I popped some divs in to show you the general idea.
 https://tailwindcss.com/docs/width#viewport-width 

You can seemingly extend your config to add vw to tailwind?

https://stackoverflow.com/questions/65759571/from-css-to-tailwind-concersion-of-vw-and-50-for-full-with-image-inside-a-f

 

* This is one of my issues with tailwind. It's basically CSS but with inexplicably less CSS than the web platform. 


https://tailwindcss.com/docs/width#:~:text=w-screen,%3A 100vw%3B 😁

https://tailwindcss.com/docs/width#:~:text=w-screen,%3A 100vw%3B

@Cassie Maybe these little tid-bits will entice you to take a deeper dive into Tailwind (*This message is not sponsored by Tailwin 😋)

I believe it gives you approximately 67 unique utility classes, just for widths, out the box.

Then you can still add whatever you want in the tailwind config file... and yet you can still add custom css in the style.css

So for example this would give you a block that is 50%

<div class="w-1/2"></div>	


And if you wanted to use something arbritary that you are only going to use once and don't need to go to the hassle of setting up config files or a style.css

You can do this.
 

<div class="w-[30vw]"></div>


Hope this is of interest 👍

Link to comment
Share on other sites

I know you're excited about it and just want to rave and get people on board. It's really great when you find something that helps you - but just a little note - if someone says that they've tried something and don't like it, saying they need to dive deeper for it to click comes off a little patronising.

My opinion isn't due to lack of comprehension. 

I understand it's benefits and the problems it solves, I just personally don't think that it's the only way around those issues! That's ok though, people are allowed to have differing opinions!

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...