GSAP ScrollTrigger水平滚动问题求助:单次滚动失效且行数缺失
Hey there! Let's work through those two issues with your GSAP ScrollTrigger horizontal scrolling setup. I’ve gone through your code and here’s how to fix both problems:
1. Fixing Single Scroll Trigger for Horizontal Switching
The main conflict here is that your vertical section-jump logic (the goToSection function and its ScrollTriggers) interferes with the horizontal scrolling pin section. When you scroll over the 6th panel, those vertical triggers still try to jump vertically instead of letting the horizontal scrub do its job. Here's the fix breakdown:
- Exclude the pin panel from vertical ScrollTriggers: Skip creating vertical triggers for the
.pinmepanel so it doesn't get pulled into the vertical jump behavior. - Enable snap for horizontal scrolling: Uncomment the
snapproperty in your horizontal ScrollTrigger config—this makes each scroll gesture snap to the next row, creating that single-switch per scroll effect. - Prevent vertical jumps in the pin section: Add a check in
goToSectionto avoid triggering vertical scrolls when targeting the pin panel. - Adjust horizontal ScrollTrigger end value: Calculate the end based on the container's actual scroll width (instead of just its set width) for more accurate horizontal scroll bounds.
2. Fixing 5 Rows Only Showing 4
Your .pinme container is set to width:700%, but let's count the inner elements: 1 .sixth header + 5 .rows = 6 elements each taking 100% width. Setting width:600% ensures all elements fit perfectly without extra space pushing the 5th row out of view.
Modified Code Snippets
JavaScript
gsap.registerPlugin(ScrollTrigger); function goToSection(i, anim) { // Skip vertical jump if targeting the pinme section const targetPanel = gsap.utils.toArray(".panel")[i]; if (targetPanel.classList.contains("pinme")) return; gsap.to(window, { scrollTo: { y: i * innerHeight, autoKill: false }, duration: 1 }); if (anim) { anim.restart(); } } gsap.utils.toArray(".panel").forEach((panel, i) => { // Don't create vertical ScrollTriggers for the pinme panel if (panel.classList.contains("pinme")) return; ScrollTrigger.create({ trigger: panel, onEnter: () => goToSection(i) }); ScrollTrigger.create({ trigger: panel, start: "bottom bottom", onEnterBack: () => goToSection(i) }); }); // Target all direct children of pinme (Six header + 5 rows) let sections = gsap.utils.toArray(".pinme > div"); gsap.to(sections, { xPercent: -100 * (sections.length - 1), ease: "none", scrollTrigger: { trigger: ".pinme", pin: true, scrub: 1, snap: 1 / (sections.length - 1), // Enable snap for single-scroll switching // Calculate end based on full scroll width minus viewport width end: () => "+=" + (document.querySelector(".pinme").scrollWidth - window.innerWidth) } });
CSS
html, body { margin: 0; padding: 0; height: 100%; font-family: "Signika Negative", sans-serif, Arial; } .blue { background-color: blue } .red { background-color: #f00; } .orange { background-color: orange; } .purple { background-color: purple } .green { background-color: green } .gray { background-color: gray } .panel { height: 100vh; position: sticky; top: 0; } h2 { font-size: 50px; color: #fff; padding: 0; margin: 0; } .rows { width: 100%; height: 100vh; background-color: #000; } .pinme { width: 600%; /* Adjusted to fit 6 full-width elements */ height: 100%; display: flex; flex-wrap: nowrap; } .sixth { width: 100% !important }
HTML
(No changes needed—your existing structure with the .sixth header and 5 .rows elements is correct)
How This Works
- Vertical ScrollTriggers now ignore the pin panel, so when you scroll into it, the horizontal scrub takes over seamlessly.
- The
snapproperty ensures each scroll gesture locks to the next horizontal section, creating the single-switch per scroll behavior you want. - The adjusted
.pinmewidth ensures all 5 rows plus the header fit without extra space hiding the last row. - The dynamic end calculation for the horizontal ScrollTrigger ensures the scroll ends exactly when the last horizontal section is fully in view.
内容的提问来源于stack exchange,提问作者Naren Verma

