diff --git a/client/css/style.css b/client/css/style.css index e134aeeb..9bd5569a 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -269,13 +269,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 { @@ -1635,11 +1648,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); @@ -1680,6 +1688,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 089a0931..5acfa82d 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -558,19 +558,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) { @@ -782,7 +785,8 @@ $(function() { toggleNotificationMarkers(false); } - viewport.removeClass("lt"); + sidebarSlide.toggle(false); + var lastActive = $("#windows > .active"); lastActive