You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过onscroll事件触发平滑滚动?参考页面效果实现求助

Hey Ryan, let's fix that scroll-triggered smooth section scrolling you're aiming for! It looks like you were on the right track with listening to the scroll event, but we need to add some guardrails and refine the logic to avoid jank and ensure it locks to each viewport-sized section just like your reference page.

Core Idea

Instead of just tracking scroll position, we need to:

  1. Detect scroll direction (up/down)
  2. Prevent multiple scroll triggers while we're already animating
  3. Calculate the exact target position (a full viewport height multiple)
  4. Animate smoothly to that target

Full React Implementation

Here's a polished version of the code that addresses these points:

import React, { Component } from 'react';

class SectionScroll extends Component {
  constructor(props) {
    super(props);
    this.isAnimating = false; // Block new scroll triggers during animation
    this.lastScrollPos = 0;
    this.viewportHeight = window.innerHeight;
  }

  componentDidMount() {
    window.addEventListener('scroll', this.handleScroll);
    // Update viewport height if window resizes
    window.addEventListener('resize', () => {
      this.viewportHeight = window.innerHeight;
    });
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScroll);
    window.removeEventListener('resize', () => {
      this.viewportHeight = window.innerHeight;
    });
  }

  handleScroll = () => {
    if (this.isAnimating) return; // Skip if we're already scrolling smoothly

    const currentScrollPos = window.pageYOffset || document.documentElement.scrollTop;
    const scrollDir = currentScrollPos > this.lastScrollPos ? 'down' : 'up';
    let targetPos = 0;

    // Calculate target position based on scroll direction
    if (scrollDir === 'down') {
      targetPos = Math.ceil(currentScrollPos / this.viewportHeight) * this.viewportHeight;
    } else {
      targetPos = Math.floor(currentScrollPos / this.viewportHeight) * this.viewportHeight;
    }

    // Exit if we're already at the target position
    if (targetPos === currentScrollPos) {
      this.lastScrollPos = currentScrollPos;
      return;
    }

    this.isAnimating = true;
    this.smoothScroll(targetPos);
    this.lastScrollPos = currentScrollPos;
  };

  smoothScroll = (target) => {
    const currentPos = window.pageYOffset || document.documentElement.scrollTop;
    const distance = target - currentPos;
    const scrollStep = distance / 30; // Adjust this to change scroll speed (smaller = slower)

    // Stop animation when we're close enough to the target
    if (Math.abs(distance) < 1) {
      window.scrollTo(0, target);
      this.isAnimating = false;
      return;
    }

    window.scrollTo(0, currentPos + scrollStep);
    requestAnimationFrame(() => this.smoothScroll(target));
  };

  render() {
    return (
      <div className="scroll-wrapper">
        {/* Make sure each section fills the entire viewport */}
        <section style={{ height: '100vh', background: '#f8f9fa', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '2rem' }}>
          Section 1
        </section>
        <section style={{ height: '100vh', background: '#e9ecef', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '2rem' }}>
          Section 2
        </section>
        <section style={{ height: '100vh', background: '#dee2e6', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '2rem' }}>
          Section 3
        </section>
      </div>
    );
  }
}

export default SectionScroll;

Key Details to Note

  • isAnimating Flag: This prevents the scroll event from firing repeatedly while we're already animating, which eliminates janky behavior.
  • Cross-Browser Scroll Position: Using window.pageYOffset || document.documentElement.scrollTop ensures we get the correct scroll position across all browsers.
  • Viewport Resize Handling: We update the viewport height when the window resizes, so the scroll targets stay accurate even if the user changes their window size.
  • Smooth Animation: requestAnimationFrame ensures our scroll animation syncs with the browser's refresh rate, making it smoother than using setInterval.

Quick Tweaks

  • Adjust the scrollStep value in the smoothScroll method to make the animation faster or slower (smaller numbers = slower scroll).
  • If you're using functional components instead of class components, you can convert this to use useEffect for event listeners and useRef for tracking isAnimating, lastScrollPos, and viewportHeight.

内容的提问来源于stack exchange,提问作者Ryan Sam

火山引擎 最新活动