mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
Start of virtual scrolling for lists
This commit is contained in:
@ -15,5 +15,19 @@ export class Content {
|
||||
// but we should be able to stamp out this behavior with a base IonicComponent
|
||||
// or something, so all elements have a domElement reference or a getElement() method
|
||||
this.domElement = elementRef.domElement;
|
||||
|
||||
setTimeout(() => {
|
||||
this.scrollElement = this.domElement.children[0];
|
||||
});
|
||||
}
|
||||
|
||||
addScrollEventListener(handler) {
|
||||
if(!this.scrollElement) { return; }
|
||||
|
||||
this.scrollElement.addEventListener('scroll', handler);
|
||||
|
||||
return () => {
|
||||
this.scrollElement.removeEventListener('scroll', handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,26 +3,52 @@ import {Component, Directive} from 'angular2/src/core/annotations_impl/annotatio
|
||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
|
||||
import {IonicComponent} from 'ionic/config/component'
|
||||
import {ListVirtualScroll} from './virtual'
|
||||
|
||||
import * as util from 'ionic/util';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'ion-list'
|
||||
selector: 'ion-list',
|
||||
properties: [
|
||||
'items',
|
||||
'virtual',
|
||||
'content'
|
||||
]
|
||||
})
|
||||
@View({
|
||||
template: `<content></content>`
|
||||
})
|
||||
export class List {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
constructor(
|
||||
elementRef: ElementRef
|
||||
) {
|
||||
this.domElement = elementRef.domElement;
|
||||
this.config = List.config.invoke(this);
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('Content', this.content);
|
||||
console.log('Virtual?', this.virtual);
|
||||
console.log('Items?', this.items.length, 'of \'em');
|
||||
|
||||
if(util.isDefined(this.virtual)) {
|
||||
this._initVirtualScrolling();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_initVirtualScrolling() {
|
||||
if(!this.content) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._virtualScrollingManager = new ListVirtualScroll(this);
|
||||
}
|
||||
|
||||
setItemTemplate(item) {
|
||||
this.itemTemplate = item;
|
||||
}
|
||||
}
|
||||
new IonicComponent(List, {
|
||||
propClasses: ['inset']
|
||||
})
|
||||
|
50
ionic/components/list/test/infinite/index.js
Normal file
50
ionic/components/list/test/infinite/index.js
Normal file
@ -0,0 +1,50 @@
|
||||
import {bootstrap, NgFor, ProtoViewRef, ViewContainerRef} from 'angular2/angular2'
|
||||
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
import {Parent} from 'angular2/src/core/annotations_impl/visibility';
|
||||
|
||||
import {Content, List, Item} from 'ionic/ionic';
|
||||
|
||||
|
||||
@Component({ selector: 'ion-app' })
|
||||
@View({
|
||||
templateUrl: 'main.html',
|
||||
directives: [Content, List, Item, ItemCellTemplate, NgFor]
|
||||
})
|
||||
class IonicApp {
|
||||
constructor() {
|
||||
console.log('IonicApp Start')
|
||||
|
||||
this.items = []
|
||||
for(let i = 0; i < 1000; i++) {
|
||||
this.items.push({
|
||||
title: 'Item ' + i
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Used to find and register headers in a view, and this directive's
|
||||
content will be moved up to the common navbar location, and created
|
||||
using the same context as the view's content area.
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'template[cell]'
|
||||
})
|
||||
export class ItemCellTemplate {
|
||||
constructor(@Parent() list: List, viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef) {
|
||||
console.log('Item cell template', list, viewContainer, protoViewRef);
|
||||
|
||||
this.protoViewRef = protoViewRef;
|
||||
this.viewContainer = viewContainer;
|
||||
|
||||
list.setItemTemplate(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function main() {
|
||||
bootstrap(IonicApp);
|
||||
}
|
15
ionic/components/list/test/infinite/main.html
Normal file
15
ionic/components/list/test/infinite/main.html
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
<ion-view nav-title="List">
|
||||
|
||||
<ion-content class="padding" #content>
|
||||
|
||||
<ion-list inset virtual [items]="items" [content]="content">
|
||||
<ion-item *cell>
|
||||
{{$item.title}}
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<f style="height: 15000px; width: 100%; background-color: green"></f>
|
||||
|
||||
</ion-content>
|
||||
|
||||
</ion-view>
|
109
ionic/components/list/virtual.js
Normal file
109
ionic/components/list/virtual.js
Normal file
@ -0,0 +1,109 @@
|
||||
|
||||
|
||||
export class ListVirtualScroll {
|
||||
constructor(list) {
|
||||
this.list = list;
|
||||
this.content = this.list.content;
|
||||
|
||||
this.viewportHeight = this.content.domElement.offsetHeight;
|
||||
|
||||
this.viewContainer = this.list.itemTemplate.viewContainer;
|
||||
|
||||
this.itemHeight = 60;
|
||||
|
||||
this.shownItems = {};
|
||||
this.enteringItems = [];
|
||||
this.leavingItems = [];
|
||||
|
||||
// Compute the initial sizes
|
||||
this.resize();
|
||||
|
||||
this.content.addScrollEventListener((event) => {
|
||||
this._handleVirtualScroll(event);
|
||||
});
|
||||
}
|
||||
|
||||
resize() {
|
||||
this.viewportHeight = this.content.domElement.offsetHeight;
|
||||
this.viewportScrollHeight = this.content.scrollElement.scrollHeight;
|
||||
|
||||
this.virtualHeight = this.list.items.length * this.itemHeight;
|
||||
this.itemsPerScreen = this.viewportHeight / this.itemHeight;
|
||||
|
||||
console.log('VIRTUAL: resize(viewportHeight:', this.viewportHeight,
|
||||
'viewportScrollHeight:', this.viewportScrollHeight, 'virtualHeight:', this.virtualHeight,
|
||||
', itemsPerScreen:', this.itemsPerScreen, ')');
|
||||
}
|
||||
|
||||
_handleVirtualScroll(event) {
|
||||
let item;
|
||||
let shownItemRef;
|
||||
|
||||
let st = event.target.scrollTop;
|
||||
let sh = event.target.scrollHeight;
|
||||
|
||||
let topIndex = Math.floor(st / this.itemHeight);
|
||||
let bottomIndex = Math.floor((st / this.itemHeight) + this.itemsPerScreen);
|
||||
|
||||
let items = this.list.items;
|
||||
|
||||
// Key iterate the shown items map
|
||||
// and compare the index to our index range,
|
||||
// pushing the items to remove to our leaving
|
||||
// list if they're ouside this range.
|
||||
for(let i in this.shownItems) {
|
||||
if(i < topIndex || i > bottomIndex) {
|
||||
this.leavingItems.push(this.shownItems[i]);
|
||||
delete this.shownItems[i];
|
||||
}
|
||||
}
|
||||
|
||||
let realIndex = 0;
|
||||
// Iterate the set of items that will be rendered, using the
|
||||
// index from the actual items list as the map for the
|
||||
// virtual items we draw
|
||||
for(let i = topIndex, realIndex = 0; i < bottomIndex && i < items.length; i++, realIndex++) {
|
||||
item = items[i];
|
||||
console.log('Drawing item', i);
|
||||
|
||||
shownItemRef = this.shownItems[i];
|
||||
|
||||
// Is this a new item?
|
||||
if(!shownItemRef) {
|
||||
let itemView = this.viewContainer.create(this.list.itemTemplate.protoViewRef, realIndex);
|
||||
itemView.setLocal('\$implicit', item);
|
||||
itemView.setLocal('\$item', item);
|
||||
shownItemRef = new VirtualItemRef(item, i, realIndex, itemView);
|
||||
|
||||
this.shownItems[i] = shownItemRef;
|
||||
this.enteringItems.push(shownItemRef);
|
||||
}
|
||||
|
||||
|
||||
//tuple.view = viewContainer.create(protoViewRef, tuple.record.currentIndex);
|
||||
|
||||
}
|
||||
|
||||
while(this.leavingItems.length) {
|
||||
let itemRef = this.leavingItems.pop();
|
||||
this.viewContainer.remove(itemRef.realIndex);
|
||||
}
|
||||
|
||||
console.log('VIRTUAL SCROLL: scroll(scrollTop:', st, 'topIndex:', topIndex, 'bottomIndex:', bottomIndex, ')');
|
||||
console.log('Container has', this.list.domElement.children.length, 'children');
|
||||
}
|
||||
|
||||
cellAtIndex(index) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class VirtualItemRef {
|
||||
constructor(item, index, realIndex, view) {
|
||||
this.item = item;
|
||||
this.index = index;
|
||||
this.realIndex = realIndex;
|
||||
this.view = view;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user