skip to Main Content

I’m aiming to recreate an animation using JavaScript and CSS. It involves a list of words from a CMS, where the quantity of a single word can change. The goal is to create an endless vertical scrolling effect without a visible loop restart, similar to a marquee. I’m struggling with modifying my code to achieve the desired behavior. CSS animations are a bit challenging for me to grasp, so any assistance would be greatly appreciated. I’m open for a new markup structure if necessary.

My code: https://codesandbox.io/s/2v85nk?file=/src/TextSlider.js

The desired effect: https://drive.google.com/file/d/1bDVqeulKpwFyDKA9sKqxduR7OFgBZKK0/view?usp=sharing

I attempted to modify these two lines within .slider-item, making some progress but not quite reaching the desired outcome.

.slider-item {
  opacity: 0.5; // Added to show the previous item slightly while transitioning
  transform: translateY(-100%); // Altered scrolling direction
}

2

Answers


  1. The new element appearing in view seems to work fine. But to make is more like desired effect you need to show old element moving out of view smoothly.

    First thing if you need transition on opacity as well add it to .slider-item class. You can add transitions for multiple property :

    .slider-item{
      transition: transform 0.5s ease, opacity 0.5s ease;
    }
    

    .slider-item keeps the element below and out of visible area.

    .slider-item.active brings it into visible area.

    Now just add class .slider-item.prev-active class that will move the element above and visible area. This should be added to element whose index == currentIndex -1. There however is a catch with using this condition as is. After a full round – The (texts.length -1)th element won’t move out because currentIndex - 1 is -1 at this point.

    I modified this condition a bit to work well when the index becomes 0 after a full round:

    (index + 1) % texts.length === currentIndex
    

    The css class:

    .slider-item.prev-active {
      opacity: 0;
      transform: translateY(-100%);
    }
    

    The class can be added as :

    className={`slider-item ${index === currentIndex ? "active" : ""} 
               ${(index + 1) % texts.length === currentIndex ? "prev-active":""`}
    

    Full code :

    const root =  document.getElementById("root")
    const {useState, useEffect} = React;
    const TextSlider = ({ texts }) => {
      const [currentIndex, setCurrentIndex] = useState(0);
    
      useEffect(() => {
        const interval = setInterval(() => {
          setCurrentIndex((prevIndex) => (prevIndex + 1) % texts.length);
        }, 2700);
    
        return () => {
          clearInterval(interval);
        };
      }, [texts.length]);
    
      return (
        <div className="text-slider">
          {texts.map((text, index) => (
            <div
              key={index}
              className={'slider-item '+ (index === currentIndex ? "active" : "") +  ((index + 1) % texts.length === currentIndex ? "prev-active" : "")}>
              <h1 style={{ fontSize: "92px" }}>{text}</h1>
              <img
                style={{ aspectRatio: "1/1", width: "86px", borderRadius: "6px" }}
                src="https://upload.wikimedia.org/wikipedia/en/1/1e/Troye_Sivan_-_Bloom_%28Official_Album_Cover%29.png"
              />
            </div>
          ))}
        </div>
      );
    };
    
    const App = () => {
      const texts = ["Text 1", "Text 2", "Text 3", "Text 4", "Text 5"];
    
      return (
        <div>
          <h1>Text Slider</h1>
          <TextSlider texts={texts} />
        </div>
      );
    };
    
    
    
    ReactDOM.render(
        <App />,
        root
    );
    .text-slider {
      height: 100px; /* Adjust the height as needed */
      overflow: hidden;
      position: relative;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      text-align: center;
    }
    
    .slider-item {
      position: absolute;
      width: 100%;
      opacity: 0;
      transform: translateY(100%);
      transition: transform 0.5s ease, opacity 0.5s ease;
      background-color: transparent;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      gap: 1rem;
    }
    .slider-item.prev-active {
      opacity: 0;
      transform: translateY(-100%);
    }
    .slider-item.active {
      opacity: 1;
      transform: translateY(0%);
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    
    
    <body id="root"></body> 
    Login or Signup to reply.
  2. We can use scroll snapping:

    .wrapper {
      border: 1px solid black;
      height: 100px;
      width: 200px;
      overflow: hidden scroll;
      -ms-overflow-style: none;
      scrollbar-width: none;
      scroll-snap-type: y mandatory;
      scroll-padding: 0px;
    }
    
    .wrapper::-webkit-scrollbar {
      display: none;
    }
    
    .item {
      height: 100px;
      width: 200px;
      font-size: 2rem;
      font-weight: bolder;
      display: flex;
      scroll-margin: 0px;
      text-align: center;
      scroll-snap-align: center;
    }
    
    .item * {
      margin: auto;
    }
    
    .item:nth-child(2n) {
      background-color: skyblue;
    }
    
    .item:nth-child(2n + 1) {
      background-color: lime;
    }
    <div class="wrapper">
      <div class="item"><span>1</span></div>
      <div class="item"><span>2</span></div>
      <div class="item"><span>3</span></div>
      <div class="item"><span>4</span></div>
      <div class="item"><span>5</span></div>
      <div class="item"><span>6</span></div>
      <div class="item"><span>7</span></div>
      <div class="item"><span>8</span></div>
      <div class="item"><span>9</span></div>
      <div class="item"><span>0</span></div>
    </div>
    <p>Use mouse wheel. Or focus element then use up and down arrows</p>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search