ドックメニュー

Mac OSのドックメニュー風メニューを実装する方法をご紹介します。

デモ

サンプルコード

HTML

<div id="dock-menu">
	<ul>
		<li><a href=""></a></li>
		<li><a href=""></a></li>
		<li><a href=""></a></li>
		<li><a href=""></a></li>
		<li><a href=""></a></li>
		<li><a href=""></a></li>
	</ul>
</div>

CSS


#dock-menu {
	display:inline-block;
	position:relative;
	z-index:1;
	min-width:460px;
	height:140px; /* 最大の高さ */
	letter-spacing:-1em;
}

/* メニュー下の背景 */
#dock-menu::before {
	position:absolute;
	left:0;
	bottom:0;
	z-index:-1;
	width:100%;
	height:40px;
	background-color:#ddd;
	content:"";
}

/* 下揃えにするための基準要素 */
#dock-menu::after {
	display:inline-block;
	height:100%;
	content:"";
	vertical-align:top;
}

#dock-menu ul {
	display:inline-block;
	margin:0;
	padding:10px;
	vertical-align:bottom;
	letter-spacing:-1em;
}

	#dock-menu ul li {
		display:inline-block;
		margin:0;
		padding:0;
		letter-spacing:0;
		vertical-align:bottom;
	}

	#dock-menu ul li + li {
		margin-left:10px;
	}

		#dock-menu ul li a {
			display:block;
			width:80px;
			height:80px;
			background-color:#999;
		}

JavaScript

(function() {
	var maxSizeRatio = 1.5, // 最大の拡大率
	    rangeRatio   = 6;   // リサイズを与える範囲の率(アイテムの幅を基準)

	window.addEventListener('DOMContentLoaded', function() {
		var dockMenuElem = document.getElementById('dock-menu');

		if (!dockMenuElem) return;

		dockMenuElem = dockMenuElem.children[0];

		Array.prototype.forEach.call(dockMenuElem.childNodes, function(itemElem) {
			if (itemElem.nodeType > 1) return true;
			itemElem.setAttribute('data-width', itemElem.offsetWidth);
		});

		window.addEventListener('mousemove', function(event) {
			var mouseX = event.clientX;

			Array.prototype.forEach.call(dockMenuElem.childNodes, function(itemElem) {
				if (itemElem.nodeType > 1) return true;

				var bounds    = itemElem.getBoundingClientRect(),
				    itemWidth = Number(itemElem.getAttribute('data-width')),
				    maxSize   = itemWidth * maxSizeRatio,
				    center    = bounds.left + (maxSize / 2),
				    range     = maxSize * rangeRatio,
				    rangeHalf = range / 2,
				    start     = center - rangeHalf,
				    end       = center + rangeHalf;

				var first, second, size = '';

				if (mouseX > start && mouseX < end) {
					first  = Math.abs(start - mouseX) / rangeHalf;
					second = 1 - (first - 1);

					if (first > 0 && first < 1) {
						size = itemWidth + ((maxSize - itemWidth) * first);
					} else if (second > 0 && second < 1) {
						size = itemWidth + ((maxSize - itemWidth) * second);
					} else if (first === 1) {
						size = maxSize;
					}
				}

				if (size) size += 'px';

				itemElem.children[0].style.width  = size;
				itemElem.children[0].style.height = size;
			});
		});

		dockMenuElem.addEventListener('mouseleave', function(event) {
			Array.prototype.forEach.call(this.childNodes, function(itemElem) {
				if (itemElem.nodeType > 1) return true;

				itemElem.children[0].style.width  = '';
				itemElem.children[0].style.height = '';
			});
		});
	});
})();

3行目の"rangeRatio"でアイテムをリサイズする範囲をアイテムの何個分かで指定します。
35行目でアイテムの中央から左側、36行目でアイテムの中央から右側のリサイズ範囲を基準にマウスの位置の割合を0~1でそれぞれ取得します。
38~44行目で35~36行目で得た割合によって大きさを得ます。

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