Skip to content

Commit

Permalink
perf(reorder): hit test refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat committed Jun 23, 2016
1 parent 99c50a1 commit 6a52a4a
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 57 deletions.
107 changes: 57 additions & 50 deletions src/components/item/item-reorder-gesture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,25 @@ const ITEM_REORDER_ACTIVE = 'reorder-active';
*/
export class ItemReorderGesture {
private selectedItem: Item = null;
private selectedItemEle: HTMLElement = null;
private selectedItemHeight: number;

private offset: Coordinates;
private lastToIndex: number;
private lastYcoord: number;
private lastScrollPosition: number;
private emptyZone: boolean;

private itemHeight: number;
private windowHeight: number;

private events: UIEventManager = new UIEventManager(false);

constructor(public list: List) {
let element = this.list.getNativeElement();
this.events.pointerEvents(element,
(ev: any) => this.onDragStart(ev),
(ev: any) => this.onDragMove(ev),
(ev: any) => this.onDragEnd(ev));
this.onDragStart.bind(this),
this.onDragMove.bind(this),
this.onDragEnd.bind(this));
}

private onDragStart(ev: any): boolean {
Expand All @@ -37,67 +40,69 @@ export class ItemReorderGesture {
return false;
}

let item = itemEle['$ionComponent'];
let item = <Item> itemEle['$ionComponent'];
if (!item) {
console.error('item does not contain ion component');
return false;
}
ev.preventDefault();

// Preparing state
this.offset = pointerCoord(ev);
this.offset.y += this.list.scrollContent(0);
// Preparing state
this.selectedItem = item;
this.itemHeight = item.height();
this.selectedItemEle = item.getNativeElement();
this.selectedItemHeight = item.height();
this.lastToIndex = item.index;
this.lastYcoord = -100;

this.windowHeight = window.innerHeight - AUTO_SCROLL_MARGIN;
this.lastScrollPosition = this.list.scrollContent(0);

this.offset = pointerCoord(ev);
this.offset.y += this.lastScrollPosition;

item.setCssClass(ITEM_REORDER_ACTIVE, true);
this.list.reorderStart();
return true;
}

private onDragMove(ev: any) {
if (!this.selectedItem) {
let selectedItem = this.selectedItemEle;
if (!selectedItem) {
return;
}
ev.preventDefault();

// Get coordinate
var coord = pointerCoord(ev);
let coord = pointerCoord(ev);
let posY = coord.y;

// Scroll if we reach the scroll margins
let scrollPosition = this.scroll(coord);

// Update selected item position
let ydiff = Math.round(coord.y - this.offset.y + scrollPosition);
this.selectedItem.setCssStyle(CSS.transform, `translateY(${ydiff}px)`);

// Only perform hit test if we moved at least 30px from previous position
if (Math.abs(coord.y - this.lastYcoord) < 30) {
return;
let scrollPosition = this.scroll(posY);

// Only perform hit test if we moved at least 30px from previous position
if (Math.abs(posY - this.lastYcoord) > 30) {
let overItem = this.itemForCoord(coord);
if (overItem) {
let toIndex = overItem.index;
if (toIndex !== this.lastToIndex || this.emptyZone) {
let fromIndex = this.selectedItem.index;
this.lastToIndex = toIndex;
this.lastYcoord = posY;
this.emptyZone = false;
this.list.reorderMove(fromIndex, toIndex, this.selectedItemHeight);
}
} else {
this.emptyZone = true;
}
}

// Hit test
let overItem = this.itemForCoord(coord);
if (!overItem) {
this.emptyZone = true;
return;
}

// Move surrounding items if needed
let toIndex = overItem.index;
if (toIndex !== this.lastToIndex || this.emptyZone) {
let fromIndex = this.selectedItem.index;
this.lastToIndex = overItem.index;
this.lastYcoord = coord.y;
this.emptyZone = false;
nativeRaf(() => {
this.list.reorderMove(fromIndex, toIndex, this.itemHeight);
});
}
// Update selected item position
let ydiff = Math.round(posY - this.offset.y + scrollPosition);
selectedItem.style[CSS.transform] = `translateY(${ydiff}px)`;
}

private onDragEnd(ev: any) {
if (!this.selectedItem) {
private onDragEnd() {
if (!this.selectedItemEle) {
return;
}

Expand All @@ -106,17 +111,17 @@ export class ItemReorderGesture {
let fromIndex = this.selectedItem.index;
this.selectedItem.setCssClass(ITEM_REORDER_ACTIVE, false);
this.selectedItem = null;
this.selectedItemEle = null;
this.list.reorderEmit(fromIndex, toIndex);
});
}

private itemForCoord(coord: Coordinates): Item {
let element = <any>document.elementFromPoint(this.offset.x - 100, coord.y);
let element = <HTMLElement>document.elementFromPoint(this.offset.x - 100, coord.y);
if (!element) {
return null;
}
element = closest(element, 'ion-item', true);
if (!element) {
if (element.nodeName !== 'ION-ITEM') {
return null;
}
let item = <Item>(<any>element)['$ionComponent'];
Expand All @@ -127,20 +132,22 @@ export class ItemReorderGesture {
return item;
}

private scroll(coord: Coordinates): number {
let scrollDiff = 0;
if (coord.y < AUTO_SCROLL_MARGIN) {
scrollDiff = -SCROLL_JUMP;
} else if (coord.y > this.windowHeight) {
scrollDiff = SCROLL_JUMP;
private scroll(posY: number): number {
if (posY < AUTO_SCROLL_MARGIN) {
this.lastScrollPosition = this.list.scrollContent(-SCROLL_JUMP);
} else if (posY > this.windowHeight) {
this.lastScrollPosition = this.list.scrollContent(SCROLL_JUMP);
}
return this.list.scrollContent(scrollDiff);
return this.lastScrollPosition;
}



/**
* @private
*/
destroy() {
this.onDragEnd();
this.events.unlistenAll();
this.events = null;
this.list = null;
Expand Down
15 changes: 9 additions & 6 deletions src/components/item/item-reorder.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ion-reorder {

.reorder-enabled {

ion-item {
.item {
will-change: transform;
}

Expand All @@ -33,16 +33,19 @@ ion-reorder {
}
}

ion-item.reorder-active {
.reorder-active {
.item-inner {
pointer-events: none;
}
}


.item.reorder-active {
z-index: 4;

box-shadow: 0 0 10px rgba(0, 0, 0, .5);
opacity: .8;
transition: none;

pointer-events: none;

ion-reorder {
pointer-events: none;
}
}
7 changes: 7 additions & 0 deletions src/components/item/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,13 @@ export class Item {
});
}

/**
* @private
*/
getNativeElement() {
return this._elementRef.nativeElement;
}

/**
* @private
*/
Expand Down
17 changes: 16 additions & 1 deletion src/components/list/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class List extends Ion {

@Output() ionItemReorder: EventEmitter<{ from: number, to: number }> = new EventEmitter<{ from: number, to: number }>();

constructor(elementRef: ElementRef, private _zone: NgZone, @Optional() private _content: Content) {
constructor(elementRef: ElementRef, private _rendered: Renderer, private _zone: NgZone, @Optional() private _content: Content) {
super(elementRef);
}

Expand Down Expand Up @@ -107,6 +107,14 @@ export class List extends Ion {
this._slidingGesture && this._slidingGesture.closeOpened();
}

setCssClass(classname: string, add: boolean) {
this._rendered.setElementClass(this.getNativeElement(), classname, add);
}

reorderStart() {
this.setCssClass('reorder-active', true);
}

/**
* @private
*/
Expand Down Expand Up @@ -139,6 +147,7 @@ export class List extends Ion {
reorderReset() {
let children = this.elementRef.nativeElement.children;
let len = children.length;
this.setCssClass('reorder-active', false);
for (let i = 0; i < len; i++) {
children[i].style.transform = '';
}
Expand All @@ -155,7 +164,13 @@ export class List extends Ion {
let lastToIndex = this._lastToIndex;
this._lastToIndex = toIndex;

// TODO: I think both loops can be merged into a single one
// but I had no luck last time I tried

/********* DOM READ ********** */
let children = this.elementRef.nativeElement.children;

/********* DOM WRITE ********* */
if (toIndex >= lastToIndex) {
for (var i = lastToIndex; i <= toIndex; i++) {
if (i !== fromIndex) {
Expand Down

0 comments on commit 6a52a4a

Please sign in to comment.