From 0eef5d92404bc2f153f516f3ec6f440699a657ee Mon Sep 17 00:00:00 2001 From: Maxime Poulin Date: Sat, 11 Jun 2016 22:16:17 -0400 Subject: [PATCH] Add touch slideout menu for mobile --- client/css/style.css | 31 +++++++++--- client/js/libs/slideout.js | 99 ++++++++++++++++++++++++++++++++++++++ client/js/lounge.js | 22 +++++---- 3 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 client/js/libs/slideout.js diff --git a/client/css/style.css b/client/css/style.css index cb3d324a..c9cc8e02 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -303,13 +303,26 @@ button { #viewport { height: 100%; - transition: all .4s; + transition: transform 160ms, -webkit-transform 160ms; -webkit-transform: translateZ(0); transform: translateZ(0); -webkit-perspective: 1000; perspective: 1000; } +#viewport.menu-open { + -webkit-transform: translate3d(220px, 0, 0); + transform: translate3d(220px, 0, 0); +} + +#viewport.menu-dragging { + transition: none !important; +} + +#viewport.menu-open .messages { + pointer-events: none; +} + #viewport .lt, #viewport .rt, #chat button.menu { @@ -1595,11 +1608,6 @@ button { margin-top: 60px !important; } - #viewport.lt { - -webkit-transform: translate3d(220px, 0, 0); - transform: translate3d(220px, 0, 0); - } - #viewport.rt #chat .sidebar { -webkit-transform: translate3d(-180px, 0, 0); transform: translate3d(-180px, 0, 0); @@ -1645,6 +1653,17 @@ button { } } +@media (min-width: 769px) { + #viewport { + -webkit-transform: none !important; + transform: none !important; + } + + #viewport.menu-open { + transition: none; + } +} + @media (max-width: 479px) { .container { margin: 40px 0 !important; diff --git a/client/js/libs/slideout.js b/client/js/libs/slideout.js new file mode 100644 index 00000000..330c89b3 --- /dev/null +++ b/client/js/libs/slideout.js @@ -0,0 +1,99 @@ +/** + * Simple slideout menu implementation. + */ +function slideoutMenu(viewport, menu) { + var touchStartPos = null; + var touchCurPos = null; + var touchStartTime = 0; + var menuWidth = parseFloat(window.getComputedStyle(menu).width); + var menuIsOpen = false; + var menuIsMoving = false; + + function toggleMenu(state) { + menuIsOpen = state; + viewport.classList.toggle("menu-open", state); + } + + function disableSlideout() { + viewport.removeEventListener("ontouchstart", onTouchStart); + } + + function onTouchStart(e) { + if (e.touches.length !== 1) { + onTouchEnd(); + return false; + } + + var touch = e.touches.item(0); + viewport.classList.toggle("menu-dragging", true); + + if ((!menuIsOpen && touch.screenX < 50) || (menuIsOpen && touch.screenX > menuWidth)) { + touchStartPos = touch; + touchCurPos = touch; + touchStartTime = Date.now(); + + viewport.addEventListener("touchmove", onTouchMove); + viewport.addEventListener("touchend", onTouchEnd); + } + } + + function onTouchMove(e) { + var touch = touchCurPos = e.touches.item(0); + var setX = touch.screenX - touchStartPos.screenX; + + if (Math.abs(setX > 30)) { + menuIsMoving = true; + } + + if (!menuIsMoving && Math.abs(touch.screenY - touchStartPos.screenY) > 30) { + onTouchEnd(); + return; + } + + if (menuIsOpen) { + setX += menuWidth; + } + + if (setX > menuWidth) { + setX = menuWidth; + } else if (setX < 0) { + setX = 0; + } + + viewport.style.transform = "translate3d(" + setX + "px, 0, 0)"; + + if (menuIsMoving) { + e.preventDefault(); + e.stopPropagation(); + } + } + + function onTouchEnd() { + var diff = touchCurPos.screenX - touchStartPos.screenX; + var absDiff = Math.abs(diff); + + if (absDiff > menuWidth / 2 || Date.now() - touchStartTime < 180 && absDiff > 50) { + toggleMenu(diff > 0); + } + + viewport.removeEventListener("touchmove", onTouchMove); + viewport.removeEventListener("touchend", onTouchEnd); + viewport.classList.toggle("menu-dragging", false); + viewport.style.transform = null; + + touchStartPos = null; + touchCurPos = null; + touchStartTime = 0; + menuIsMoving = false; + } + + viewport.addEventListener("touchstart", onTouchStart); + + return { + disable: disableSlideout, + toggle: toggleMenu, + isOpen: function() { + return menuIsOpen; + } + }; +} diff --git a/client/js/lounge.js b/client/js/lounge.js index b537541a..5c2ac8f0 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -542,19 +542,22 @@ $(function() { }); var viewport = $("#viewport"); + var sidebarSlide = window.slideoutMenu(viewport[0], sidebar[0]); var contextMenuContainer = $("#context-menu-container"); var contextMenu = $("#context-menu"); - viewport.on("click", ".lt, .rt", function(e) { + $("#main").on("click", function(e) { + if ($(e.target).is(".lt")) { + sidebarSlide.toggle(!sidebarSlide.isOpen()); + } else if (sidebarSlide.isOpen()) { + sidebarSlide.toggle(false); + } + }); + + viewport.on("click", ".rt", function(e) { var self = $(this); viewport.toggleClass(self.attr("class")); - if (viewport.is(".lt, .rt")) { - e.stopPropagation(); - chat.find(".chat").one("click", function(e) { - e.stopPropagation(); - viewport.removeClass("lt"); - }); - } + e.stopPropagation(); }); function positionContextMenu(that, e) { @@ -761,7 +764,8 @@ $(function() { toggleNotificationMarkers(false); } - viewport.removeClass("lt"); + sidebarSlide.toggle(false); + var lastActive = $("#windows > .active"); lastActive