横並びに配置した画像をスライドショーのように無限に横スクロールさせる方法を紹介します。
JavaScriptでスクロールスピードとスクロール方向を設定しています。

1つのスライダーだけスクロール

DEMO

HTML

<div class="marqueer">
	<ul>
		<li><img src="https://blog.grinee.net/demo/images/image1.png" alt=""></li>
		<li><img src="https://blog.grinee.net/demo/images/image2.png" alt=""></li>
		<li><img src="https://blog.grinee.net/demo/images/image3.png" alt=""></li>
	</ul>
</div>

CSS

並べたい写真の枚数や余白はこちらで調整します。

.marqueer {
	margin: 10px 0;
	overflow: hidden;
}
.marqueer ul {
	display: flex;
	margin: 0;
	padding: 0;
	width: max-content;
}
.marqueer ul li {
	list-style: none;
	padding: 0 5px; /* 余白 */
	width: calc(100vw / 3 - 10px); /* 3 写真の枚数にあわせる */
}
.marqueer img {
	display: block;
	width: 100%;
}

JavaScript

スクロールスピードとスクロール方向を設定しています。

window.addEventListener('load', function () {
	const marquee = document.querySelector('.marqueer ul'); // ul要素
	const items = Array.from(marquee.children);             // 子要素
	const speedSeconds = 20;                                // スクロール1回にかかる秒数
	const direction = 'left';                               // 'left' または 'right'

	// 無限スクロール用に複製
	items.forEach(item => marquee.appendChild(item.cloneNode(true)));

	let marqueeWidth = marquee.scrollWidth / 2;
	let pos = direction === 'left' ? 0 : -marqueeWidth;
	const pixelsPerFrame = marqueeWidth / (speedSeconds * 60);

	function animate() {
		pos += direction === 'left' ? -pixelsPerFrame : pixelsPerFrame;

		// 無限ループ
		if (pos <= -marqueeWidth) pos += marqueeWidth;
		if (pos >= 0) pos -= marqueeWidth;

		marquee.style.transform = `translateX(${pos}px)`;
		requestAnimationFrame(animate);
	}

	animate();
});

複数のスライダーを設置する

左右交互にスライダーをスクロールさせています。

DEMO

HTML

<div class="marqueer2">
	<ul>
		<li><img src="https://blog.grinee.net/demo/images/image1.png" alt=""></li>
		<li><img src="https://blog.grinee.net/demo/images/image2.png" alt=""></li>
		<li><img src="https://blog.grinee.net/demo/images/image3.png" alt=""></li>
	</ul>
</div>
<div class="marqueer2">
	<ul>
		<li><img src="https://blog.grinee.net/demo/images/image1.png" alt=""></li>
		<li><img src="https://blog.grinee.net/demo/images/image2.png" alt=""></li>
		<li><img src="https://blog.grinee.net/demo/images/image3.png" alt=""></li>
	</ul>
</div>
<div class="marqueer2">
	<ul>
		<li><img src="https://blog.grinee.net/demo/images/image1.png" alt=""></li>
		<li><img src="https://blog.grinee.net/demo/images/image2.png" alt=""></li>
		<li><img src="https://blog.grinee.net/demo/images/image3.png" alt=""></li>
	</ul>
</div>

CSS

1つだけの時と同じです。

.marqueer2 {
	margin: 10px 0;
	overflow: hidden;
}
.marqueer2 ul {
	display: flex;
	margin: 0;
	padding: 0;
	width: max-content;
}
.marqueer2 ul li {
	list-style: none;
	padding: 0 5px; /* 余白 */
	width: calc(100vw / 3 - 10px); /* 3 写真の枚数にあわせる */
}
.marqueer2 img {
	display: block;
	width: 100%;
}

JavaScript

左右の流れる順番を変える場合はconst directionの’left’ : ‘right’; を入れ替えてください。

window.addEventListener('load', function () {
	const marquees = document.querySelectorAll('.marqueer2');

	marquees.forEach((marqueeWrapper, index) => {
		const marquee = marqueeWrapper.querySelector('ul');      // ul要素
		const items = Array.from(marquee.children);              // 子要素
		const speedSeconds = 20;                                 // スクロール1回にかかる秒数
		const direction = (index % 2 === 0) ? 'left' : 'right'; // 奇数番目は左、偶数は右

		// 無限スクロール用に複製
		items.forEach(item => marquee.appendChild(item.cloneNode(true)));

		const marqueeWidth = marquee.scrollWidth / 2;
		let pos = direction === 'left' ? 0 : -marqueeWidth;
		const pixelsPerFrame = marqueeWidth / (speedSeconds * 60);

		function animate() {
			pos += direction === 'left' ? -pixelsPerFrame : pixelsPerFrame;

			// 無限ループ
			if (pos <= -marqueeWidth) pos += marqueeWidth;
			if (pos >= 0) pos -= marqueeWidth;

			marquee.style.transform = `translateX(${pos}px)`;
			requestAnimationFrame(animate);
		}

		animate();
	});
});