Infinite scroller switches direction mid way through

Hey I'm trying to make a animation for a case opening component, for some reason it seems to switch direction mid way through, another thing is my approach right now might seem wrong as I need to make it stop at where ever the winning item is (in the centre) and I'm not sure how I'd approach that either
1 Reply
V_LN
V_LN2mo ago
Code to component:
import '../../css/InfiniteScroller.css';
import React, { useEffect, useState, useRef } from "react";

export default function InfiniteScroller({ items, winningItemId, dataDirection }) {
const [scrollerContent, setScrollerContent] = useState(items);
const [isAnimated, setIsAnimated] = useState(false);
const scrollerRef = useRef(null);
const animationRef = useRef(null);
const hasDuplicated = useRef(false);

useEffect(() => {
if (!hasDuplicated.current && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
setScrollerContent((prevContent) => {
let newContent = [...prevContent];
for (let i = 0; i < 3; i++) {
newContent = [...newContent, ...items];
}
return newContent;
});

setIsAnimated(true);
hasDuplicated.current = true;
}
}, [items]);

const updateAnimationSpeed = (speed) => {
if (animationRef.current) {
animationRef.current.style.setProperty('--_animation-duration', `${speed}s`);
}
};

const smoothSpeedTransition = (startSpeed, endSpeed, duration, easingFunction) => {
let startTime = null;

const step = (timestamp) => {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / duration, 1);

const easedProgress = easingFunction(progress);

const newSpeed = startSpeed + (endSpeed - startSpeed) * easedProgress;
updateAnimationSpeed(newSpeed);

if (progress < 1) {
requestAnimationFrame(step);
}
};

requestAnimationFrame(step);
};
import '../../css/InfiniteScroller.css';
import React, { useEffect, useState, useRef } from "react";

export default function InfiniteScroller({ items, winningItemId, dataDirection }) {
const [scrollerContent, setScrollerContent] = useState(items);
const [isAnimated, setIsAnimated] = useState(false);
const scrollerRef = useRef(null);
const animationRef = useRef(null);
const hasDuplicated = useRef(false);

useEffect(() => {
if (!hasDuplicated.current && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
setScrollerContent((prevContent) => {
let newContent = [...prevContent];
for (let i = 0; i < 3; i++) {
newContent = [...newContent, ...items];
}
return newContent;
});

setIsAnimated(true);
hasDuplicated.current = true;
}
}, [items]);

const updateAnimationSpeed = (speed) => {
if (animationRef.current) {
animationRef.current.style.setProperty('--_animation-duration', `${speed}s`);
}
};

const smoothSpeedTransition = (startSpeed, endSpeed, duration, easingFunction) => {
let startTime = null;

const step = (timestamp) => {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / duration, 1);

const easedProgress = easingFunction(progress);

const newSpeed = startSpeed + (endSpeed - startSpeed) * easedProgress;
updateAnimationSpeed(newSpeed);

if (progress < 1) {
requestAnimationFrame(step);
}
};

requestAnimationFrame(step);
};
const easeIn = (t) => t * t;
const easeOut = (t) => 1 - Math.pow(1 - t, 2);

useEffect(() => {
updateAnimationSpeed(10);

const accelerate = () => {
smoothSpeedTransition(10, 1, 3000, easeIn);
};

const decelerate = () => {
smoothSpeedTransition(1, 20, 5000, easeOut);
};

accelerate();
setTimeout(() => {
decelerate();
}, 3000);

return () => {};
}, []);

const scrollerAttributes = {
className: "scroller",
"data-animated": isAnimated ? "true" : undefined,
"data-direction": dataDirection,
};

return (
<div {...scrollerAttributes} ref={scrollerRef}>
<ul
className='tag-list scroller__inner'
ref={animationRef}
>
{scrollerContent.map((item, index) => (
<li key={index}>
<div className='flex flex-col items-center'>
<div className="w-[100px] h-[100px] overflow-clip">
<img src={item.winnable.image} alt={item.winnable.title} className="w-full h-auto" />
</div>

<h3 className='mt-2'>{item.winnable.title}</h3>
</div>
</li>
))}
</ul>
</div>
);
}
const easeIn = (t) => t * t;
const easeOut = (t) => 1 - Math.pow(1 - t, 2);

useEffect(() => {
updateAnimationSpeed(10);

const accelerate = () => {
smoothSpeedTransition(10, 1, 3000, easeIn);
};

const decelerate = () => {
smoothSpeedTransition(1, 20, 5000, easeOut);
};

accelerate();
setTimeout(() => {
decelerate();
}, 3000);

return () => {};
}, []);

const scrollerAttributes = {
className: "scroller",
"data-animated": isAnimated ? "true" : undefined,
"data-direction": dataDirection,
};

return (
<div {...scrollerAttributes} ref={scrollerRef}>
<ul
className='tag-list scroller__inner'
ref={animationRef}
>
{scrollerContent.map((item, index) => (
<li key={index}>
<div className='flex flex-col items-center'>
<div className="w-[100px] h-[100px] overflow-clip">
<img src={item.winnable.image} alt={item.winnable.title} className="w-full h-auto" />
</div>

<h3 className='mt-2'>{item.winnable.title}</h3>
</div>
</li>
))}
</ul>
</div>
);
}
.tag-list {
margin: 0;
padding-inline: 0;
list-style: none;
}

.tag-list li {
padding: 1rem;
background: var(--clr-primary-400);
border-radius: 0.5rem;
box-shadow: 0 0.5rem 1rem -0.25rem var(--clr-primary-900);
}

.scroller__inner {
padding-block: 1rem;
display: flex;
gap: 1rem;
flex-wrap: wrap;
}

.scroller {
max-width: 600px;
}

.scroller[data-animated="true"] {
overflow: hidden;

-webkit-mask: linear-gradient(
90deg,
transparent,
white 20%,
white 80%,
transparent
);

mask: linear-gradient(
90deg,
transparent,
white 20%,
white 80%,
transparent
);
}

.scroller[data-animated="true"] .scroller__inner {
width: max-content;
flex-wrap: nowrap;
animation:
scroll
var(--_animation-duration, 5s)
linear
infinite;
}

@keyframes scroll {
to {
transform: translateX(calc(-50% - 0.5rem));
}
}
.tag-list {
margin: 0;
padding-inline: 0;
list-style: none;
}

.tag-list li {
padding: 1rem;
background: var(--clr-primary-400);
border-radius: 0.5rem;
box-shadow: 0 0.5rem 1rem -0.25rem var(--clr-primary-900);
}

.scroller__inner {
padding-block: 1rem;
display: flex;
gap: 1rem;
flex-wrap: wrap;
}

.scroller {
max-width: 600px;
}

.scroller[data-animated="true"] {
overflow: hidden;

-webkit-mask: linear-gradient(
90deg,
transparent,
white 20%,
white 80%,
transparent
);

mask: linear-gradient(
90deg,
transparent,
white 20%,
white 80%,
transparent
);
}

.scroller[data-animated="true"] .scroller__inner {
width: max-content;
flex-wrap: nowrap;
animation:
scroll
var(--_animation-duration, 5s)
linear
infinite;
}

@keyframes scroll {
to {
transform: translateX(calc(-50% - 0.5rem));
}
}
Want results from more Discord servers?
Add your server