I experienced some serious wonkiness with my first couple of attempts at the timeline line animation, admittedly almost entirely due to a lack of experience with this tool. I had trouble delineating which variables were necessary and which were superfluous.
// Timeline.js, in the same useEffect as the year Refs.
gsap.from('#line', {
scrollTrigger: {
trigger: 'body',
scrub: true,
start: 'top bottom',
endTrigger: '#footer',
end: 'top top',
snap: {
snapTo: yearRefs,
duration: { min: 0.2, max: 3 },
delay: 0.2,
ease: 'power1.inOut',
},
},
scaleY: 0,
transformOrigin: 'top, top',
ease: 'none',
});
...
<Line id='line />
...
const Line = styled.span`
width: 6px;
max-height: 181001px;
height: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 43px;
z-index: 1;
background-color: ${props => props.theme.colors.darkGreen};
@media ${props => props.theme.breakpoints.lg} {
left: calc(50% - 1px);
}
`;
If I remember correctly, this code resulted in a line that sprang from the middle of the timeline (quite a scroll down the page) that I could barely see pop into view over the bottom of the view port....headed in the wrong direction.
Seeking Assistance
I turned to the GSAP forum for help troubleshooting the behavior of my timeline line. At this point there was a ton of work on the project that was irrelevant to the line "animation" so I spent time removing code from the snippet I posted to the forum. I was delighted to find that someone had responded quite quickly, even if to ask that I repeat the process again in Codesandbox. The response that followed resulted in functionality that was the closest yet to what I desired. The timeline line and year refs were almost in sync.
// Timeline.js, in the same useEffect as the year Refs.
gsap.from('#line', {
scrollTrigger: {
scrub: true,
trigger: "#line-anchor",
start: "top center",
end: "bottom bottom",
endTrigger: "#footer",
},
scaleY: 0,
transformOrigin: "top, top",
ease: "none",
});
// TimelineKey.js
<Sticky id="line-anchor">
Pretty Close, but Not Fully There
-I translated this update to the repo, where I knew the behavior would be different due to the existence of multiple elements above the timeline ordered list (the header, nav, an h1), and was successful...or so I thought. I set the trigger to the id of the element that immediately preceded the timeline, set the endTrigger as the id of the footer, and committed. I was thrilled that it worked. However, in the coming days, I rarely saw the line animation again. Sometimes it would flash on the screen on initial page load before disappearing for the rest of that work session. Occasionally I could see it when Chrome DevTools was open, but not when it was closed. Sometimes I wouldn't see the line for days, despite not changing any code relevant to the scrollTrigger animation. I did not want to give up (we were so close!), but I was also running out of troubleshooting ideas. Was copying over everything in my local environment to Codesandbox for feedback from the folks in the forum going to be worth my time?
A Pure CSS Solution
Luckily we did not get to that point. A colleague had an idea that enabled me to continue to use scrollTrigger for the year refs, but implement a pure CSS solution for the line animation itself. The solution? Simply: an absolutely positioned element from the top to the bottom of the viewport that is always 50% of the height of the viewport. All that was left to do was increase the z-index of the header so the line would only show once the user scrolled down to where the timeline began.
// Timeline.js
...
<Line />
...
/* Timeline line denoting scroll position */
const Line = styled.span`
width: 6px;
height: 50vh;
position: fixed;
top: 0;
left: 43px;
z-index: 1;
background-color: ${props => props.theme.colors.darkGreen};
@media ${props => props.theme.breakpoints.lg} {
left: calc(50% - 1px);
}
`;
This commit here includes the changes: https://github.com/savaslabs/dlib/commit/9441f3c67ae987aad26694ba693d2f573a30e8a6.
Voila! The line and year refs are perfectly synced.
GSAP's ScrollTrigger met my needs for updating styling based on the scroll. However, there's still a lot I cannot explain about why the implementation of the timeline line was not as expected.