<div class="menu-01" id="js-menu-01">
<ul class="menu-01__items">
<li><a class="menu-01__item" href="#">MENU01-1<span class="menu-01__item-mask"></span></a></li>
<li><a class="menu-01__item" href="#">MENU01-2<span class="menu-01__item-mask"></span></a></li>
<li><a class="menu-01__item" href="#">MENU01-3<span class="menu-01__item-mask"></span></a></li>
</ul>
</div><a class="menu-01__button js-menu-01-button" href="">
<div class="menu-01__button-open"><span class="menu-01__button-open-dots"><span></span><span></span><span></span></span><span class="menu-01__button-open-dots"><span></span><span></span><span></span></span><span class="menu-01__button-open-dots"><span></span><span></span><span></span></span></div>
<div class="menu-01__button-close"></div>
</a>
//- メニュー
.menu-01#js-menu-01
ul.menu-01__items
each item in items
li
a.menu-01__item(href=item.link) #{ item.text }
span.menu-01__item-mask
//- ボタン
a.menu-01__button.js-menu-01-button(href='')
.menu-01__button-open
- for (var n = 0; n < 3; n++)
span.menu-01__button-open-dots
span
span
span
.menu-01__button-close
{
"items": [
{
"link": "#",
"text": "MENU01-1"
},
{
"link": "#",
"text": "MENU01-2"
},
{
"link": "#",
"text": "MENU01-3"
}
]
}
$BLOCK_NAME: '.menu-01';
// 変数
$color_white: #fff;
$color_black: #2b2b2b;
$color_bg: #d4d4d4;
$duration_bg: 0.7s;
$easing_bg: cubic-bezier(0.7, 0, 0.3, 1);
$duration_item: 0.5s;
$easing_item: cubic-bezier(1, 0, 0, 1);
$duration_button: 0.2s;
$easing_button: cubic-bezier(0.7, 0, 0.3, 1);
$duration_border: 0.5s;
$easing_border: cubic-bezier(0.075, 0.82, 0.165, 1);
#{ $BLOCK_NAME } {
// メニュー
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
align-items: center;
visibility: hidden;
&::before {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
content: '';
background: $color_bg;
transition: transform $duration_bg $easing_bg 0.6s;
transform: scaleX(0);
transform-origin: right;
}
&.is-open {
visibility: visible;
&::before {
transition-delay: 0s;
transform: scaleX(1);
}
}
&__items {
width: 320px;
margin: 0 auto 24px;
& > li {
margin-bottom: 24px;
overflow: hidden;
&:last-child {
margin-bottom: 0;
}
}
}
&__item {
position: relative;
display: block;
padding: 24px 0;
font-size: 19px;
line-height: 1;
transition: transform $duration_border $easing_border 0.6s;
transform: translateX(101%);
&::before,
&::after {
position: absolute;
right: 0;
bottom: 0;
left: 0;
display: block;
height: 1px;
content: '';
background: $color_black;
}
&::before {
opacity: 0.28;
}
&::after {
transition: transform $duration_border $easing_border;
transform: scaleX(0);
transform-origin: right;
}
&:hover::after {
transform: scaleX(1);
transform-origin: left;
}
@at-root #{ $BLOCK_NAME }.is-open & {
transition-delay: 0.4s;
transform: translateX(0);
}
}
&__item-mask {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
background: $color_black;
transition: transform $duration_border $easing_border;
@at-root #{ $BLOCK_NAME }.is-open & {
transition-delay: 1s;
transform: translateX(101%);
}
}
// ボタン
&__button {
position: fixed;
top: 12px;
right: 12px;
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
&::before {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
content: '';
background: $color_black;
border: 1px solid $color_black;
border-radius: 50%;
transition: $duration_button $easing_button;
transition-property: background-color, transform;
}
&:hover::before {
background: $color_white;
transform: scale(1.2);
}
&.is-open:hover::before {
background: $color_black;
}
}
&__button-open {
position: relative;
z-index: 1;
width: 15px;
height: 15px;
@at-root #{ $BLOCK_NAME }__button.is-open & {
display: none;
}
}
&__button-open-dots {
position: absolute;
right: 0;
left: 0;
display: block;
height: 3px;
margin: auto;
&:nth-child(1) {
top: 0;
}
&:nth-child(2) {
top: 0;
bottom: 0;
}
&:nth-child(3) {
bottom: 0;
}
& > span {
position: absolute;
top: 0;
display: block;
width: 3px;
height: 3px;
margin: auto;
background: $color_white;
transition: $duration_button $easing_button;
transition-property: background-color, width;
&:nth-child(1) {
left: 0;
}
&:nth-child(2) {
right: 0;
left: 0;
}
&:nth-child(3) {
right: 0;
}
@at-root #{ $BLOCK_NAME }__button:hover & {
background: $color_black;
&:nth-child(2) {
width: 100%;
}
}
}
}
&__button-close {
position: relative;
z-index: 1;
display: none;
width: 15px;
height: 15px;
@at-root #{ $BLOCK_NAME }__button.is-open & {
display: block;
}
&::before,
&::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: block;
height: 1px;
margin: auto;
content: '';
background: $color_white;
transition: transform $duration_button $easing_button;
@at-root #{ $BLOCK_NAME }__button:hover & {
transform: rotate(0);
}
}
&::before {
transform: rotate(45deg);
}
&::after {
transform: rotate(-45deg);
}
}
}
export function menu01() {
const menu = new Menu01();
menu.init();
}
class Menu01 {
menuEl: HTMLElement;
menuItemEls: HTMLCollection;
buttonEls: HTMLCollection;
isOpen: Boolean;
isBusy: Boolean;
animTime: number;
constructor() {
this.menuEl = document.getElementById('js-menu-01');
this.menuItemEls = this.menuEl ? this.menuEl.getElementsByClassName('menu-01__item') : null;
this.buttonEls = this.menuEl ? document.getElementsByClassName('js-menu-01-button') : null;
this.isOpen = false;
this.isBusy = false;
this.animTime = 1500;
}
/**
* 初期化
*/
init() {
if (!this.menuEl) return
this.onClickButton();
this.onClickMenuItem();
}
/**
* メニューを開閉する
*/
toggleMenu() {
// 開閉アニメーション中の場合、処理を終了
if (this.isBusy) return;
if (this.isOpen) {
// メニューが開いている場合
this.closeMenu();
} else {
// メニューが閉じている場合
this.openMenu();
}
}
/**
* メニューを開く
*/
openMenu() {
// 開閉アニメーション実行状態を「true」にする
this.isBusy = true;
// アニメーション開始
this.menuEl.classList.add('is-open');
[...this.buttonEls].forEach(el => {
el.classList.add('is-open');
});
this.isOpen = true;
// アニメーション終了後
setTimeout(() => {
// 開閉アニメーション実行状態を「false」にする
this.isBusy = false;
}, this.animTime);
}
/**
* メニューを閉じる
*/
closeMenu() {
// 開閉アニメーション実行状態を「true」にする
this.isBusy = true;
// アニメーション開始
this.menuEl.style.visibility = 'visible';
this.menuEl.classList.remove('is-open');
[...this.buttonEls].forEach(el => {
el.classList.remove('is-open');
});
// アニメーション終了後
setTimeout(() => {
this.menuEl.style.visibility = null;
this.isOpen = false;
// 開閉アニメーション実行状態を「false」にする
this.isBusy = false;
}, this.animTime);
}
/**
* メニュー開閉ボタンクリック時の挙動
*/
onClickButton() {
[...this.buttonEls].forEach(el => {
el.addEventListener('click', e => {
e.preventDefault();
this.toggleMenu();
});
});
}
/**
* メニューリンククリック時の挙動
*/
onClickMenuItem() {
[...this.menuItemEls].forEach(el => {
el.addEventListener('click', e => {
e.preventDefault();
this.closeMenu();
setTimeout(() => {
// メニューが閉じた後の挙動を記載
console.log('メニューリンククリック時の挙動');
}, this.animTime);
});
});
}
}