Skip to content

Commit c44f6b6

Browse files
committed
perf(animation): improves _progress() hot function
- progress() is the function where more time is spent during any swipe gesture - replace iterating over the _fx properties, using an array instead - optimize pointerCoord(), profiler showed it’s one of the most called functions
1 parent 70f8a8e commit c44f6b6

File tree

5 files changed

+93
-76
lines changed

5 files changed

+93
-76
lines changed

src/animations/animation.ts

Lines changed: 80 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class Animation {
1010
private _cL: number;
1111
private _e: HTMLElement[];
1212
private _eL: number;
13-
private _fx: {[key: string]: EffectProperty};
13+
private _fx: EffectProperty[];
1414
private _dur: number = null;
1515
private _es: string = null;
1616
private _bfSty: { [property: string]: any; };
@@ -159,26 +159,39 @@ export class Animation {
159159
* @private
160160
* NO DOM
161161
*/
162+
163+
private _getProp(name: string): EffectProperty {
164+
if (this._fx) {
165+
return this._fx.find((prop) => prop.name === name);
166+
} else {
167+
this._fx = [];
168+
}
169+
return null;
170+
}
171+
162172
private _addProp(state: string, prop: string, val: any): EffectProperty {
163-
this._fx = this._fx || {};
164-
let fxProp = this._fx[prop];
173+
let fxProp = this._getProp(prop);
165174

166175
if (!fxProp) {
167176
// first time we've see this EffectProperty
168-
fxProp = this._fx[prop] = {
169-
trans: (TRANSFORMS[prop] === 1)
170-
};
177+
var shouldTrans = (TRANSFORMS[prop] === 1);
178+
fxProp = {
179+
name: prop,
180+
trans: shouldTrans,
171181

172-
// add the will-change property for transforms or opacity
173-
fxProp.wc = (fxProp.trans ? CSS.transform : prop);
182+
// add the will-change property for transforms or opacity
183+
wc: (shouldTrans ? CSS.transform : prop)
184+
};
185+
this._fx.push(fxProp);
174186
}
175187

176188
// add from/to EffectState to the EffectProperty
177-
let fxState: EffectState = (<any>fxProp)[state] = {
189+
let fxState: EffectState = {
178190
val: val,
179191
num: null,
180192
unit: '',
181193
};
194+
fxProp[state] = fxState;
182195

183196
if (typeof val === 'string' && val.indexOf(' ') < 0) {
184197
let r = val.match(CSS_VALUE_REGEX);
@@ -594,72 +607,72 @@ export class Animation {
594607
*/
595608
_progress(stepValue: number) {
596609
// bread 'n butter
597-
var val: any;
610+
let val: any;
611+
let effects = this._fx;
612+
let nuElements = this._eL;
598613

599-
if (this._fx && this._eL) {
600-
// flip the number if we're going in reverse
601-
if (this._rv) {
602-
stepValue = ((stepValue * -1) + 1);
603-
}
604-
var transforms: string[] = [];
605-
var effects = this._fx;
606-
var elements = this._e;
607-
for (var prop in effects) {
608-
var fx = effects[prop];
609-
610-
if (fx.from && fx.to) {
611-
var fromNum = fx.from.num;
612-
var toNum = fx.to.num;
613-
var tweenEffect = (fromNum !== toNum);
614-
if (tweenEffect) {
615-
this._twn = true;
616-
}
614+
if (!effects || !nuElements) {
615+
return;
616+
}
617617

618-
if (stepValue === 0) {
619-
// FROM
620-
val = fx.from.val;
618+
// flip the number if we're going in reverse
619+
if (this._rv) {
620+
stepValue = ((stepValue * -1) + 1);
621+
}
622+
var i, j;
623+
var finalTransform: string = '';
624+
var elements = this._e;
625+
for (i = 0; i < effects.length; i++) {
626+
var fx = effects[i];
627+
628+
if (fx.from && fx.to) {
629+
var fromNum = fx.from.num;
630+
var toNum = fx.to.num;
631+
var tweenEffect = (fromNum !== toNum);
632+
if (tweenEffect) {
633+
this._twn = true;
634+
}
621635

622-
} else if (stepValue === 1) {
623-
// TO
624-
val = fx.to.val;
636+
if (stepValue === 0) {
637+
// FROM
638+
val = fx.from.val;
625639

626-
} else if (tweenEffect) {
627-
// EVERYTHING IN BETWEEN
628-
val = (((toNum - fromNum) * stepValue) + fromNum) + fx.to.unit;
640+
} else if (stepValue === 1) {
641+
// TO
642+
val = fx.to.val;
629643

630-
} else {
631-
val = null;
632-
}
644+
} else if (tweenEffect) {
645+
// EVERYTHING IN BETWEEN
646+
val = (((toNum - fromNum) * stepValue) + fromNum) + fx.to.unit;
647+
}
633648

634-
if (val !== null) {
635-
if (fx.trans) {
636-
transforms.push(prop + '(' + val + ')');
649+
if (val !== null) {
650+
var prop = fx.name;
651+
if (fx.trans) {
652+
finalTransform += prop + '(' + val + ') ';
637653

638-
} else {
639-
for (var i = 0; i < this._eL; i++) {
640-
// ******** DOM WRITE ****************
641-
elements[i].style[prop] = val;
642-
}
654+
} else {
655+
for (j = 0; j < nuElements; j++) {
656+
// ******** DOM WRITE ****************
657+
elements[j].style[prop] = val;
643658
}
644659
}
645660
}
646661
}
662+
}
647663

648-
// place all transforms on the same property
649-
if (transforms.length) {
650-
if (!this._rv && stepValue !== 1 || this._rv && stepValue !== 0) {
651-
transforms.push('translateZ(0px)');
652-
}
664+
// place all transforms on the same property
665+
if (finalTransform.length) {
666+
if (!this._rv && stepValue !== 1 || this._rv && stepValue !== 0) {
667+
finalTransform += 'translateZ(0px)';
668+
}
653669

654-
var transformString = transforms.join(' ');
655-
var cssTransform = CSS.transform;
656-
for (var i = 0; i < this._eL; i++) {
657-
// ******** DOM WRITE ****************
658-
elements[i].style[cssTransform] = transformString;
659-
}
670+
var cssTransform = CSS.transform;
671+
for (i = 0; i < elements.length; i++) {
672+
// ******** DOM WRITE ****************
673+
elements[i].style[cssTransform] = finalTransform;
660674
}
661675
}
662-
663676
}
664677

665678
/**
@@ -900,15 +913,16 @@ export class Animation {
900913
*/
901914
_willChg(addWillChange: boolean) {
902915
let wc: string[];
903-
904-
if (addWillChange) {
916+
var effects = this._fx;
917+
if (addWillChange && effects) {
905918
wc = [];
906-
for (var prop in this._fx) {
907-
if (this._fx[prop].wc === 'webkitTransform') {
919+
for (var i = 0; i < effects.length; i++) {
920+
var propWC = effects[i].wc;
921+
if (propWC === 'webkitTransform') {
908922
wc.push('transform', '-webkit-transform');
909923

910924
} else {
911-
wc.push(this._fx[prop].wc);
925+
wc.push(propWC);
912926
}
913927
}
914928
}
@@ -1164,6 +1178,7 @@ export interface PlayOptions {
11641178
}
11651179

11661180
export interface EffectProperty {
1181+
name: string;
11671182
trans: boolean;
11681183
wc?: string;
11691184
to?: EffectState;

src/components/menu/menu.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ ion-menu ion-backdrop {
5151
z-index: -1;
5252
display: none;
5353

54-
opacity: .1;
54+
opacity: .01;
5555
}
5656

5757
.menu-content {

src/components/refresher/test/refresher.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ describe('Refresher', () => {
232232
function touchEv(y: number) {
233233
return {
234234
type: 'mockTouch',
235-
touches: [{clientY: y}],
235+
pageX: 0,
236+
pageY: y,
236237
preventDefault: function(){}
237238
};
238239
}

src/gestures/drag-gesture.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export class PanGesture {
117117
let coord = pointerCoord(ev);
118118
if (this.detector.detect(coord)) {
119119

120-
if (this.detector.pan() !== 0 && this.canCapture(ev) &&
120+
if (this.detector.pan() !== 0 &&
121121
(!this.gestute || this.gestute.capture())) {
122122
this.onDragStart(ev);
123123
this.captured = true;
@@ -156,7 +156,6 @@ export class PanGesture {
156156

157157
// Implemented in a subclass
158158
canStart(ev: any): boolean { return true; }
159-
canCapture(ev: any): boolean { return true; }
160159
onDragStart(ev: any) { }
161160
onDragMove(ev: any) { }
162161
onDragEnd(ev: any) { }

src/util/dom.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -195,16 +195,18 @@ export function windowLoad(callback?: Function) {
195195
export function pointerCoord(ev: any): PointerCoordinates {
196196
// get coordinates for either a mouse click
197197
// or a touch depending on the given event
198-
let c = { x: 0, y: 0 };
199198
if (ev) {
200-
const touches = ev.touches && ev.touches.length ? ev.touches : [ev];
201-
const e = (ev.changedTouches && ev.changedTouches[0]) || touches[0];
202-
if (e) {
203-
c.x = e.clientX || e.pageX || 0;
204-
c.y = e.clientY || e.pageY || 0;
199+
var changedTouches = ev.changedTouches;
200+
if (changedTouches && changedTouches.length > 0) {
201+
var touch = changedTouches[0];
202+
return { x: touch.clientX, y: touch.clientY };
203+
}
204+
var pageX = ev.pageX;
205+
if (pageX !== undefined) {
206+
return { x: pageX, y: ev.pageY };
205207
}
206208
}
207-
return c;
209+
return { x: 0, y: 0 };
208210
}
209211

210212
export function hasPointerMoved(threshold: number, startCoord: PointerCoordinates, endCoord: PointerCoordinates) {

0 commit comments

Comments
 (0)