Is there any way to get the native look and feeling in Navigation drawer(Menu) in ionic v1 application rather than pushing the center content?
I want to get the Android native look and feel in ionic application like above picture.
Though this is an old question and Ionic v2+ is currently available with this native Navigation drawer features (<ion-menu>
). I just thought to share some code that shows how to implement this feature in Ionic v1 app. I think it will help to those who are still using Ionic v1 in their mobile app. Also Ionic v2+ implementation could be found at the end of this post.
I just created a sample Ionic v1 app using their ready-made sidemenu
app template.
ionic start myApp sidemenu --type ionic1
Then I created the following nativeSideMenu.js
file inside js
folder.
(function () {
'use strict';
angular.module('ionic.contrib.NativeDrawer', ['ionic'])
.controller('drawerCtrl', ['$rootScope', '$scope', '$element', '$attrs', '$ionicGesture', '$ionicBody', '$document', function ($rootScope, $scope, $element, $attr, $ionicGesture, $ionicBody, $document) {
var el = document.querySelectorAll("drawer")[0];
var mainContent = angular.element(document.querySelectorAll("ion-pane")[0]);
var dragging = false;
var startX, lastX, offsetX, newX, startY, lastY, offsetY, newY;
var side;
var isOpen = false;
var primaryScrollAxis = null;
mainContent.addClass('drawer-content');
// How far to drag before triggering
var thresholdX = 10;
// How far from edge before triggering
var edgeX = 40;
// Width of drawer set in css
var drawerWidth = 270;
var LEFT = 0;
var RIGHT = 1;
var isTargetDrag = false;
var isContentDrag = false;
var width = $element[0].clientWidth;
var height = $element[0].clientHeight;
var enableAnimation = function () {
angular.element(el).addClass('animate');
};
var disableAnimation = function () {
angular.element(el).removeClass('animate');
};
// Check if this is on target or not
var isTarget = function (el) {
while (el) {
if (el === $element[0]) {
return true;
}
el = el.parentNode;
}
};
var startDrag = function (e) {
disableAnimation();
dragging = true;
offsetX = lastX - startX;
offsetY = lastY - startY;
$ionicBody.addClass('drawer-open');
};
var startTargetDrag = function (e) {
disableAnimation();
dragging = true;
isTargetDrag = true;
offsetX = lastX - startX;
offsetY = lastY - startY;
};
var doEndDrag = function (e) {
startX = null;
lastX = null;
offsetX = null;
startY = null;
lastY = null;
offsetY = null;
isTargetDrag = false;
if (!dragging) {
return;
}
dragging = false;
enableAnimation();
if (isOpen && newX < (-width / 3)) {
el.style.transform = el.style.webkitTransform = 'translate3d(' + (-width - 5) + 'px, 0, 0)';
$ionicBody.removeClass('drawer-open');
isOpen = false;
} else if (newX < (-width / 1.5)) {
el.style.transform = el.style.webkitTransform = 'translate3d(' + (-width - 5) + 'px, 0, 0)';
$ionicBody.removeClass('drawer-open');
isOpen = false;
} else {
el.style.transform = el.style.webkitTransform = 'translate3d(0px, 0, 0)';
$ionicBody.addClass('drawer-open');
isOpen = true;
}
};
var doEndContentDrag = function (e) {
if (startX > lastX) {
startX = null;
lastX = null;
offsetX = null;
startY = null;
lastY = null;
offsetY = null;
isTargetDrag = false;
isContentDrag = false;
if (!dragging) {
return;
}
dragging = false;
enableAnimation();
el.style.transform = el.style.webkitTransform = 'translate3d(-101%, 0, 0)';
$ionicBody.removeClass('drawer-open');
isOpen = false;
}
else {
el.style.transform = el.style.webkitTransform = 'translate3d(0 0, 0)';
}
};
var doDrag = function (e) {
if (e.defaultPrevented) {
return;
}
if (!lastX) {
startX = e.gesture.touches[0].pageX;
}
if (!lastY) {
startY = e.gesture.touches[0].pageY;
}
lastX = e.gesture.touches[0].pageX;
lastY = e.gesture.touches[0].pageY;
if (!dragging) {
// Dragged 15 pixels and finger is by edge
if (Math.abs(lastX - startX) > thresholdX) {
if (isTarget(e.target)) {
startTargetDrag(e);
} else if (startX < edgeX) {
startDrag(e);
}
}
// Closing from outside of drawer
else if (isOpen && startX > width) {
disableAnimation();
dragging = true;
isContentDrag = true;
}
} else {
newX = Math.min(0, (-width + (lastX - offsetX)));
newY = Math.min(0, (-height + (lastY - offsetY)));
var absX = Math.abs(lastX - startX);
var absY = Math.abs(lastY - startY);
if (isContentDrag && lastX < startX) {
var drawerOffsetX = lastX - drawerWidth;
el.style.transform = el.style.webkitTransform = 'translate3d(' + -absX + 'px, 0, 0)';
}
else if (isTargetDrag && absX > absY + 5) {
el.style.transform = el.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)';
} else {
el.style.transform = el.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)';
}
}
if (dragging) {
e.gesture.srcEvent.preventDefault();
}
};
side = $attr.side == 'left' ? LEFT : RIGHT;
var dragFunction = function (e) {
if (el.attributes.candrag) {
doDrag(e);
}
};
var dragEndFunction = function (e) {
if (el.attributes.candrag) {
doEndDrag(e);
}
};
var onContentDrag = function (e) {
if (isOpen) {
doDrag(e);
}
};
var onContentTap = function (e) {
if (isOpen) {
closeDrawer();
e.gesture.srcEvent.preventDefault();
}
};
var contentDragEndFunction = function (e) {
if (isOpen) {
doEndContentDrag(e);
e.gesture.srcEvent.preventDefault();
}
};
var openDrawer = function () {
enableAnimation();
el.style.transform = el.style.webkitTransform = 'translate3d(0%, 0, 0)';
$ionicBody.addClass('drawer-open');
isOpen = true;
};
var closeDrawer = function () {
enableAnimation();
el.style.transform = el.style.webkitTransform = 'translate3d(-101%, 0, 0)';
$ionicBody.removeClass('drawer-open');
isOpen = false;
};
var toggleDrawer = function () {
if (isOpen) {
closeDrawer();
} else {
openDrawer();
}
};
this.close = function () {
closeDrawer();
};
$rootScope.toggleDrawerRoot = function () {
toggleDrawer();
};
this.open = function () {
openDrawer();
};
this.toggle = function () {
toggleDrawer();
};
$ionicGesture.on('drag', function (e) {
doDrag(e);
}, $document);
$ionicGesture.on('dragend', function (e) {
doEndDrag(e);
}, $document);
var dragGesture = $ionicGesture.on('drag', dragFunction, $document);
var dragEndGesture = $ionicGesture.on('dragend', dragEndFunction, $document);
var contentTapGesture = $ionicGesture.on('tap', onContentTap, mainContent);
var contentDragGesture = $ionicGesture.on('drag', onContentDrag, mainContent);
var contentDragEndGesture = $ionicGesture.on('dragend', contentDragEndFunction, mainContent);
$scope.$on('$destroy', function () {
$ionicGesture.off(dragGesture, 'drag', dragFunction);
$ionicGesture.off(dragEndGesture, 'dragend', dragEndFunction);
$ionicGesture.off(contentTapGesture, 'tap', onContentTap);
$ionicGesture.off(contentDragGesture, 'drag', onContentDrag);
$ionicGesture.off(contentDragEndGesture, 'dragend', contentDragEndFunction);
});
}])
.directive('drawer', ['$rootScope', '$ionicGesture', function ($rootScope, $ionicGesture) {
return {
restrict: 'E',
controller: 'drawerCtrl',
link: function ($scope, $element, $attr, ctrl) {
$element.addClass($attr.side);
$scope.openDrawer = function () {
ctrl.open();
};
$scope.closeDrawer = function () {
ctrl.close();
};
$scope.toggleDrawer = function () {
ctrl.toggle();
};
}
}
}])
.directive('menuAndDrawerToggle', ['$rootScope', '$ionicGesture', function ($rootScope, $ionicGesture) {
return {
controller: '',
link: function ($scope, $element, $attr) {
$element.bind('click', function () {
$rootScope.toggleDrawerRoot();
});
}
};
}])
.directive('menuAndDrawerClose', ['$ionicViewService', function ($ionicViewService) {
return {
restrict: 'AC',
require: '^drawer',
link: function ($scope, $element, $attr, ctrl) {
$element.bind('click', function () {
ctrl.close();
});
}
};
}]);
})();
And also created the following nativeSideMenu.css
file inside css
folder
drawer {
display: block;
position: fixed;
width: 270px;
height: 100%;
z-index: 100;
background-color:white;
}
drawer .header-menu{
margin-top:0px;
padding-top:0px;
}
drawer .header-menu ion-list .list{
padding-top:0px;
}
drawer.animate {
-webkit-transition: all ease-in-out 400ms;
transition: all ease-in-out 400ms;
}
drawer.left {
-webkit-transform: translate3d(-101%, 0, 0);
transform: translate3d(-101%, 0, 0);
box-shadow: 1px 5px 10px rgba(0,0,0,0.3);
}
drawer.right {
right: 0;
top: 0;
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
}
.drawer-open .drawer-content .pane, .drawer-open .drawer-content .scroll-content {
pointer-events: none;
opacity:0.2;
}
drawer .item-icon-left .icon{
font-size: 24px;
padding-bottom: 2px;
}
ion-side-menu .current a.item-content,ion-side-menu .clickable.current, drawer .current a.item-content, drawer .clickable.current {
color:#343434 !important;
}
drawer a.item-content, drawer .clickable{
color: #858585;
font-size:20px;
}
drawer .item-complex .item-content{
padding-top:16px;
padding-bottom:16px;
font-size:20px;
}
/*This section would normally have .platform-android at the front */
.platform-android4_0 drawer.left{
box-shadow: none;
border-left:0px;
border-right:0px;
}
.header-menu{
top:0px;
bottom:44px;
}
.menu .scroll-content {
top: 42px;
}
.menu-content{
box-shadow: none;
}
.drawer-open ion-pane.pane{
background-color: rgb(0,0,0);
}
.drawer-open view{
background-color: rgb(0,0,0);
}
.ion-view.pane{
transition: opacity .25s ease-in-out;
-moz-transition: opacity .25s ease-in-out;
-webkit-transition: opacity .25s ease-in-out;
}
.menu-open ion-view.pane, .drawer-open ion-view.pane{
/* opacity:0.4;
*/
}
.bar .title.no-nav{
text-align: left;
left: 0px !important;
}
.bar .button-icon.ion-navicon{
margin-left: -20px;
padding-right: 20px;
}
.bar .title{
text-align: left;
left: 16px !important;
}
.modal-wrapper .bar .title{
text-align: center;
left: 0px !important;
}
.bar .title.title-center{
text-align: center;
left: 0px !important;
right: 0px !important;
}
.ion-ios7-arrow-back:before{
content:"\f2ca";
}
.bar .button.button-icon.ion-ios7-arrow-back:before{
font-size: 17px;
line-height: 32px;
padding-right: 100px;
}
After that I added reference links to index.html
file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<link rel="manifest" href="manifest.json">
<!-- un-comment this code to enable service worker
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.log('Error', err));
}
</script>-->
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<link href="css/nativeSideMenu.css" rel="stylesheet">
<!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
<link href="css/ionic.app.css" rel="stylesheet">
-->
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<!-- your app's js -->
<script src="js/app.js"></script>
<script src="js/nativeSideMenu.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-app="starter">
<ion-nav-view></ion-nav-view>
</body>
</html>
Then I included the ionic.contrib.NativeDrawer
in angular.module
in app.js
file.
angular.module('starter', ['ionic', 'starter.controllers','ionic.contrib.NativeDrawer'])
After that I updated the code of menu.html
file inside templates folder like below.
<ion-side-menus>
<ion-side-menu-content>
<ion-nav-bar class="bar-stable bar-positive-900">
<ion-nav-back-button>
</ion-nav-back-button>
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" menu-and-drawer-toggle>
</button>
</ion-nav-buttons>
</ion-nav-bar>
<ion-pane drawer-menu-content>
<ion-nav-view name="menuContent"></ion-nav-view>
</ion-pane>
</ion-side-menu-content>
<drawer side="left">
<ion-content>
<ion-list>
<ion-item menu-and-drawer-close ng-click="login()">
Login
</ion-item>
<ion-item menu-and-drawer-close href="#/app/search">
Search
</ion-item>
<ion-item menu-and-drawer-close href="#/app/browse">
Browse
</ion-item>
<ion-item menu-and-drawer-close href="#/app/playlists">
Playlists
</ion-item>
</ion-list>
</ion-content>
</drawer>
</ion-side-menus>
Now just run the ionic serve
and have the native feeling in Navigation drawer.
Use bellow reference links for more details
We do not have that much of work to do in Ionic v2+ because by default it is giving the native feeling of the Navigation drawer. But we could change that behavior by passing the value to type
on the <ion-menu>
<ion-menu type="overlay" [content]="mycontent">...</ion-menu>
According to DOC type
details as bellow.
The display type of the menu. Default varies based on the mode, see the menuType in the config. Available options: "overlay", "reveal", "push".
Hope this will help to someone else!