<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"
    }
  ]
}
  • Content:
    $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);
        }
      }
    }
    
  • URL: /components/raw/menu01/menu01.scss
  • Filesystem Path: src/components/menus/menu01/menu01.scss
  • Size: 4.8 KB
  • Content:
    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);
          });
        });
      }
    }
    
  • URL: /components/raw/menu01/menu01.ts
  • Filesystem Path: src/components/menus/menu01/menu01.ts
  • Size: 3 KB