mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
perf(reorder): hit test refactored
This commit is contained in:
@@ -13,12 +13,15 @@ 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);
|
||||
@@ -26,9 +29,9 @@ export class ItemReorderGesture {
|
||||
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 {
|
||||
@@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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'];
|
||||
@@ -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;
|
||||
|
||||
@@ -24,7 +24,7 @@ ion-reorder {
|
||||
|
||||
.reorder-enabled {
|
||||
|
||||
ion-item {
|
||||
.item {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,14 @@ 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);
|
||||
@@ -41,8 +48,4 @@ ion-item.reorder-active {
|
||||
transition: none;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
ion-reorder {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +403,13 @@ export class Item {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getNativeElement() {
|
||||
return this._elementRef.nativeElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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 = '';
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user