Skip to content

Commit 1c402d5

Browse files
committed
Merge branch 'z-brooks-stagger-fix'
2 parents 8d42434 + 80ce3d1 commit 1c402d5

File tree

5 files changed

+206
-277
lines changed

5 files changed

+206
-277
lines changed

build/tasks/lint.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ var paths = require('../paths');
33
var eslint = require('gulp-eslint');
44

55
gulp.task('lint', function() {
6-
return gulp.src(paths.source)
6+
return gulp.src([paths.source, paths.unitTests])
77
.pipe(eslint())
88
.pipe(eslint.format())
99
.pipe(eslint.failOnError());

src/animator.js

Lines changed: 49 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -230,15 +230,19 @@ export class CssAnimator {
230230
}
231231

232232
/**
233-
* Execute an 'enter' animation on an element
234-
* @param element Element to animate
235-
* @returns Resolved when the animation is done
233+
* Animates element on enter or leave
234+
* @param element element to animate
235+
* @param direction 'enter' or 'leave'
236+
* @param doneClass class to apply when done
237+
* @private
236238
*/
237-
enter(element: HTMLElement): Promise<boolean> {
239+
_stateAnim(element: HTMLElement, direction: string, doneClass: string) {
240+
const auClass = 'au-' + direction;
241+
const auClassActive = auClass + '-active';
238242
return new Promise((resolve, reject) => {
239-
let classList = element.classList;
243+
const classList = element.classList;
240244

241-
this._triggerDOMEvent(animationEvent.enterBegin, element);
245+
this._triggerDOMEvent(animationEvent[direction + 'Begin'], element);
242246

243247
// Step 1.2: remove done classes
244248
if (this.useAnimationDoneClasses) {
@@ -247,8 +251,8 @@ export class CssAnimator {
247251
}
248252

249253
// Step 2: Add animation preparation class
250-
classList.add('au-enter');
251-
let prevAnimationNames = this._getElementAnimationNames(element);
254+
classList.add(auClass);
255+
const prevAnimationNames = this._getElementAnimationNames(element);
252256

253257
// Step 3: setup event to check whether animations started
254258
let animStart;
@@ -257,7 +261,7 @@ export class CssAnimator {
257261
animHasStarted = true;
258262
this.isAnimating = true;
259263

260-
this._triggerDOMEvent(animationEvent.enterActive, element);
264+
this._triggerDOMEvent(animationEvent[direction + 'Active'], element);
261265

262266
// Stop event propagation, bubbling will otherwise prevent parent animation
263267
evAnimStart.stopPropagation();
@@ -268,180 +272,92 @@ export class CssAnimator {
268272
// Step 3.1: Wait for animation to finish
269273
let animEnd;
270274
this._addMultipleEventListener(element, 'webkitAnimationEnd animationend', animEnd = (evAnimEnd) => {
271-
if (! animHasStarted) {
275+
if (!animHasStarted) {
272276
return;
273277
}
274278

275279
// Step 3.1.0: Stop event propagation, bubbling will otherwise prevent parent animation
276280
evAnimEnd.stopPropagation();
277281

278282
// Step 3.1.1: remove animation classes
279-
classList.remove('au-enter-active');
280-
classList.remove('au-enter');
283+
classList.remove(auClassActive);
284+
classList.remove(auClass);
281285

282286
// Step 3.1.2 remove animationend listener
283287
evAnimEnd.target.removeEventListener(evAnimEnd.type, animEnd);
284288

285-
// Step 3.1.3 in case animation done animations are active, add the defined entered class to the element
289+
// Step 3.1.3 in case animation done animations are active, add the defined done class to the element
286290
if (this.useAnimationDoneClasses &&
287-
this.animationEnteredClass !== undefined &&
288-
this.animationEnteredClass !== null) {
289-
classList.add(this.animationEnteredClass);
291+
doneClass !== undefined &&
292+
doneClass !== null) {
293+
classList.add(doneClass);
290294
}
291295

292296
this.isAnimating = false;
293-
this._triggerDOMEvent(animationEvent.enterDone, element);
297+
this._triggerDOMEvent(animationEvent[direction + 'Done'], element);
294298

295299
resolve(true);
296300
}, false);
297301

298302
// Step 4: check if parent element is defined to stagger animations otherwise trigger active immediately
299-
let parent = element.parentElement;
303+
const parent = element.parentElement;
300304
let delay = 0;
305+
const attrib = 'data-animator-pending' + direction;
301306

302-
let cleanupAnimation = () => {
307+
const cleanupAnimation = () => {
303308
// Step 5: if no animations scheduled cleanup animation classes
304-
let animationNames = this._getElementAnimationNames(element);
309+
const animationNames = this._getElementAnimationNames(element);
305310
if (! this._animationChangeWithValidKeyframe(animationNames, prevAnimationNames)) {
306-
classList.remove('au-enter-active');
307-
classList.remove('au-enter');
311+
classList.remove(auClassActive);
312+
classList.remove(auClass);
308313

309314
this._removeMultipleEventListener(element, 'webkitAnimationEnd animationend', animEnd);
310315
this._removeMultipleEventListener(element, 'webkitAnimationStart animationstart', animStart);
311316

312-
this._triggerDOMEvent(animationEvent.enterTimeout, element);
317+
this._triggerDOMEvent(animationEvent[direction + 'Timeout'], element);
313318
resolve(false);
314319
}
320+
parent && parent.setAttribute(attrib, +(parent.getAttribute(attrib) || 1) - 1);
315321
};
316322

317323
if (parent !== null &&
318-
parent !== undefined &&
319-
(
320-
parent.classList.contains('au-stagger') ||
321-
parent.classList.contains('au-stagger-enter')
322-
)) {
323-
let elemPos = Array.prototype.indexOf.call(parent.children, element);
324-
delay = this._getElementAnimationDelay(parent) * elemPos;
325-
324+
parent !== undefined &&
325+
(
326+
parent.classList.contains('au-stagger') ||
327+
parent.classList.contains('au-stagger-enter')
328+
)) {
329+
const offset = +(parent.getAttribute(attrib) || 0);
330+
parent.setAttribute(attrib, offset + 1);
331+
delay = this._getElementAnimationDelay(parent) * offset;
326332
this._triggerDOMEvent(animationEvent.staggerNext, element);
327333

328334
setTimeout(() => {
329-
classList.add('au-enter-active');
335+
classList.add(auClassActive);
330336
cleanupAnimation();
331337
}, delay);
332338
} else {
333-
classList.add('au-enter-active');
339+
classList.add(auClassActive);
334340
cleanupAnimation();
335341
}
336342
});
337343
}
338344

345+
/**
346+
* Execute an 'enter' animation on an element
347+
* @param element Element to animate
348+
* @returns Resolved when the animation is done
349+
*/
350+
enter(element: HTMLElement): Promise<boolean> {
351+
return this._stateAnim(element, 'enter', this.animationEnteredClass);
352+
}
353+
339354
/**
340355
* Execute a 'leave' animation on an element
341356
* @param element Element to animate
342357
* @returns Resolved when the animation is done
343358
*/
344359
leave(element: HTMLElement): Promise<boolean> {
345-
return new Promise((resolve, reject) => {
346-
let classList = element.classList;
347-
348-
this._triggerDOMEvent(animationEvent.leaveBegin, element);
349-
350-
// Step 1.1: remove done classes
351-
if (this.useAnimationDoneClasses) {
352-
classList.remove(this.animationEnteredClass);
353-
classList.remove(this.animationLeftClass);
354-
}
355-
356-
// Step 2: Add animation preparation class
357-
classList.add('au-leave');
358-
let prevAnimationNames = this._getElementAnimationNames(element);
359-
360-
// Step 3: setup event to check whether animations started
361-
let animStart;
362-
let animHasStarted = false;
363-
this._addMultipleEventListener(element, 'webkitAnimationStart animationstart', animStart = (evAnimStart) => {
364-
animHasStarted = true;
365-
this.isAnimating = true;
366-
367-
this._triggerDOMEvent(animationEvent.leaveActive, element);
368-
369-
// Stop event propagation, bubbling will otherwise prevent parent animation
370-
evAnimStart.stopPropagation();
371-
372-
evAnimStart.target.removeEventListener(evAnimStart.type, animStart);
373-
}, false);
374-
375-
// Step 3.1: Wait for animation to finish
376-
let animEnd;
377-
this._addMultipleEventListener(element, 'webkitAnimationEnd animationend', animEnd = (evAnimEnd) => {
378-
if (! animHasStarted) {
379-
return;
380-
}
381-
382-
// Step 3.1.0: Stop event propagation, bubbling will otherwise prevent parent animation
383-
evAnimEnd.stopPropagation();
384-
385-
// Step 3.1.1: remove animation classes
386-
classList.remove('au-leave-active');
387-
classList.remove('au-leave');
388-
389-
// Step 3.1.2 remove animationend listener
390-
evAnimEnd.target.removeEventListener(evAnimEnd.type, animEnd);
391-
392-
// Step 3.1.3 in case animation done animations are active, add the defined left class to the element
393-
if (this.useAnimationDoneClasses &&
394-
this.animationLeftClass !== undefined &&
395-
this.animationLeftClass !== null) {
396-
classList.add(this.animationLeftClass);
397-
}
398-
399-
this.isAnimating = false;
400-
this._triggerDOMEvent(animationEvent.leaveDone, element);
401-
402-
resolve(true);
403-
}, false);
404-
405-
406-
// Step 4: check if parent element is defined to stagger animations otherwise trigger leave immediately
407-
let parent = element.parentElement;
408-
let delay = 0;
409-
410-
let cleanupAnimation = () => {
411-
// Step 5: if no animations scheduled cleanup animation classes
412-
let animationNames = this._getElementAnimationNames(element);
413-
if (! this._animationChangeWithValidKeyframe(animationNames, prevAnimationNames)) {
414-
classList.remove('au-leave-active');
415-
classList.remove('au-leave');
416-
417-
this._removeMultipleEventListener(element, 'webkitAnimationEnd animationend', animEnd);
418-
this._removeMultipleEventListener(element, 'webkitAnimationStart animationstart', animStart);
419-
420-
this._triggerDOMEvent(animationEvent.leaveTimeout, element);
421-
resolve(false);
422-
}
423-
};
424-
425-
if (parent !== null &&
426-
parent !== undefined &&
427-
(
428-
parent.classList.contains('au-stagger') ||
429-
parent.classList.contains('au-stagger-leave')
430-
)) {
431-
let elemPos = Array.prototype.indexOf.call(parent.children, element);
432-
delay = this._getElementAnimationDelay(parent) * elemPos;
433-
434-
this._triggerDOMEvent(animationEvent.staggerNext, element);
435-
436-
setTimeout(() => {
437-
classList.add('au-leave-active');
438-
cleanupAnimation();
439-
}, delay);
440-
} else {
441-
classList.add('au-leave-active');
442-
cleanupAnimation();
443-
}
444-
});
360+
return this._stateAnim(element, 'leave', this.animationLeftClass);
445361
}
446362

447363
/**

test/animate.spec.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import {initialize} from 'aurelia-pal-browser';
55
jasmine.getFixtures().fixturesPath = 'base/test/fixtures/';
66

77
describe('animator-css', () => {
8-
var sut;
8+
let sut;
99
beforeAll(() => initialize());
1010
beforeEach(() => {
1111
sut = new CssAnimator();
1212
});
1313
describe('animate function', () => {
14-
var elem,
15-
testClass;
14+
let elem;
15+
let testClass;
1616

1717
describe('with valid keyframes', () => {
1818
beforeEach(() => {
@@ -30,7 +30,7 @@ describe('animator-css', () => {
3030
});
3131

3232
it('should animate multiple elements', (done) => {
33-
var elements = $('.sequenced-items li');
33+
let elements = $('.sequenced-items li');
3434

3535
sut.animate([elements.eq(0)[0], elements.eq(1)[0], elements.eq(2)[0]], testClass).then(() => {
3636
expect(sut.isAnimating).toBe(false);
@@ -42,9 +42,9 @@ describe('animator-css', () => {
4242
});
4343

4444
it('should not fire add/remove events', (done) => {
45-
var eventCalled = false
46-
, listenerAdd = document.addEventListener(animationEvent.addClassBegin, () => eventCalled = true)
47-
, listenerRemove = document.addEventListener(animationEvent.removeClassBegin, () => eventCalled = true);
45+
let eventCalled = false;
46+
const listenerAdd = document.addEventListener(animationEvent.addClassBegin, () => eventCalled = true);
47+
const listenerRemove = document.addEventListener(animationEvent.removeClassBegin, () => eventCalled = true);
4848

4949
sut.animate(elem, testClass).then(() => {
5050
expect(eventCalled).toBe(false);
@@ -54,7 +54,7 @@ describe('animator-css', () => {
5454
done();
5555
});
5656
});
57-
})
57+
});
5858

5959
// missing keyframes currently break the promise animator
6060
describe('without valid keyframes', () => {
@@ -71,6 +71,6 @@ describe('animator-css', () => {
7171
done();
7272
});
7373
});
74-
})
74+
});
7575
});
7676
});

0 commit comments

Comments
 (0)