Skip to content

Commit

Permalink
feat(collection-repeat): add repeat lsdirective for huge lists
Browse files Browse the repository at this point in the history
  • Loading branch information
ajoslin committed Apr 28, 2014
1 parent 73b9775 commit f0a1c03
Show file tree
Hide file tree
Showing 5 changed files with 556 additions and 42 deletions.
6 changes: 5 additions & 1 deletion demos/collection-repeat/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ <h1 class="title">3000 Contacts</h1>
<a class="item"
collection-repeat="item in getContacts()"
collection-item-height="getItemHeight(item)"
collection-item-width="getItemWidth(item)"
ng-href="https://www.google.com/#q={{item.first_name + '+' + item.last_name}}"
ng-style="{height: getItemHeight(item), 'line-height': getItemHeight(item) + 'px', 'padding-top': 0, 'padding-bottom': 0}"
ng-style="{height: getItemHeight(item), 'line-height': getItemHeight(item) + 'px', 'padding-top': 0, 'padding-bottom': 0, width: getItemWidth(item)}"
ng-class="{'item-divider': item.isLetter}">
<img ng-if="!item.isLetter" ng-src="http://placekitten.com/60/{{55 + ($index % 10)}}">
{{item.letter || (item.first_name+' '+item.last_name)}}
Expand Down Expand Up @@ -83,6 +84,9 @@ <h1 class="title">3000 Contacts</h1>
margin-top: 10px;
margin-right: 10px;
}
.list {
height: 100%;
}
</style>
</body>
</html>
5 changes: 4 additions & 1 deletion demos/collection-repeat/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ angular.module('contactsApp', ['ionic'])

//Letters are shorter, everything else is 52 pixels
$scope.getItemHeight = function(item) {
return item.isLetter ? 38 : 80;
return item.isLetter ? 45 : '25%';
};
$scope.getItemWidth = function(item) {
return item.isLetter ? '100%' : '50%';
};

var letterHasMatch = {};
Expand Down
26 changes: 20 additions & 6 deletions js/angular/directive/collectionRepeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,30 @@ function($collectionRepeatManager, $collectionDataSource, $parse) {
} else if (!isVertical && !$attr.collectionItemWidth) {
throw new Error("collection-repeat expected attribute collection-item-width to be a an expression that returns a number.");
}
var heightGetter = $attr.collectionItemHeight ?
$attr.collectionItemHeight = $attr.collectionItemHeight || '100%';
$attr.collectionItemWidth = $attr.collectionItemWidth || '100%';

var heightParsed = $attr.collectionItemHeight ?
$parse($attr.collectionItemHeight) :
function() { return scrollView.__clientHeight; };
var widthGetter = $attr.collectionItemWidth ?
var widthParsed = $attr.collectionItemWidth ?
$parse($attr.collectionItemWidth) :
function() { return scrollView.__clientWidth; };
console.log(widthGetter());
setTimeout(function() {
console.log(widthGetter());
});

var heightGetter = function(scope, locals) {
var result = heightParsed(scope, locals);
if (angular.isString(result) && result.indexOf('%') > -1) {
return Math.floor(parseInt(result, 10) / 100 * scrollView.__clientHeight);
}
return result;
};
var widthGetter = function(scope, locals) {
var result = widthParsed(scope, locals);
if (angular.isString(result) && result.indexOf('%') > -1) {
return Math.floor(parseInt(result, 10) / 100 * scrollView.__clientWidth);
}
return result;
};

var match = $attr.collectionRepeat.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
if (!match) {
Expand Down
88 changes: 54 additions & 34 deletions js/angular/service/collectionRepeatManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ function($rootScope, $timeout) {
this.dataSource = options.dataSource;
this.element = options.element;
this.scrollView = options.scrollView;
this.itemSizePrimary = options.itemSizePrimary;
this.itemSizeSecondary = options.itemSizeSecondary;

this.isVertical = !!this.scrollView.options.scrollingY;
this.renderedItems = {};

this.lastRenderScrollValue = this.bufferTransformOffset = this.hasBufferStartIndex =
this.hasBufferEndIndex = this.bufferItemsLength = 0;
this.setCurrentIndex(0);

this.scrollView.__$callback = this.scrollView.__callback;
this.scrollView.__callback = angular.bind(this, this.renderScroll);
Expand All @@ -34,12 +33,18 @@ function($rootScope, $timeout) {
this.scrollSize = function() {
return this.scrollView.__clientHeight;
};
this.getSecondaryScrollSize = function() {
this.secondaryScrollSize = function() {
return this.scrollView.__clientWidth;
};
this.transformString = function(y, x) {
return 'translate3d('+x+'px,'+y+'px,0)';
};
this.primaryDimension = function(dim) {
return dim.height;
};
this.secondaryDimension = function(dim) {
return dim.width;
};
} else {
this.scrollView.options.getContentWidth = getViewportSize;

Expand All @@ -52,12 +57,18 @@ function($rootScope, $timeout) {
this.scrollSize = function() {
return this.scrollView.__clientWidth;
};
this.getSecondaryScrollSize = function() {
this.secondaryScrollSize = function() {
return this.scrollView.__clientHeight;
};
this.transformString = function(x, y) {
return 'translate3d('+x+'px,'+y+'px,0)';
};
this.primaryDimension = function(dim) {
return dim.width;
};
this.secondaryDimension = function(dim) {
return dim.height;
};
}
}

Expand All @@ -67,36 +78,41 @@ function($rootScope, $timeout) {
this.removeItem(i);
}
},
resize: function() {
calculateDimensions: function() {
var primaryPos = 0;
var secondaryPos = 0;
var itemsPerSpace = 0;
var len = this.dataSource.dimensions.length;
this.dimensions = this.dataSource.dimensions.map(function(dimensions, index) {
var secondaryScrollSize = this.secondaryScrollSize();
var previous;

return this.dataSource.dimensions.map(function(dim) {
var rect = {
primarySize: this.isVertical ? dimensions.height : dimensions.width,
secondarySize: this.isVertical ? dimensions.width : dimensions.height,
primaryPos: primaryPos,
secondaryPos: secondaryPos
primarySize: this.primaryDimension(dim),
secondarySize: Math.min(this.secondaryDimension(dim), secondaryScrollSize)
};

itemsPerSpace++;
secondaryPos += rect.secondarySize;
if (secondaryPos >= this.getSecondaryScrollSize()) {
secondaryPos = 0;
primaryPos += rect.primarySize;

if (!this.itemsPerSpace) {
this.itemsPerSpace = itemsPerSpace;
if (previous) {
secondaryPos += previous.secondarySize;
if (previous.primaryPos === primaryPos &&
secondaryPos + rect.secondarySize > secondaryScrollSize) {
secondaryPos = 0;
primaryPos += previous.primarySize;
} else {
}
}

rect.primaryPos = primaryPos;
rect.secondaryPos = secondaryPos;

previous = rect;
return rect;
}, this);

this.viewportSize = primaryPos;
},
resize: function() {
this.dimensions = this.calculateDimensions();
var last = this.dimensions[this.dimensions.length - 1];
this.viewportSize = last ? last.primaryPos + last.primarySize : 0;
this.setCurrentIndex(0);
this.lastRenderScrollValue = 0;
this.render(true);
},
setCurrentIndex: function(index, height) {
Expand Down Expand Up @@ -129,40 +145,45 @@ function($rootScope, $timeout) {
},
getIndexForScrollValue: function(i, scrollValue) {
var rect;
//Scrolling down
//Scrolling up
if (scrollValue <= this.dimensions[i].primaryPos) {
while ( (rect = this.dimensions[i - 1]) && rect.primaryPos > scrollValue) {
i -= this.itemsPerSpace;
i--;
}
//Scrolling up
//Scrolling down
} else {
while ( (rect = this.dimensions[i + 1]) && rect.primaryPos < scrollValue) {
i += this.itemsPerSpace;
i++;
}
}
return i;
},
render: function(shouldRedrawAll) {
if (this.currentIndex >= this.dataSource.getLength()) {
return;
}

var i;
if (shouldRedrawAll) {
if (this.currentIndex >= this.dataSource.getLength() || shouldRedrawAll) {
for (i in this.renderedItems) {
this.removeItem(i);
}
if (this.currentIndex >= this.dataSource.getLength()) return null;
}

var rect;
var scrollValue = this.scrollValue();
var scrollDelta = scrollValue - this.lastRenderScrollValue;
var scrollSize = this.scrollSize();
var scrollSizeEnd = scrollSize + scrollValue;
var startIndex = this.getIndexForScrollValue(this.currentIndex, scrollValue);
var bufferStartIndex = Math.max(0, startIndex - this.itemsPerSpace);

//Make buffer start on previous row
var bufferStartIndex = Math.max(startIndex - 1, 0);
while (bufferStartIndex > 0 &&
(rect = this.dimensions[bufferStartIndex]) &&
rect.primaryPos === this.dimensions[startIndex - 1].primaryPos) {
bufferStartIndex--;
}
var startPos = this.dimensions[bufferStartIndex].primaryPos;

i = bufferStartIndex;
var rect;
while ((rect = this.dimensions[i]) && (rect.primaryPos - rect.primarySize < scrollSizeEnd)) {
this.renderItem(i, rect.primaryPos - startPos, rect.secondaryPos);
i++;
Expand All @@ -183,7 +204,6 @@ function($rootScope, $timeout) {
}
},
renderItem: function(dataIndex, primaryPos, secondaryPos) {
var self = this;
var item = this.dataSource.getItem(dataIndex);
if (item) {
this.dataSource.attachItem(item);
Expand Down
Loading

0 comments on commit f0a1c03

Please sign in to comment.