Skip to content

Instantly share code, notes, and snippets.

@davidmaogomezz
Created September 16, 2015 18:13
Show Gist options
  • Save davidmaogomezz/b9f5b086be6707b585b5 to your computer and use it in GitHub Desktop.
Save davidmaogomezz/b9f5b086be6707b585b5 to your computer and use it in GitHub Desktop.
Ionic long press list reorder
<html ng-app="ionicApp">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Ionic App</title>
<link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
</head>
<body ng-controller="MyCtrl">
<ion-header-bar class="bar-positive">
<h1 class="title">Bookmarks</h1>
</ion-header-bar>
<ion-content>
<ion-list sortable draggable=".card" sorted="onReorder($fromIndex, $toIndex)">
<ion-item ng-repeat="contact in contacts" class="item-light item-avatar card item-complex">
<div class="item-content">
<img ng-src="{{contact.img}}" />
<div class="item-divider">{{contact.name}}</div>
<div class="item-text-wrap">
<ul>
<li>Phone: {{contact.phone}}</li>
<li>Mobile: {{contact.mobile}}</li>
<li>Email: {{contact.email}}</li>
</ul>
</div>
</div>
</ion-item>
</ion-list>
</ion-content>
</body>
</html>
ionic.Gestures.gestures.Hold.defaults.hold_threshold = 20;
angular.module('ionicApp', ['ionic'])
.controller('MyCtrl', function($scope) {
$scope.onReorder = function (fromIndex, toIndex) {
var moved = $scope.contacts.splice(fromIndex, 1);
$scope.contacts.splice(toIndex, 0, moved[0]);
};
$scope.contacts = [
{ name: 'Frank', img: 'http://placehold.it/82x132', phone: '0101 123456', mobile: '0770 123456', email: 'frank@emailionicsorter.com' },
{ name: 'Susan', img: 'http://placehold.it/82x132', phone: '0101 123456', mobile: '0770 123456', email: 'frank@emailionicsorter.com' },
{ name: 'Emma', img: 'http://placehold.it/82x132', phone: '0101 123456', mobile: '0770 123456', email: 'frank@emailionicsorter.com' },
{ name: 'Scott', img: 'http://placehold.it/82x132', phone: '0101 123456', mobile: '0770 123456', email: 'frank@emailionicsorter.com' },
{ name: 'Bob', img: 'http://placehold.it/82x132', phone: '0101 123456', mobile: '0770 123456', email: 'frank@emailionicsorter.com' },
{ name: 'Olivia', img: 'http://placehold.it/82x132', phone: '0101 123456', mobile: '0770 123456', email: 'frank@emailionicsorter.com' },
{ name: 'Anne', img: 'http://placehold.it/82x132', phone: '0101 123456', mobile: '0770 123456', email: 'frank@emailionicsorter.com' }
];
})
.directive('sortable', ['$ionicGesture', '$ionicScrollDelegate', function ($ionicGesture, $ionicScrollDelegate) {
return {
restrict: 'A',
scope: {
draggable: '@',
sorted: '&'
},
link: function (scope, element, attrs) {
var settings = {
draggable: scope.draggable ? scope.draggable : '.card',
duration: 200
};
var dragging = null, placeholder = null, offsetY = 0, marginTop = 0;
var cardSet, initialIndex, currentIndex, animating = false;
var placeholderHeight;
var scrollInterval;
var createPlaceholder = function createPlaceholder(height) {
// Use marginTop to compensate for extra margin when animating the placeholder
return $('<div></div>')
.css({
height: height + 'px',
marginTop: (currentIndex > 0 ? -marginTop : -1) + 'px'
})
.addClass('placeholder');
};
var touchHold = function touchHold(e) {
// Get the element we're about to start dragging
dragging = angular.element(e.target).closest(settings.draggable);
if (!dragging.length) dragging = null;
if (dragging) {
// Get the initial index
initialIndex = currentIndex = dragging.index(settings.draggable);
var position = dragging.position();
// Get relative position of touch
var clientY = e.gesture.touches[0].clientY;
offsetY = clientY - position.top - element.offset().top;
// Switch to Absolute position at same location
dragging.css({
position: 'absolute',
zIndex: 1000,
left: position.left + 'px',
top: position.top + 'px',
width: dragging.outerWidth() + 'px'
})
.addClass('dragging');
// Get the set of cards that were re-ordering with
cardSet = element.find(settings.draggable + ':not(.dragging)');
// We need to know the margin size so we can compensate for having two
// margins where we previously had one (due to the placeholder being there)
marginTop = parseInt(dragging.css('marginTop')) + 1;
// Replace with placeholder (add the margin for when placeholder is full size)
placeholderHeight = dragging.outerHeight() + marginTop;
placeholder = createPlaceholder(placeholderHeight);
placeholder.insertAfter(dragging);
// Interval to handle auto-scrolling window when at top or bottom
initAutoScroll();
scrollInterval = setInterval(autoScroll, 20);
}
};
var holdGesture = $ionicGesture.on('hold', touchHold, element);
var touchMove = function touchMove(e) {
if (dragging) {
e.stopPropagation();
touchY = e.touches ? e.touches[0].clientY : e.clientY;
var newTop = touchY - offsetY - element.offset().top;
// Reposition the dragged element
dragging.css('top', newTop + 'px');
// Check for position in the list
var newIndex = 0;
cardSet.each(function (i) {
if (newTop > $(this).position().top) {
newIndex = i + 1;
}
});
if (!animating && newIndex !== currentIndex) {
currentIndex = newIndex;
var oldPlaceholder = placeholder;
// Animate in a new placeholder
placeholder = createPlaceholder(1);
// Put it in the right place
if (newIndex < cardSet.length) {
placeholder.insertBefore(cardSet.eq(newIndex));
} else {
placeholder.insertAfter(cardSet.eq(cardSet.length - 1));
}
// Animate the new placeholder to full height
animating = true;
setTimeout(function () {
placeholder.css('height', placeholderHeight + 'px');
// Animate out the old placeholder
oldPlaceholder.css('height', 1);
setTimeout(function () {
oldPlaceholder.remove();
animating = false;
}, settings.duration);
}, 50);
}
}
};
var touchMoveGesture = $ionicGesture.on('touchmove', touchMove, element);
var mouseMoveGesture = $ionicGesture.on('mousemove', touchMove, element);
var touchRelease = function touchRelease(e) {
if (dragging) {
// Set element back to normal
dragging.css({
position: '',
zIndex: '',
left: '',
top: '',
width: ''
}).removeClass('dragging');
// Remove placeholder
placeholder.remove();
placeholder = null;
if (initialIndex !== currentIndex && scope.sorted) {
// Call the callback with the instruction to re-order
scope.$fromIndex = initialIndex;
scope.$toIndex = currentIndex;
scope.$apply(scope.sorted);
}
dragging = null;
clearInterval(scrollInterval);
}
};
var releaseGesture = $ionicGesture.on('release', touchRelease, element);
scope.$on('$destroy', function () {
$ionicGesture.off(holdGesture, 'hold', touchHold);
$ionicGesture.off(touchMoveGesture, 'touchmove', touchMove);
$ionicGesture.off(mouseMoveGesture, 'mousemove', touchMove);
$ionicGesture.off(releaseGesture, 'release', touchRelease);
});
var touchY, scrollHeight, containerTop, maxScroll;
var scrollBorder = 80, scrollSpeed = 0.2;
// Setup the autoscroll based on the current scroll window size
var initAutoScroll = function initAutoScroll() {
touchY = -1;
var scrollArea = element.closest('.scroll');
var container = scrollArea.parent();
scrollHeight = container.height();
containerTop = container.position().top;
maxScroll = scrollArea.height() - scrollHeight;
};
// Autoscroll function to scroll window up and down when
// the touch point is close to the top or bottom
var autoScroll = function autoScroll() {
var scrollChange = 0;
if (touchY >= 0 && touchY < containerTop + scrollBorder) {
// Should scroll up
scrollChange = touchY - (containerTop + scrollBorder);
} else if (touchY >= 0 && touchY > scrollHeight - scrollBorder) {
// Should scroll down
scrollChange = touchY - (scrollHeight - scrollBorder);
}
if (scrollChange !== 0) {
// get the updated scroll position
var newScroll = $ionicScrollDelegate.getScrollPosition().top + scrollSpeed * scrollChange;
// Apply scroll limits
if (newScroll < 0)
newScroll = 0;
else if (newScroll > maxScroll)
newScroll = maxScroll;
// Set the scroll position
$ionicScrollDelegate.scrollTo(0, newScroll, false);
}
};
}
};
}]);
body {
cursor: url('http://ionicframework.com/img/finger.png'), auto;
}
.pane, .view {
background-color: #e0e0e0;
}
.item-avatar .item-content > img:first-child {
width: 40px;
max-width: 40px;
height: 65px;
max-height: 65px;
margin-top: 12px;
}
.item-avatar .item-content .item-text-wrap {
font-size: 14px;
line-height: 1.3;
}
.item-light .item-divider {
background-color: white;
}
.item-complex.item-light > .item-content.active, .item-complex.item-light > .item-content:active {
background-color: white;
}
.dragging {
-moz-transform: scale(1.05, 1.05);
-ms-transform: scale(1.05, 1.05);
-o-transform: scale(1.05, 1.05);
-webkit-transform: scale(1.05, 1.05);
transform: scale(1.05, 1.05);
-moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
-webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
}
.placeholder {
-moz-transition: all 200ms ease-in-out;
-o-transition: all 200ms ease-in-out;
-webkit-transition: all 200ms ease-in-out;
transition: all 200ms ease-in-out;
}
@vTrip
Copy link

vTrip commented Jun 15, 2016

how does this differ from andy lee's ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment