Как сделать бесконечную анимацию с GSAP
К сожалению не могу понять как сделать бесконечное прокручивание списка элементов с помощью библиотеки GSAP 3. Сам принцип понятен, что нужно каждой плитке делать смещение, но не понятно как его сделать с помощью библиотеки GSAP 3. Суть задания состоит в том, что нужно нажать на некую кнопку, чтоб запустить механизм бесконечного вращения всего списка элементов и в одну сторону, а через некоторое время плавно остановиться на рандомной плитке. Сейчас список элементов вращается в обе стороны и последняя плитка и с отображением пустоты в конце и вначале списка. Прикрепляю свой пример реализациина codepen.
import React, {
Suspense,
useEffect,
useRef,
useState
} from "https://cdn.skypack.dev/react";
import ReactDOM from "https://cdn.skypack.dev/react-dom";
const items = [...Array(20).keys()];
interface HandleClickParams {
nodeGamesSection: HTMLDivElement | null;
nodeGamesGrid: HTMLUListElement | null;
nodeGameGridItems: HTMLLIElement[];
}
function getRandomInt(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getPositionOfWinner({
nodeGamesSection,
nodeGameGridItems,
winner
}: Pick<HandleClickParams, "nodeGamesSection" | "nodeGameGridItems"> & {
winner: number;
}) {
const margin = 12;
const center = nodeGamesSection!?.offsetWidth / 2; // получаем центр контейнера
const widthOfImg = nodeGameGridItems[winner]!?.offsetWidth / 2; // получаем центр плитки
const left = nodeGameGridItems[winner]!?.offsetLeft - margin; // получаем позицию плитки по x
return left - center - widthOfImg;
}
const handleClick = ({
nodeGamesGrid,
nodeGamesSection,
nodeGameGridItems
}: HandleClickParams) => {
if (nodeGamesGrid === null || nodeGamesSection === null) return;
const winner = getRandomInt(0, nodeGameGridItems.length - 1);
TweenMax.to(nodeGameGridItems, 5, {
x:
getPositionOfWinner({
winner,
nodeGameGridItems,
nodeGamesSection
}) * -1,
ease: Power4.easeOut
});
};
function App() {
const refGamesSection = useRef<HTMLDivElement | null>(null);
const refGamesGrid = useRef<HTMLUListElement | null>(null);
const refGameGridItems = useRef<HTMLLIElement[]>([]);
useEffect(() => {
return () => {
refGameGridItems.current = [];
};
}, []);
return (
<div className="container my-5">
<div className="d-flex justify-content-center">
<button
type="button"
class="btn btn-secondary arrow"
onClick={() => {
handleClick({
nodeGamesSection: refGamesSection.current,
nodeGamesGrid: refGamesGrid.current,
nodeGameGridItems: refGameGridItems.current
});
}}
>
Start
</button>
</div>
<div className="section">
<div className="ul-container" ref={refGamesSection}>
<ul ref={refGamesGrid}>
{items.map(function (item) {
return (
<li
key={item}
ref={(element: HTMLLIElement) =>
refGameGridItems.current.push(element)
}
>
<img
src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22286%22%20height%3D%22180%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20286%20180%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_17810fa9800%20text%20%7B%20fill%3Argba(255%2C255%2C255%2C.75)%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_17810fa9800%22%3E%3Crect%20width%3D%22286%22%20height%3D%22180%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2299.1171875%22%20y%3D%2296.3%22%3EImage%20cap%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E"
alt="Card image cap"
/>
<p>Image item {item}</p>
</li>
);
})}
</ul>
</div>
</div>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<Suspense fallback={() => <h1>Loading...</h1>}>
<App />
</Suspense>
</React.StrictMode>,
document.getElementById("root")
);
