先日の多階層メニュー作成の記事を投稿しました。
その応用でメニューそれぞれを立体的な面(四角柱の一面)ととらえ、かつ四角柱をY軸を軸として回転させるアニメーションを作成しました(CSSで回転を実現しています)。
必ずしもヘッダーメニューが上部横一列に並んでいる必要がないとき、若しくはサイドに配置できるため省スペースとなります。
2つのパターン(ノーマルと少し歪ませたもの)がありますので、ご参考になれば幸いです。
使用するファイルは事前準備等は、多階層メニュー作成の記事と同様ですので、割愛させていただきます。
ノーマルパターン
CODEPENで実装と確認
「Run Pen」をクリックしてください。
See the Pen dropdown&accordion_3 by blue moon (@blue-moon) on CodePen.
HTMLファイル
<body>
<header>
<nav>
<!-- 第1階層のグローバルメニュー -->
<ul class="main-menu-list">
<li class="main-menu">
<p>menu1<i class="fas fa-angle-down"></i></p>
<!-- 第2階層メニュー -->
<ul class="category">
<li>
<a href="#">category1</a>
<!-- 第3階層メニュー開閉のためのクリック用アイコン -->
<span><i class="fas fa-minus"></i></span>
<!-- 第3階層メニュー -->
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
<li>
<a href="#">category2</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
</ul>
</li>
<li class="main-menu">
<p>menu2<i class="fas fa-angle-down"></i></p>
<ul class="category">
<li>
<a href="#">category1</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
<li>
<a href="#">category2</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
</ul>
</li>
<li class="main-menu">
<p>menu3<i class="fas fa-angle-down"></i></p>
<ul class="category">
<li>
<a href="#">category1</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
<li>
<a href="#">category2</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
</ul>
</li>
<li class="main-menu">
<p>menu4<i class="fas fa-angle-down"></i></p>
<ul class="category">
<li>
<a href="#">category1</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
<li>
<a href="#">category2</a>
<span><i class="fas fa-minus"></i></span>
<ul class="item-list">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
<li><a href="#">item3</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</header>
</body>
CSSファイル
/* reset&base */
body {
font-size: 12px;
color: #666;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
line-height: 1.8;
opacity: 0.8;
}
p {
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
nav {
margin: 30px auto;
width: 100px;
perspective: 1000; /* 要素に遠近感を与える */
-webkit-perspective: 1000;
}
/* 第1階層 */
.main-menu-list {
transform-style: preserve-3d; /* 子要素を 3D 空間に配置 */
transform: rotateX(-10deg) rotateY(-20deg);
animation: rotate 20s linear infinite;
}
@keyframes rotate {
from {
transform: rotateX(-10deg) rotateY(20deg);
}
to {
transform: rotateX(-10deg) rotateY(-340deg);
}
}
.main-menu-list:hover{
animation-play-state: paused; /* 回転一時停止 */
}
.main-menu {
position: absolute; /* 横並び */
margin: 0 1px;
width: 100px;
height: min-content;
text-align: center;
cursor: pointer;
box-sizing: border-box;
border: 1px solid #fff;
}
.main-menu:first-child {
transform: translateZ(50px);
background-color: #48feff;
}
.main-menu:nth-child(2) {
transform: rotateY(90deg) translateZ(50px);
background-color: #fa6099;
}
.main-menu:nth-child(3) {
transform: rotateY(180deg) translateZ(50px);
background-color: #fced5c;
}
.main-menu:last-child {
transform: rotateY(-90deg) translateZ(50px);
background-color: #94ff5f;
}
.fa-angle-down {
padding-left: 5px;
cursor: pointer;
}
/* 第2階層 */
.category {
padding: 0;
}
.category > li {
font-size: 0.8em;
}
.main-menu:first-child .category li {
background-color: #87fdff;
}
.main-menu:nth-child(2) .category li {
background-color: #fe7dad;
}
.main-menu:nth-child(3) .category li {
background-color: #fff190;
}
.main-menu:last-child .category li {
background-color: #b2ff8d;
}
.category li span {
position: relative;
cursor: pointer;
}
.fa-minus {
padding-left: 0;
}
.category li span::after {
position: absolute;
top: calc(0.25em * 1.1);
left: calc(50% - 0.1em);
width: 0.2em;
height: 0.875em;
content: "";
background-color: #666;
transition: 0.3s ease-in-out;
border-radius: 0.1em;
}
.close::after {
transform: rotate(90deg);
}
/* 第3階層 */
.main-menu:first-child .category li .item-list li {
background-color: #b7fdff;
}
.main-menu:nth-child(2) .category li .item-list li {
background-color: #fcabc9;
}
.main-menu:nth-child(3) .category li .item-list li {
background-color: #fef2b5;
}
.main-menu:last-child .category li .item-list li {
background-color: #c9fcb2;
}
補足説明
マウスオーバーした時に、アニメーションをストップするため、jQueryのcssメソッドで .css(‘animation’, ”)したり、CSSで :hover {animation: unset} 等すると、マウスが離れた時にまた最初から回転アニメーションが始まってしまいます。
それを解決してくれるのがアニメーションを一時停止する、:hover{animation-play-state: paused; }で、停止したところから再開してくれます。
.main-menu {position: absolute;} がないと、それぞれのメニューが同じ行(回転台?)に配置できません。top やleftの位置は省略していますが、必要に応じて調整してください。
親要素に {display: flex;}としても、同じ行(回転台?)に配置されません。
JSファイル
$(window).on('load resize', function() {
const cateList = $('header .category');
const itemList = $('header .item-list');
const moreIcon = $('.category li span');
cateList.hide();
itemList.hide();
//第1階層メニューをマウスオーバーで第2階層メニューをスライドダウン
$('nav .main-menu').hover(function() {
$(this).children(cateList).not(':animated').slideDown(300);
},function() {
moreIcon.removeClass('close');
$(itemList).slideUp(300);
$(cateList).slideUp(300);
});
//第2階層メニューのアイコンクリックで第3階層メニューをアコーディオン開閉
$(moreIcon).click(function() {
$(this).next(itemList).not(':animated').slideToggle(300);
if ($(this).hasClass('close')) {
moreIcon.removeClass('close');
}else{
$(this).addClass('close');
}
moreIcon.not($(this)).removeClass('close');
moreIcon.not($(this)).next(itemList).slideUp(300);
});
});
少しだけ傾いているパターン
HTMLとJSは前述のものを流用しています。
CODEPEN で実装と確認
See the Pen dropdown&accordion_4 by blue moon (@blue-moon) on CodePen.
CSSファイル
/* reset&base */
body {
font-size: 12px;
color: #666;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
line-height: 1.8;
opacity: 0.8;
}
p {
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
nav {
margin: 30px auto;
width: 100px;
perspective: 1000; /* 要素に遠近感を与える */
-webkit-perspective: 1000;
}
/* 第1階層 */
.main-menu-list {
transform-style: preserve-3d; /* 子要素を 3D 空間に配置 */
transform: rotateX(-10deg) rotateY(-20deg);
animation: rotate 20s linear infinite;
}
@keyframes rotate {
from {
transform: rotateX(-10deg) rotateY(20deg);
}
to {
transform: rotateX(-10deg) rotateY(-340deg);
}
}
.main-menu-list:hover{
animation-play-state: paused; /* 回転一時停止 */
}
.main-menu {
position: absolute; /* 横並び */
margin: 0 1px;
width: 100px;
height: min-content;
text-align: center;
cursor: pointer;
box-sizing: border-box;
border: 1px solid #fff;
}
.main-menu:first-child {
background-color: #48feff;
transform: translateZ(50px) skewY(10deg);
}
.main-menu:nth-child(2) {
background-color: #fa6099;
transform: rotateY(90deg) translateZ(50px) translateY(17px) skewY(10deg);
}
.main-menu:nth-child(3) {
background-color: #fced5c;
transform: rotateY(180deg) translateZ(50px) translateY(17px) skewY(-10deg);
}
.main-menu:last-child {
background-color: #94ff5f;
transform: rotateY(-90deg) translateZ(50px) skewY(-10deg);
}
.fa-angle-down {
padding-left: 5px;
cursor: pointer;
}
/* 第2階層 */
.category {
padding: 0;
}
.category > li {
font-size: 0.8em;
}
.main-menu:first-child .category li {
background-color: #87fdff;
}
.main-menu:nth-child(2) .category li {
background-color: #fe7dad;
}
.main-menu:nth-child(3) .category li {
background-color: #fff190;
}
.main-menu:last-child .category li {
background-color: #b2ff8d;
}
.category li span {
position: relative;
cursor: pointer;
}
.fa-minus {
padding-left: 0;
}
.category li span::after {
position: absolute;
top: calc(0.25em * 1.1);
left: calc(50% - 0.1em);
width: 0.2em;
height: 0.875em;
content: "";
background-color: #666;
transition: 0.3s ease-in-out;
border-radius: 0.1em;
}
.close::after {
transform: rotate(90deg);
}
/* 第3階層 */
.main-menu:first-child .category li .item-list li {
background-color: #b7fdff;
}
.main-menu:nth-child(2) .category li .item-list li {
background-color: #fcabc9;
}
.main-menu:nth-child(3) .category li .item-list li {
background-color: #fef2b5;
}
.main-menu:last-child .category li .item-list li {
background-color: #c9fcb2;
}
補足説明
傾きを持たせるtransformプロパティの値にskew(●●deg)を設定しており、これは子要素(文字)にも引き継がれます。
もし、文字は傾かせたくないということであれば、子要素には親要素とは同じ整数を反対の正負として、skewに設定してください。
例えば、親要素が{transform: skewY(10deg);}となっている場合、子要素の文字に{transform: skewY(-10deg);}とすれば相殺され文字はブラウザに対しては平行となります。但し、ボックス内ではボックスの辺との平行は維持されません。
また、transformプロパティは、aタグや、spanタグのインライン要素には効きませんので、ブロック要素もしくは、インラインブロック要素へ変換するのをお忘れなく。
変形可能な要素のみが
MDN CSSカスケーディングスタイルシートより引用transform
の対象になります。つまり、レイアウトが CSS ボックスモデルによって管理される、非置換インラインボックス、表の列ボックス、表の列グループボックスを除くすべての要素です。
実際、menu1,menu2とmenu3,menu4は、文字の傾き方向が違いますが、傾きの調整は省略しております。
エッシャーの『上昇と下降』の中で描かれているペンローズの階段ではありませんが、前者グループと後者グループで傾きを逆方向にしなければ、最初のmenu1とmenu4がくっつきません。