skip to Main Content

I need help with this javascript function that I can’t get to work. I have to mention I am not a developer nor trying to be one, I am just trying to automate a process and decided to have ChatGPT help. I have very basic knowledge of HTML and CSS, a tiny bit of Javascript, so I figured I might be able to get there with AI but I got stuck.

Basically, I have a container with 5 elements in it. I am trying to make a rolling effect where as soon as an element starts leaving the container through the left side, it should slide back into view through the right side, creating this smooth continuous animation.

Right now the animation starts properly, but the elements are not coming back into view as I need them to. I mean they do but they do it very poorly, out of order, not respecting their distances between each other and they don’t really slide in smoothly, they just pop up randomly along the width of the container. Any idea how I should approach it?

const container = document.querySelector('.container');
const elements = document.querySelectorAll('.element');
const rollButton = document.getElementById('rollButton');
let animationInterval = null;

// Set initial positions of elements
elements.forEach(element => {
  element.style.transform = `translateX(0%)`;
  element.direction = -1; // -1 for moving left initially
});

// Function to start or stop the animation when the button is clicked
rollButton.addEventListener('click', () => {
  if (animationInterval === null) {
    // Start animation
    console.log('animation started');
    container.classList.add('rolling');
    const animationDuration = 10000; // Adjust as needed
    const frameRate = 60; // Frames per second
    const distancePerFrame = (container.offsetWidth / animationDuration) * (1000 / frameRate);
    animationInterval = setInterval(() => rollAnimation(distancePerFrame), 1000 / frameRate);
  }
});

// Function to move elements from right to left
function rollAnimation(distancePerFrame) {
  let allElementsLeftContainer = true; // Flag to check if all elements have left the container

  elements.forEach(element => {
    const rect = element.getBoundingClientRect();
    const x = rect.left;
    const y = rect.top;

    // Print the coordinates of each element within the container
    console.log(`Element ${element.id}: x = ${x}, y = ${y}`);

    // Move element based on its direction
    const transformValue = `translateX(${parseFloat(element.style.transform.replace('translateX(', '').replace(')', '')) + distancePerFrame * element.direction}%)`;
    element.style.transform = transformValue;

    if (element.direction === -1 && rect.right < 0) {
      // If element exits through the left side, re-enter from the right side
      const offset = (container.offsetWidth + rect.right) / container.offsetWidth * 100;
      element.style.transform = `translateX(${100 + offset}%)`;
    } else if (element.direction === 1 && rect.left > container.offsetWidth) {
      // If element exits through the right side, re-enter from the left side
      const offset = (rect.left - container.offsetWidth) / container.offsetWidth * 100;
      element.style.transform = `translateX(-${offset}%)`;
    }

    if (rect.left + rect.width > 0) {
      // If any element is still inside the container, set flag to false
      allElementsLeftContainer = false;
    }
  });

  // Check if all elements have left the container
  if (allElementsLeftContainer) {
    // Stop animation
    clearInterval(animationInterval);
    animationInterval = null; // Reset animation interval
    console.log('Animation stopped');
  }
}
.container {
  display: flex;
  justify-content: center;
  /* Center the elements horizontally */
  align-items: center;
  /* Center the elements vertically */
  position: relative;
  width: 100%;
  overflow: hidden;
  /* Hide elements outside the container */
  padding: 0;
  /* Remove padding */
  margin: 0;
  /* Remove margin */
}

.line {
  position: fixed;
  /* Fixed position */
  top: 27%;
  /* Position at the top of the viewport */
  left: 50%;
  /* Position in the middle horizontally */
  width: 2px;
  /* Thickness of the line */
  height: 400px;
  /* Height spanning the entire viewport */
  background-color: black;
  z-index: 1;
  /* Ensure it's above other content */
  transform: translateX(-50%);
  /* Center the line horizontally */
}

.element {
  width: 200px;
  height: 300px;
  margin-right: 40px;
  /* Small space between elements */
  background-color: #E2E8C0;
  /* Background color */
  animation: rollAnimation 10s linear infinite;
}

.element:last-child {
  margin-right: 0;
  /* No margin for the last element */
}

.element img {
  display: block;
  /* Ensure images don't have extra space below */
}

.container.rolling .element {
  animation-play-state: running;
}

.container:not(.rolling) .element {
  animation-play-state: paused;
  transform: translateX(0%);
  /* Reset the position when animation is paused */
}

.element.new {
  animation: rollInFromRight 10s linear;
  /* Adjust duration and easing as needed */
}


/* BUTTON */

#rollButton {
  position: fixed;
  bottom: 20px;
  /* Adjust as needed */
  left: 50%;
  transform: translateX(-50%);
  padding: 10px 20px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

#rollButton:hover {
  background-color: #45a049;
}
<div class="line"></div>

<div class="container">
  <div class="element" id="image1">
    <img src="placeholder1.jpg" alt="Image 1">
  </div>
  <div class="element" id="image2">
    <img src="placeholder2.jpg" alt="Image 2">
  </div>
  <div class="element" id="image3">
    <img src="placeholder3.jpg" alt="Image 3">
  </div>
  <div class="element" id="image4">
    <img src="placeholder4.jpg" alt="Image 4">
  </div>
  <div class="element" id="image5">
    <img src="placeholder5.jpg" alt="Image 5">
  </div>
</div>


<button id="rollButton">Roll</button>

2

Answers


  1. I have a container with 5 elements in it. I am trying to make a
    rolling effect where as soon as an element starts leaving the container
    through the left side, it should slide back into view through the right
    side, creating this smooth continuous animation.

    There are many different ways to achieve this effect.

    One approach (shown below) is to set up an initial display where the series of five elements is doubled – and then have the series extend as the scroll proceeds, such that there are always new elements available to scroll in from the right-hand side as the scroll continues leftwards.


    Working Example:

    const container = document.querySelector('.container');
    const containerBorderLeft = container.getBoundingClientRect().left;
    const innerContainerReset = container.innerHTML;
    const toggleAnimationButton = document.querySelector('.toggleAnimationButton');
    
    let scrollLeftInterval;
    let innerContainer = document.querySelector('.innerContainer');
    let circles = document.querySelectorAll('.circle');
    let i = 0;
    
    const toggleAnimation = () => {
    
      innerContainer.classList.toggle('scrollLeft');
      
      if (!innerContainer.classList.contains('scrollLeft')) {
        clearInterval(scrollLeftInterval);
        container.innerHTML = innerContainerReset;
        innerContainer = document.querySelector('.innerContainer');
        circles = document.querySelectorAll('.circle');
        i = 0;
      }  
     
      else {
        function manageScroll() {
          if (circles[i]?.getBoundingClientRect().right < containerBorderLeft) {
            const newCircle = circles[i].cloneNode();
            innerContainer.appendChild(newCircle);
            circles = document.querySelectorAll('.circle');
            i++;
            innerContainer.style.setProperty('transform', `translateX(calc((10 + ${i}) * -140px))`);
          }
          requestAnimationFrame(manageScroll);
        }
        requestAnimationFrame(manageScroll);
      }
    }
    
    toggleAnimationButton.addEventListener('click', toggleAnimation);
    body {
     margin: 0 12px 24px;
    }
    
    .container {
      display: block;
      width: 720px;
      height: 140px;
      margin: 0 auto 12px;
      background-color: rgb(0, 0, 63);
      overflow: hidden;
    }
    
    .innerContainer {
      width: max-content;
      height: 140px;
      transform: translateX(0);
      transition: transform 4s linear;
    }
    .circle {
      display: inline-block;
      width: 100px;
      height: 100px;
      margin: 20px 22px;
      border-radius: 50%;
    }
    
    .circle.red {
      background-color: rgb(255, 0, 0);
    }
    
    .circle.orange {
      background-color: rgb(255, 127, 0);
    }
    
    .circle.yellow {
      background-color: rgb(255, 255, 0);
    }
    
    .circle.green {
      background-color: rgb(0, 191, 0);
    }
    
    .circle.blue {
      background-color: rgb(0, 127, 255);
    }
    
    .innerContainer.scrollLeft {
      transform: translateX(calc(10 * -140px));
    }
    
    .toggleAnimationButton {
      display: block;
      width: 160px;
      height: 24px;
      margin: 0 auto;
      cursor: pointer;
    }
    <div class="container">
      <div class="innerContainer">
        <div class="circle red"></div>
        <div class="circle orange"></div>
        <div class="circle yellow"></div>
        <div class="circle green"></div>
        <div class="circle blue"></div>
        <div class="circle red"></div>
        <div class="circle orange"></div>
        <div class="circle yellow"></div>
        <div class="circle green"></div>
        <div class="circle blue"></div>
      </div>  
    </div>
    
    
    <button class="toggleAnimationButton">Toggle Animation</button>
    Login or Signup to reply.
  2. My suggestion is to create a single "sprite" image (instead of five images), then you can simply use them as a background image. In order to get that slot machine effect animation you can either use CSS keyframes of the JS’s Animations API on the background-position CSS property.

    const rand = (min, max) => Math.random() * (max - min) + min;
    const elSlot = document.querySelector("#slot");
    const elBar = document.querySelector("#bar");
    
    let turn = 0;
    
    elBar.addEventListener("click", () => {
      turn += Math.round(rand(10, 25));
      elSlot.animate(
        [{ backgroundPosition: `${turn * -100 + 50}px 0px` }],
        {fill: "forwards", duration: rand(2000, 3000), easing: "ease-in-out" }
      );
    });
    #slot {
      width: 200px;
      position: relative;
      background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 100">
        <text x="-.1em" y=".9em" font-size="90">🍇🍊🍒🍋🍉</text>
        </svg>') -50px / 500px 100px repeat;
      height: 100px;
      
      &::before {
        content: "";
        position: absolute;
        width: 2px;
        height: 100%;
        left: calc(50% - 1px);
        background: #000;
      }
    }
    <div id="slot"></div>
    <button id="bar">Roll</button>

    …if that’s something you were aiming for.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search