[CSS, JavaScript, Интерфейсы, HTML] Динамическое меню c поддержкой touch move и mouse move на RevolveR

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
19-Окт-2020 00:30

Наверняка многие из вас хотели бы научиться создавать красивые и подвижные меню в духе Android Java и Kotlin приложений. Скорее всего даже многие из вас ради этого уходили в области программирования отдельных приложений и были вынуждены осваивать инородный стек.
В этой статье я расскажу как просто и непринужденно можно создавать линейные динамические меню поддерживающие не только события мыши, но и поинтеры для мобильных версий.
И так: Simple Dynamic Menu by RevolveR Labs.

Начинается все с верстки. Она должна быть семантической, легкой и современной.
<nav class="dynamic-menu">
  <ul>
    <li><a href="https://revolvercmf.ru">RevolveR Labs</a></li>
    <li><a href="#">Ultra newest solutions</a></li>
    <li><a href="#">The way of incredible</a></li>
    <li><a href="#">In search of the best</a></li>
    <li><a href="#">Progressive RevolveR frontends</a></li>
    <li><a href="#">Developing of new era</a></li>
  </ul>
</nav>

Мы используем стандартный маркированный список и HTML 5 в качестве элемента враппера, а чтобы сделать меню плавающим сразу пропишем CSS стили вытягивающие меню на за пределы экрана на всю ширину списка элементов и скроем все лишнее до области видимости:
.dynamic-menu {
  display: inline-block;
  text-align: center;
  overflow: hidden;
  margin: 0 auto;
  height: 3vw;
  width: 80%;
}
  .dynamic-menu ul {
    transition: all 2.5s ease-in-out;
    position: relative;
    list-style: none;
    width: 900vw;
    padding: 0;
    margin: 0;
    left: 0vw;
  }
    .dynamic-menu ul li {
      box-shadow: 0 0 0.1vw #333;
      border: .1vw dashed #fff;
      background: #a2a2a2;
      margin-bottom: 1vw;
      display: inline-block;
      border-radius: .2vw;
      margin-right: .5vw;
      padding: .2vw 1vw;
      background: #888;
      float: left;
    }
      .dynamic-menu ul li a {
        text-shadow: 0 0 0.2vw #fff;
        font: normal 2vw Helvetica;
        text-decoration: none;
        color: #006400;
      }
      .dynamic-menu ul li a:hover {
        text-decoration: underline;
        color: #674c2be0;
      }

Наш CSS готов. Теперь меню будет расположено по центру и все элементы списка не отобразяться. Нам осталось написать хэндлеры меню, которые будут отвечать за плавное перемещение списка при нажатой левой кнопке мыши или событии touch.
Handler для desktop версии
Для работы хэндлера нам понадобится инициализировать RevolveR инстанс и использовать некоторое встроенное API работы с событиями:
let launch = RR.browser;
RR.menuMove = null;
if( !RR.isM ) {
  RR.event('.dynamic-menu ul', 'mousedown', (e) => {
    e.preventDefault();
    if( !RR.menuMove ) {
      RR.menuLeft = RR.curxy[0];
      RR.MenuMoveObserver = RR.event('body', 'mousemove', (e) => {
        e.preventDefault();
        RR.styleApply('.dynamic-menu ul', ['transition: all 0s ease']);
        RR.menuMove = true;
        RR.menuPosition = ( RR.menuLeft - RR.curxy[0] ) *-1;
        RR.styleApply('.dynamic-menu ul', ['left:'+ RR.menuPosition +'px']);
        RR.event('body', 'mouseup', (e) => {
          e.preventDefault();
          if( e.target.tagName === 'A' && !RR.touchFreeze ) {
            //R.loadURI(target.href, target.title);
            console.log(e.target.href);
            RR.touchFreeze = true;
            RR.menuMove = null;
          }
          void setTimeout(() => {
            RR.menuMove = null;
          }, 50);
          void setTimeout(() => {
            if( !RR.menuMove ) {
              RR.styleApply('.dynamic-menu ul', ['left: 0px', 'transition: all 2.5s cubic-bezier(0.175, 0.885, 0.32, 1.275)']);
            }
          }, 2500);
        });
      });
    }
  });
}

Большинство необходимых event уже работают после запуска гетера RR.browser(). Это например отслеживание событий изменения размера окна и постоянно обновление положения указателя мыши RR.curxy.
Здесь мы используем метод блокировки флагами для того, чтобы добиться синхронности выполнения событий и задать блокирующие флаги, например, для реализации запрета события перехода по ссылке, если меню находится в движении.
RR.MenuMoveObserver является собой event стеком, который хранит MD5 hash события для того, чтобы можно было выключить часть хэндлера отвечающего за смену положения по оси X. Мы выключаем обсерверы каждый раз когда событие клик завершилось в пользу mouseup.
Готово. При нажатии на левую клавишу мыши, если держать кнопку утопленной будет происходить отслеживание положения курсора мыши по оси X, а обсервер обеспечит своевременное обновление положения left контейнера списка меню внутри враппера области видимости и лента меню начнет двигаться открывая не поместившиеся элементы списка.
Мобильный handler меню
Основной принцип работы остается примерно таким же, но я не стал схлопывать разные хэндлерры в один обработчик чтобы оставить понятность и читабельность кода.
Проверив, что инициализирован мобильный инстанс браузера мы подключим аналоги слушателей событий уже не для мыши, а для touch экрана.
if( RR.isM ) {
  RR.event('.dynamic-menu ul', 'touchstart', (e) => {
    e.preventDefault();
    RR.menuMove = null;
    RR.event('body', 'touchend', (e) => {
      e.preventDefault();
      if( !RR.menuMove ) {
        RR.touchFreeze = null;
        let target = e.changedTouches[0].target;
        if( RR.isO(RR.MenuMoveObserver) ) {
          for( i of RR.MenuMoveObserver ) {
            RR.detachEvent( i[ 2 ] );
          }
        }
        if( target.tagName === 'A' && !RR.touchFreeze ) {
          //R.loadURI(target.href, target.title);
          console.log(e.target.href);
          RR.touchFreeze = true;
          RR.menuMove = null;
        }
        void setTimeout(() => {
          if( !RR.menuMove ) {
            RR.styleApply('.dynamic-menu ul', ['left: 0px', 'transition: all 2.5s cubic-bezier(0.175, 0.885, 0.32, 1.275)']);
            //RR.animate('.dynamic-menu ul', ['left:0px:1000:wobble']);
          }
        }, 2500);
      }
    });
    if( !RR.menuMove ) {
      RR.menuLeft = e.changedTouches[0].screenX;
      RR.MenuMoveObserver = RR.event('body', 'touchmove', (e) => {
        e.preventDefault();
        RR.styleApply('.dynamic-menu ul', ['transition: all 0s ease']);
        RR.menuMove = true;
        RR.menuPosition = ( RR.menuLeft - e.changedTouches[0].screenX ) *-1;
        RR.styleApply('.dynamic-menu ul', ['left:'+ RR.menuPosition +'px']);
          RR.event('body', 'touchend', (e) => {
            RR.menuMove = null;
          });
      });
    }
  });
}

В коде вы увидите небольшую разницу. Во первых event.target теперь не работает и нужно следить за сериями touch. Я добавил анимацию возвращения меню с эффектом easing и теперь меню само плавно возвращается в начальное положение спустя некоторое время бездействия с меню:
void setTimeout(() => {
  if( !RR.menuMove ) {
    RR.styleApply('.dynamic-menu ul', ['left: 0px', 'transition: all 2.5s cubic-bezier(0.175, 0.885, 0.32, 1.275)']);
  }
}, 2500);

Demo
Чтобы посмотреть как работает Dynamic Menu на базе библиотеки RevolveR вы можете пройти по ссылке.
Итог
Многие из вас используют готовые плагины или встраивают блоки верстки с меню гамбургера, однако динамические линейные меню ни чуть не хуже и позволяют экономить место в интерфейсах. Многим из вас наверняка показалось бы сложным реализовать такие меню из-за разницы в слушателях событий и неумения использовать флаги. Этот пример был направлен показать, что средствами обычного JavaScript можно не обладая какой-то запредельной квалификацией реализовывать такие же вещи, как и в удобных Android приложениях, а мастерская работа с анимациями позволяет создавать потрясные эффекты переходов и разные хуки событий на какие-либо срабатывания.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_css, #_javascript, #_interfejsy (Интерфейсы), #_html, #_javascript, #_html_5, #_touch, #_mouse, #_menu, #_css, #_javascript, #_interfejsy (
Интерфейсы
)
, #_html
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 25-Ноя 10:03
Часовой пояс: UTC + 5