カルーセル(マウスカーソルの位置によって向きを変え自動移動)

マウスカーソルの位置によって向きを変え自動移動するカルーセルを実装する方法をご紹介します。

  • ※ IE9以上対応。
  • ※ ページ読み込み時はマウスの座標が取得できないので、方向を定めることはできません。

デモ

カルーセル要素の表示領域の半分を基準にマウスカーソルの前後で左方向、右方向にスライドします。

サンプルコード

HTML

<div id="carousel">
	<ul>
		<li>1</li>
		<li class="even">2</li>
		<li>3</li>
		<li class="even">4</li>
		<li>5</li>
		<li class="even">6</li>
		<li>7</li>
		<li class="even">8</li>
	</ul>
</div>

CSS


#carousel {
	overflow:hidden;
	width:500px; /* 表示領域 */
}

	#carousel ul {
		position:relative;
		overflow:hidden;
		margin:0;
		padding:0;
		list-style:none;
	}

		#carousel ul li {
			float:left;
			margin:0;
			padding:15px 0;
			width:100px; /* アイテムの幅 */
			background-color:#ccc;
			font-size:16px;
			text-align:center;
		}

		#carousel ul li.even {
			background-color:#eee;
		}

JavaScript

(function() {
	/**
	 * 1回に移動する量
	 */
	var movePos = 5;

	window.addEventListener('DOMContentLoaded', function() {
		var listElem = document.querySelector('#carousel > ul');

		if (!listElem) return;

		var itemElems     = listElem.childNodes,
		    listWidth     = listElem.parentNode.offsetWidth,
		    listHalfWidth = listWidth / 2,
		    cloneElems    = [],
		    sizes         = [],
		    direction     = '';

		// アイテム要素の複製と幅の取得
		itemElems.forEach(function(elem) {
			if (elem.nodeType > 1) return false;

			cloneElems.push(elem.cloneNode(true));

			var style = document.defaultView.getComputedStyle(elem, '');

			sizes.push(
				parseInt(style['margin-left']) +
				parseInt(style['margin-right']) +
				elem.offsetWidth
			);
		});

		var size       = sizes.reduce(function(a, x) { return a + x; }),
		    defPos     = size * -1,
		    currentPos = defPos,
		    maxPos     = defPos * 2;

		// 幅の設定
		listElem.style.width = (size * 3) + 'px';

		// 複製アイテムを前に出力
		cloneElems.reverse().forEach(function(elem) {
			var cloneElem = elem.cloneNode(true);
			cloneElem.classList.add('clone-before');
			listElem.insertBefore(cloneElem, listElem.firstChild);
		});

		// 複製アイテムを後に出力
		cloneElems.reverse().forEach(function(elem) {
			var cloneElem = elem.cloneNode(true);
			cloneElem.classList.add('clone-after');
			listElem.appendChild(cloneElem);
		});

		// マウスカーソルの座標によるスライドする方向を設定
		window.addEventListener('mousemove', function(event) {
			var mouseX   = event.clientX,
			    listPosX = listElem.parentNode.getBoundingClientRect().left;

			direction = listPosX + listHalfWidth < mouseX ? 'lr' : 'rl';
		});

		// スライド動作
		setInterval(function() {
			if (direction === 'lr') {
				currentPos -= movePos;
				if (currentPos <= maxPos) currentPos = defPos;
			} else {
				currentPos += movePos;
				if (currentPos >= 0) currentPos = defPos;
			}

			listElem.style.left = currentPos + 'px';
		}, 24);
	});
})();

カルーセルの中央にいくにしたがって移動する速度を遅くするには、次の64行目から67行目ようにマウスカーソルと中央の間の距離を中央にいくにしたがって0になるようにして、その値を移動量に乗算することで実装できます。

JavaScript

(function() {
	/**
	 * 1回に移動する量
	 */
	var movePos = 5;

	window.addEventListener('DOMContentLoaded', function() {
		var listElem = document.querySelector('#carousel > ul');

		if (!listElem) return;

		var itemElems      = listElem.childNodes,
		    listWidth      = listElem.parentNode.offsetWidth,
		    listHalfWidth  = listWidth / 2,
		    cloneElems     = [],
		    sizes          = [],
		    direction      = '',
		    currentMovePos = movePos;

		// アイテム要素の複製と幅の取得
		itemElems.forEach(function(elem) {
			if (elem.nodeType > 1) return false;

			cloneElems.push(elem.cloneNode(true));

			var style = document.defaultView.getComputedStyle(elem, '');

			sizes.push(
				parseInt(style['margin-left']) +
				parseInt(style['margin-right']) +
				elem.offsetWidth
			);
		});

		var size       = sizes.reduce(function(a, x) { return a + x; }),
		    defPos     = size * -1,
		    currentPos = defPos,
		    maxPos     = defPos * 2;

		// 幅の設定
		listElem.style.width = (size * 3) + 'px';

		// 複製アイテムを前に出力
		cloneElems.reverse().forEach(function(elem) {
			var cloneElem = elem.cloneNode(true);
			cloneElem.classList.add('clone-before');
			listElem.insertBefore(cloneElem, listElem.firstChild);
		});

		// 複製アイテムを後に出力
		cloneElems.reverse().forEach(function(elem) {
			var cloneElem = elem.cloneNode(true);
			cloneElem.classList.add('clone-after');
			listElem.appendChild(cloneElem);
		});

		// マウスカーソルの座標によるスクロール方向を設定
		window.addEventListener('mousemove', function(event) {
			var mouseX   = event.clientX,
			    listPosX = listElem.parentNode.getBoundingClientRect().left;

			direction = listPosX + listHalfWidth < mouseX ? 'lr' : 'rl';

			var ratio = Math.abs(1 - ((mouseX - listPosX) / listHalfWidth));
			if (ratio > 1) ratio = 1;

			currentMovePos = movePos * ratio;
		});

		// スクロール動作
		setInterval(function() {
			if (direction === 'lr') {
				currentPos -= currentMovePos;
				if (currentPos <= maxPos) currentPos = defPos;
			} else {
				currentPos += currentMovePos;
				if (currentPos >= 0) currentPos = defPos;
			}

			listElem.style.left = currentPos + 'px';
		}, 24);
	});
})();

JavaScript逆引きリファレンス一覧へ戻る