You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React鼠标滚动控制视频分段播放功能故障求助

React滚动控制视频分段播放问题

需求

  • 向下滚动时播放数秒视频后停止
  • 向上滚动时播放反向制作的视频
  • 自定义可点击进度条

现存问题

  • 滚动时页面会滑至下一个组件,无法停留在视频组件直至所有视频片段播放完成
  • 从第三个组件返回视频组件后,反向滚动功能失效

代码实现

ScrollVideo.js

import React, { useRef, useEffect, useState } from 'react';
import styled from 'styled-components';
import ScrollVid from '../content/videos/ft.mp4';
import VidImage from '../content/images/vidcover.JPG';

const VideoWrapper = styled.div`
  position: relative;
  width: 100%;
  height: 100vh;
  overflow: hidden;

  video {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
`;

const ProgressBar = styled.div`
  position: absolute;
  bottom: 0;
  left: 0;
  height: 5px;
  background: #fff;
  transition: width 0.3s ease;
  width: ${(props) => (props.$progress / 7) * 100}%;
`;

const ScrollVideo = () => {
  const videoRef = useRef(null);
  const containerRef = useRef(null);
  const [scrollIndex, setScrollIndex] = useState(0);
  const [isPlaying, setIsPlaying] = useState(false);
  const [isInView, setIsInView] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        const entry = entries[0];
        setIsInView(entry.isIntersecting);
      },
      { threshold: 0.8 }
    );

    if (containerRef.current) {
      observer.observe(containerRef.current);
    }

    return () => {
      if (containerRef.current) {
        observer.unobserve(containerRef.current);
      }
    };
  }, []);

  useEffect(() => {
    if (isInView) {
      const handleWheel = (event) => {
        event.preventDefault();
        if (isPlaying) return;

        const { deltaY } = event;
        const newScrollIndex = Math.min(
          8,
          Math.max(0, scrollIndex + (deltaY > 0 ? 1 : -1))
        );

        if (newScrollIndex !== scrollIndex) {
          setScrollIndex(newScrollIndex);
        }
      };

      window.addEventListener('wheel', handleWheel, { passive: false });

      return () => {
        window.removeEventListener('wheel', handleWheel);
      };
    }
  }, [isInView, isPlaying, scrollIndex]);

  useEffect(() => {
    const video = videoRef.current;

    if (video && scrollIndex > 0 && scrollIndex <= 7) {
      setIsPlaying(true);
      video.currentTime = (scrollIndex - 1) * 3;
      video.play();
      const timeout = setTimeout(() => {
        video.pause();
        setIsPlaying(false);
      }, 3000);
      return () => clearTimeout(timeout);
    } else if (scrollIndex === 8) {
      const nextSection = containerRef.current.nextElementSibling;
      if (nextSection) {
        setScrollIndex(9); // 防止重复触发跳转
        nextSection.scrollIntoView({ behavior: 'smooth' });
      }
    } else if (scrollIndex === 0) {
      const prevSection = containerRef.current.previousElementSibling;
      if (prevSection) {
        prevSection.scrollIntoView({ behavior: 'smooth' });
      }
    }
  }, [scrollIndex]);

  return (
    <VideoWrapper ref={containerRef}>
      <video
        ref={videoRef}
        muted
        preload="metadata"
        poster={VidImage}
        playsInline
        className="w-full h-auto object-cover"
      >
        <source src={ScrollVid} type="video/mp4" />
      </video>
      <ProgressBar $progress={scrollIndex} />
    </VideoWrapper>
  );
};

export default ScrollVideo;

App.js

import React from 'react';
import Header from './components/Header';
import HeroSection from './components/HeroSection';
import SecondSection from './components/SecondSection';
import Specs from './components/SpecsTruck'
import Scroll from './components/ScrollVideo'
import Vid from './components/FullpageWithVideos'
import './style.css'

function App() {
  return (
    <div style={{ backgroundColor: 'black', color: 'white' }}>
      <Header />
      <HeroSection />
      <Vid/>
      <Scroll/>
      <SecondSection />
      <Specs/>
    </div>
  );
}
export default App;

Style.css

.video-section {
  position: relative;
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

.video-section video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.progress-bar {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 5px;
  background: #fff;
  transition: width 0.3s;
}

内容的提问来源于stack exchange,提问作者Malik Usman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 09:34:59