Taken from here: https://github.com/DevAndyLee/Ionic-Sorter Read more here: http://blog.scottlogic.com/2014/11/25/ionic-sorter.html
A Pen by Leonid Zolotarev on CodePen.
<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> |
Taken from here: https://github.com/DevAndyLee/Ionic-Sorter Read more here: http://blog.scottlogic.com/2014/11/25/ionic-sorter.html
A Pen by Leonid Zolotarev on CodePen.
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; | |
} |
how does this differ from andy lee's ?