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
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:
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.
…if that’s something you were aiming for.