React轮播组件:如何暂停setInterval及添加切换动画?
Hey there! Let's work through your two carousel issues step by step, with code tweaks tailored to your existing setup.
1. Pause Auto-Carousel When Modal is Open, Resume on Close
The core issue here is that your carousel's interval doesn't know when the modal is open. We need to pass the modal's state up to the parent ImageCarousel component and use that to control the interval.
Step 1: Update the Image component to emit modal state changes
Add a prop to let the parent know when the modal toggles, and call it whenever the modal state changes:
import React, { useState, Fragment } from "react"; import Overlay from "../Overlay"; import "./Image.scss"; const Image = ({ imageSrc, onModalToggle }) => { const [isModalOpen, setIsModalOpen] = useState(false); const handleShowDialog = () => { const newState = !isModalOpen; setIsModalOpen(newState); // Pass the new modal state up to the parent onModalToggle(newState); }; return ( <Fragment> <img className="original-image" src={imageSrc} onClick={handleShowDialog} alt="carousel" /> {isModalOpen && ( <Overlay closeOverlay={handleShowDialog}> <img className="modal-image" src={imageSrc} alt="carousel" /> </Overlay> )} </Fragment> ); }; export default Image;
Step 2: Modify ImageCarousel to control the interval with modal state
We'll use a ref to track the interval ID, a state to track if we're paused, and update our useEffect logic to handle pausing/resuming:
import { useState, useEffect, useRef } from "react"; import "./Carousel.scss"; import Image from "../Image"; import CarouselControls from "./CarouselControls"; const ImageCarousel = ({ imageUrls }) => { const [currentPosition, setCurrentPosition] = useState(0); const [activeArrow, setActiveArrow] = useState(""); const [isPaused, setIsPaused] = useState(false); const timerRef = useRef(null); // Track interval ID // Function to start the auto-slide interval const startAutoSlide = () => { timerRef.current = setInterval(changeSlide, 2000); }; // Function to clear the interval const stopAutoSlide = () => { if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; } }; // Handle interval setup on initial load and when imageUrls changes useEffect(() => { setCurrentPosition(0); startAutoSlide(); // Cleanup on unmount or imageUrls change return () => stopAutoSlide(); }, [imageUrls]); // Pause/resume when isPaused changes useEffect(() => { if (isPaused) { stopAutoSlide(); } else { // Only restart if we weren't already running (avoid duplicate intervals) if (!timerRef.current) { startAutoSlide(); } } }, [isPaused]); const changeSlide = () => { const lastIndex = imageUrls.length - 1; setCurrentPosition(currentPosition => { return currentPosition === lastIndex ? 0 : currentPosition + 1; }); }; const goToPreviousSlide = () => { setActiveArrow("left"); const newPosition = currentPosition === 0 ? imageUrls.length - 1 : currentPosition - 1; setCurrentPosition(newPosition); }; const goToNextSlide = () => { setActiveArrow("right"); const newPosition = currentPosition === imageUrls.length - 1 ? 0 : currentPosition + 1; setCurrentPosition(newPosition); }; const goToParticularSlide = position => { setCurrentPosition(position); }; return ( <div className="image-carousel"> <header className="carousel-header"> Image Carousel Component</header> {imageUrls && imageUrls.length > 0 && ( <div className="carousel-container"> <Image imageSrc={imageUrls[currentPosition]} onModalToggle={(isOpen) => setIsPaused(isOpen)} // Pass modal state to update pause state /> <CarouselControls imageUrls={imageUrls} activeArrow={activeArrow} currentPosition={currentPosition} goToNextSlide={goToNextSlide} goToParticularSlide={goToParticularSlide} goToPreviousSlide={goToPreviousSlide} /> </div> )} </div> ); }; export default ImageCarousel;
Now when the modal opens, isPaused becomes true and the interval stops; when closed, isPaused flips back to false and the interval resumes.
2. Add Slide In/Out Animations
To add smooth slide transitions, we'll render all slides in the carousel container, use CSS to position them absolutely, and apply transform transitions based on their position relative to the current slide.
Step 1: Update ImageCarousel to render all slides
Modify the carousel container section to render every image, with classes indicating their position:
// Inside ImageCarousel's return statement, replace the single Image component with: <div className="carousel-container"> {imageUrls.map((url, index) => { let slideClass = "carousel-slide"; if (index === currentPosition) { slideClass += " active"; } else if (index === (currentPosition - 1 + imageUrls.length) % imageUrls.length) { slideClass += " prev"; } else if (index === (currentPosition + 1) % imageUrls.length) { slideClass += " next"; } else { slideClass += " hidden"; } return ( <div key={index} className={slideClass}> <Image imageSrc={url} onModalToggle={(isOpen) => setIsPaused(isOpen)} /> </div> ); })} <CarouselControls imageUrls={imageUrls} activeArrow={activeArrow} currentPosition={currentPosition} goToNextSlide={goToNextSlide} goToParticularSlide={goToParticularSlide} goToPreviousSlide={goToPreviousSlide} /> </div>
Step 2: Add animation styles to Carousel.scss
Add these styles to handle positioning and transitions:
.carousel-container { position: relative; overflow: hidden; width: 100%; height: 400px; // Adjust based on your needs } .carousel-slide { position: absolute; top: 0; left: 0; width: 100%; height: 100%; transition: transform 0.5s ease-in-out; } .carousel-slide.active { transform: translateX(0); } .carousel-slide.prev { transform: translateX(-100%); } .carousel-slide.next { transform: translateX(100%); } .carousel-slide.hidden { transform: translateX(100%); opacity: 0; pointer-events: none; } // Adjust your original-image class to fill the slide .original-image { width: 100%; height: 100%; object-fit: cover; cursor: pointer; }
This setup will slide the current slide in from the right when moving forward, and from the left when moving backward. The transition is smooth thanks to the transform property and CSS transition.
内容的提问来源于stack exchange,提问作者Ankit Kumar

