AngularJS + Masonry (with zoom)
by Anov Siradj
HTML
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/masonry/2.1.08/jquery.masonry.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.5/angular.min.js"></script>
<div ng-controller="MainCtrl">
<input type="text" ng-model="nameFilter" />
<a href="#" ng-click="order = 'id'">Order by id</a>
<a href="#" ng-click="order = 'name'">Order by name</a>
<a href="#" ng-click="order = 'age'">Order by age</a>
<ul class="grid" masonry="true">
<li ng-repeat="item in items | filter: { name: nameFilter } | orderBy: order">
<span>{{item.name}}</span>
<br /> <span>Age: {{item.age}}</span>
<br /> <span>Company: {{item.company}}</span>
</li>
</ul>
</div>
CSS
.grid {
border: solid 1px #ccc padding: 10px 10px 0 0;
}
.grid li {
display: inline-block;
width: 100px;
height: 100px;
border: solid 1px #ccc;
margin: 0 0 10px 10px;
background: #eee;
}
/**** Transitions ****/
.masonry,
.masonry li { /* apply before masonry add class */
position: absolute; /* important to add it before enter animation start */
-webkit-transition-duration: .7s;
transition-duration: .7s;
-webkit-transition-property: all;
transition-property: all;
}
.masonry .masonry-enter { /* limit animated properties for entering elements */
-webkit-transition-property: -webkit-transform, opacity;
transition-property: transform, opacity;
}
.masonry-enter,
.masonry-leave.masonry-leave-active {
opacity: 0;
-webkit-transform: scale(0.1);
transform: scale(0.1);
}
.masonry-leave,
.masonry-enter.masonry-enter-active {
opacity: 1;
-webkit-transform: scale(1);
transform: scale(1);
}
JavaScript
angular.module('app', [])
.directive("masonry", function () {
var NGREPEAT_SOURCE_RE = '<!-- ngRepeat: ((.*) in ((.*?)( track by (.*))?)) -->';
return {
compile: function(element, attrs) {
// auto add animation to brick element
var animation = attrs.ngAnimate || "'masonry'";
var $brick = element.children();
$brick.attr("ng-animate", animation);
// generate item selector (exclude leaving items)
var type = $brick.prop('tagName');
var itemSelector = type+":not([class$='-leave-active'])";
return function (scope, element, attrs) {
var options = angular.extend({
itemSelector: itemSelector
}, scope.$eval(attrs.masonry));
// try to infer model from ngRepeat
if (!options.model) {
var ngRepeatMatch = element.html().match(NGREPEAT_SOURCE_RE);
if (ngRepeatMatch) {
options.model = ngRepeatMatch[4];
}
}
// initial animation
element.addClass('masonry');
// Wait inside directives to render
setTimeout(function () {
element.masonry(options);
element.on("$destroy", function () {
element.masonry('destroy')
});
if (options.model) {
scope.$apply(function() {
scope.$watchCollection(options.model, function (_new, _old) {
if(_new == _old) return;
// Wait inside directives to render
setTimeout(function () {
...