如何将smooth-scrollbar.js指定到.content容器而非body,修复自定义光标滚动不跟随问题
Got it, let's fix that cursor sync problem by shifting the smooth-scrollbar target from body to your .content container. Here's the step-by-step adjustment plus the full working code:
Key Modifications Needed
1. Update Smooth-Scrollbar Initialization
Change the target from document.body to your .content container when initializing Scrollbar.
2. Adjust ScrollTrigger Proxy & Defaults
Update the ScrollTrigger scroller proxy to point to the .content container, and set it as the default scroller for ScrollTrigger.
3. Tweak CSS Layout
Make body fixed (so it doesn't scroll natively) and set .content as the full-viewport scrollable container.
Modified Full Code
JavaScript
const cursor = document.querySelector('#cursor'); const cursorCircle = cursor.querySelector('.cursor__circle'); const mouse = { x: -100, y: -100 }; // mouse pointer's coordinates const pos = { x: 0, y: 0 }; // cursor's coordinates const speed = 0.1; // between 0 and 1 const updateCoordinates = e => { mouse.x = e.clientX; mouse.y = e.clientY; } window.addEventListener('mousemove', updateCoordinates); function getAngle(diffX, diffY) { return Math.atan2(diffY, diffX) * 180 / Math.PI; } function getSqueeze(diffX, diffY) { const distance = Math.sqrt( Math.pow(diffX, 2) + Math.pow(diffY, 2) ); const maxSqueeze = 0.15; const accelerator = 1500; return Math.min(distance / accelerator, maxSqueeze); } const updateCursor = () => { const diffX = Math.round(mouse.x - pos.x); const diffY = Math.round(mouse.y - pos.y); pos.x += diffX * speed; pos.y += diffY * speed; const angle = getAngle(diffX, diffY); const squeeze = getSqueeze(diffX, diffY); const scale = 'scale(' + (1 + squeeze) + ', ' + (1 - squeeze) +')'; const rotate = 'rotate(' + angle +'deg)'; const translate = 'translate3d(' + pos.x + 'px ,' + pos.y + 'px, 0)'; cursor.style.transform = translate; cursorCircle.style.transform = rotate + scale; }; function loop() { updateCursor(); requestAnimationFrame(loop); } requestAnimationFrame(loop); const cursorModifiers = document.querySelectorAll('[cursor-class]'); cursorModifiers.forEach(curosrModifier => { curosrModifier.addEventListener('mouseenter', function() { const className = this.getAttribute('cursor-class'); cursor.classList.add(className); }); curosrModifier.addEventListener('mouseleave', function() { const className = this.getAttribute('cursor-class'); cursor.classList.remove(className); }); }); gsap.registerPlugin(ScrollTrigger); ScrollTrigger.defaults({ markers: false }); // --- Modified Part --- // Initialize smooth-scrollbar on .content container instead of body const scrollContainer = document.querySelector('.content'); var scrollbar = Scrollbar.init(scrollContainer, { delegateTo: document, alwaysShowTracks: false, speed: 0.7, damping:0.1 }); // Update ScrollTrigger proxy to use the content container ScrollTrigger.scrollerProxy(scrollContainer, { scrollTop(value) { if (arguments.length) { scrollbar.scrollTop = value; } return scrollbar.scrollTop; } }); scrollbar.addListener(ScrollTrigger.update); // Set default scroller to the content container ScrollTrigger.defaults({ scroller: scrollContainer }); // --- End Modified Part ---
CSS
#cursor { position: fixed; z-index: 99; left: 0; top: 0; pointer-events: none; will-change: transform; mix-blend-mode: difference; } @media (hover: hover) and (pointer: fine) { .cursor__circle { width: 64px; height: 64px; margin-top: -50%; margin-left: -50%; border-radius: 50%; border: solid 1px rgba(227, 222, 193, 0.64); transition: opacity 0.3s cubic-bezier(0.25, 1, 0.5, 1), background-color 0.3s cubic-bezier(0.25, 1, 0.5, 1), border-color 0.3s cubic-bezier(0.25, 1, 0.5, 1), width 0.3s cubic-bezier(0.25, 1, 0.5, 1), height 0.3s cubic-bezier(0.25, 1, 0.5, 1); } #cursor.arrow .cursor__circle{ width: 128px; height: 128px; background-color: #073099; border-color: #073099; } #cursor.arrow::after{ content: ''; position: absolute; left: -50%; top: -50%; width: 100%; height: 100%; background-image: url('https://svgshare.com/i/MzQ.svg'); background-repeat: no-repeat; background-position: center; } #cursor.subtle .cursor__circle{ opacity: 0.16; } #cursor.overlay .cursor__circle{ width: 48px; height: 48px; background-color: rgba(227, 222, 193, 0.08); border-color: transparent; } } * { margin: 0; padding: 0; border: none; outline: none; text-decoration: none; } /* --- Modified Part --- */ html, body { width: 100%; height: 100%; overflow: hidden; /* Prevent native body scroll */ } .content { width: 100%; height: 100vh; /* Fill viewport height */ overflow: hidden; /* Let smooth-scrollbar handle scrolling */ } /* --- End Modified Part --- */ section{ position: relative; display: flex; align-items: center; justify-content: center; background-color: #f7f7f7; height: 100%; /* Ensure sections fill content container */ } section::after{ content: 'Only for laptop or desktop 🙅♂️'; position: fixed; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0.7); font-family: 'Poppins', sans-serif; font-size: 1rem; color: #1c213e; line-height: 1.7; } .container{ width: 33vw; } .image-container{ overflow: hidden; cursor: pointer; width: 100%; height: 75%; /* margin-bottom: 0.5vw; */ margin-top: 10vw; } .image-container img{ width: 100%; height: 100%; object-fit: cover; filter: brightness(95%); transition: transform 0.6s cubic-bezier(0.25, 1, 0.5, 1); } .text{ font-family: 'Poppins', sans-serif; font-size: 1rem; color: #1c213e; line-height: 1.7; } .underline{ position: relative; display: inline-block; cursor: pointer; } .underline::after{ content: ''; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px; background-color: #1c213e; opacity: 0.32; transition: opacity 0.3s cubic-bezier(0.25, 1, 0.5, 1); } .button{ position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; font-family: 'Poppins', sans-serif; color: #1c213e; } .button.icon-button{ background-color: transparent; width: 40px; height: 40px; margin: -8px; } .info{ display: flex; align-items: center; justify-content: space-between; } @media (hover: hover) and (pointer: fine) { .image-container:hover img{ transform: scale(1.1); } .underline:hover::after{ opacity: 1; } section::after{ display: none; } }
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/smooth-scrollbar/8.7.4/smooth-scrollbar.js"></script> <script src="https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js"></script> <script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/gsap-latest-beta.min.js"></script> <div id="cursor"> <div class="cursor__circle"></div> </div> <div class="content"> <section> <div class="container"> <div class="image-container" cursor-class="arrow"> <img src="https://i.ibb.co/HDvyzVW/vase-2.jpg" alt="minimalist vase"> </div> </div> </section> <section> <div class="container"> <div class="image-container" cursor-class="arrow"> <img src="https://i.ibb.co/HDvyzVW/vase-2.jpg" alt="minimalist vase"> </div> </div> </section> </div>
Why This Works
When smooth-scrollbar was bound to body, it was replacing the native scroll behavior, which caused your fixed-position cursor to lose sync because the page's scroll offset wasn't being tracked correctly. By moving the scroll context to the .content container:
- The
bodystays fixed, so your cursor (positionedfixed) remains anchored to the viewport correctly. - Smooth-scrollbar handles scrolling inside the
.contentcontainer, which doesn't interfere with the cursor's position tracking. - ScrollTrigger is properly linked to the new scroll container, ensuring any scroll-based animations still work as expected.
内容的提问来源于stack exchange,提问作者gjjr

