diff options
| author | Andrej Mihajlov <and@codeispoetry.ru> | 2017-03-17 23:01:16 +0000 |
|---|---|---|
| committer | Andrej Mihajlov <and@codeispoetry.ru> | 2017-03-17 23:01:16 +0000 |
| commit | 3f1345fe42b44d1d9d0ee7ef9888020889d89b3e (patch) | |
| tree | c530aa9a560ec3d7cf6cba3b09f3a85553295101 | |
| parent | aae71ffac0d0656e550f3653a4393a18bb7cd4d1 (diff) | |
| download | mullvadvpn-3f1345fe42b44d1d9d0ee7ef9888020889d89b3e.tar.xz mullvadvpn-3f1345fe42b44d1d9d0ee7ef9888020889d89b3e.zip | |
New keyframe animation
| -rw-r--r-- | app/lib/keyframe-animation.js (renamed from app/lib/tray-animation.js) | 128 | ||||
| -rw-r--r-- | app/lib/tray-animator.js | 113 | ||||
| -rw-r--r-- | app/lib/tray-icon-manager.js | 85 | ||||
| -rw-r--r-- | app/lib/tray-icon-provider.js | 47 | ||||
| -rw-r--r-- | app/main.js | 3 | ||||
| -rw-r--r-- | test/keyframe-animation.spec.js | 227 | ||||
| -rw-r--r-- | test/tray-animator.spec.js | 125 |
7 files changed, 338 insertions, 390 deletions
diff --git a/app/lib/tray-animation.js b/app/lib/keyframe-animation.js index f415ecbe75..b8f4a206d4 100644 --- a/app/lib/tray-animation.js +++ b/app/lib/keyframe-animation.js @@ -2,18 +2,18 @@ import assert from 'assert'; import { nativeImage } from 'electron'; /** - * Tray animation descriptor + * Keyframe animation * * @export - * @class TrayAnimation + * @class KeyframeAnimation */ -export default class TrayAnimation { +export default class KeyframeAnimation { /** * Set callback called on each frame update * * @type {function} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ set onFrame(v) { this._onFrame = v; } @@ -22,7 +22,7 @@ export default class TrayAnimation { * * @readonly * @type {function} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get onFrame() { this._onFrame; } @@ -30,7 +30,7 @@ export default class TrayAnimation { * Set callback called when animation finished * * @type {function} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ set onFinish(v) { this._onFinish = v; } @@ -39,7 +39,7 @@ export default class TrayAnimation { * * @readonly * - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get onFinish() { this._onFinish; } @@ -47,7 +47,7 @@ export default class TrayAnimation { * Set animation pace per frame in ms * * @type {number} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ set speed(v) { this._speed = parseInt(v); } @@ -56,7 +56,7 @@ export default class TrayAnimation { * * @readonly * @type {number} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get speed() { return this._speed; } @@ -64,7 +64,7 @@ export default class TrayAnimation { * Set animation repetition * @type {bool} * - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ set repeat(v) { this._repeat = !!v; } @@ -73,14 +73,14 @@ export default class TrayAnimation { * * @readonly * @type {bool} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get repeat() { return this._repeat; } /** * Set animation reversal * @type {bool} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ set reverse(v) { this._reverse = !!v; } @@ -89,14 +89,14 @@ export default class TrayAnimation { * * @readonly * @type {bool} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get reverse() { return this._repeat; } /** * Set animation alternation * @type {bool} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ set alternate(v) { this._alternate = !!v; } @@ -105,7 +105,7 @@ export default class TrayAnimation { * * @readonly * @type {bool} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get alternate() { return this._alternate; } @@ -114,7 +114,7 @@ export default class TrayAnimation { * * @readonly * @type {array} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get source() { return this._source.slice(); } @@ -123,7 +123,7 @@ export default class TrayAnimation { * * @readonly * @type {Electron.NativeImage[]} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get nativeImages() { return this._nativeImages.slice(); } @@ -132,7 +132,7 @@ export default class TrayAnimation { * * @readonly * @type {bool} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get isFinished() { return this._isFinished; } @@ -143,8 +143,8 @@ export default class TrayAnimation { * @param {string} filePattern - file name pattern where {s} is replaced with index * @param {number[]} range - sequence range [start, end] * - * @memberOf TrayAnimation - * @return {TrayAnimation} + * @memberOf KeyframeAnimation + * @return {KeyframeAnimation} */ static fromFileSequence(filePattern, range) { assert(range.length === 2 && range[0] < range[1]); @@ -154,14 +154,14 @@ export default class TrayAnimation { images.push(filePattern.replace('{s}', i)); } - return new TrayAnimation(images); + return new KeyframeAnimation(images); } /** - * Creates an instance of TrayAnimation. + * Creates an instance of KeyframeAnimation. * @param {string[]} images * - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ constructor(images) { assert(images.length > 0); @@ -194,7 +194,7 @@ export default class TrayAnimation { * * @readonly * @type {Electron.NativeImage} - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ get currentImage() { return this._nativeImages[this._currentFrame]; @@ -202,19 +202,27 @@ export default class TrayAnimation { /** * Prepare initial state for animation before running it. - * @param {object} [options = {}] - animation options - * @param {number} [options.startFrame] - start frame - * @param {number} [options.endFrame] - end frame + * @param {object} [options = {}] - animation options + * @param {number} [options.startFrame] - start frame + * @param {number} [options.endFrame] - end frame * @param {bool} [options.beginFromCurrentState] - continue animation from current state - * @memberOf TrayAnimation + * @param {string} [options.advanceTo] - resets current frame. (possible values: end) + * @memberOf KeyframeAnimation */ play(options = {}) { - let {startFrame, endFrame, beginFromCurrentState} = options; + let { startFrame, endFrame, beginFromCurrentState, advanceTo } = options; - if(startFrame === undefined && endFrame === undefined) { - this._frameRange = [ 0, this._numFrames - 1 ]; + if(startFrame !== undefined && endFrame !== undefined) { + assert(startFrame >= 0 && startFrame < this._numFrames); + assert(endFrame >= 0 && endFrame < this._numFrames); + + if(startFrame < endFrame) { + this._frameRange = [ startFrame, endFrame ]; + } else { + this._frameRange = [ endFrame, startFrame ]; + } } else { - throw 'not implemented'; + this._frameRange = [ 0, this._numFrames - 1 ]; } if(!beginFromCurrentState || this._isFirstRun) { @@ -225,14 +233,22 @@ export default class TrayAnimation { this._isFirstRun = false; } + if(advanceTo === 'end') { + this._currentFrame = this._frameRange[this._reverse ? 0 : 1]; + } + this._isFinished = false; - this._render(); - this._unscheduleUpdate(); + + this._render(); this._scheduleUpdate(); } + /** + * Stop animation + * @memberOf KeyframeAnimation + */ stop() { this._unscheduleUpdate(); } @@ -271,18 +287,14 @@ export default class TrayAnimation { /** * Advance animation frame - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ _advanceFrame() { // do not advance frame when animation is finished if(this._isFinished) { return; } // advance frame - let nextFrame = this._nextFrame(this._currentFrame, this._reverse); - - // let animation pick up from current state - let didReachEnd = (nextFrame < this._frameRange[0] && this._reverse) || // out of bounds but moving into - (nextFrame > this._frameRange[1] && !this._reverse); // out of bounds but moving into + let didReachEnd = this._currentFrame === this._frameRange[this._reverse ? 0 : 1]; // did reach end? if(didReachEnd) { @@ -297,31 +309,41 @@ export default class TrayAnimation { // change animation direction if marked for alternation if(this._alternate) { this._reverse = !this._reverse; - - // clamp range - nextFrame = Math.min(Math.max(this._frameRange[0], nextFrame), this._frameRange[1]); - // skip corner frame when alternating by advancing frame once again - nextFrame = this._nextFrame(nextFrame, this._reverse); + this._currentFrame = this._nextFrame(this._currentFrame, this._frameRange, this._reverse); } else { - nextFrame = this._frameRange[this._reverse ? 1 : 0]; + this._currentFrame = this._frameRange[this._reverse ? 1 : 0]; } + } else { + this._currentFrame = this._nextFrame(this._currentFrame, this._frameRange, this._reverse); } - - this._currentFrame = nextFrame; } /** * Calculate next frame * @private - * @param {number} cur - current frame - * @param {bool} isReverse - reverse sequence direction? + * @param {number} cur - current frame + * @param {number[]} frameRange - frame range + * @param {bool} isReverse - reverse sequence direction? * @returns {number} * - * @memberOf TrayAnimation + * @memberOf KeyframeAnimation */ - _nextFrame(cur, isReverse) { - return cur + (isReverse ? -1 : 1); + _nextFrame(cur, frameRange, isReverse) { + if(isReverse) { + if(cur < frameRange[0]) { + return cur + 1; + } else if(cur > frameRange[0]) { + return cur - 1; + } + } else { + if(cur > frameRange[1]) { + return cur - 1; + } else if(cur < frameRange[1]) { + return cur + 1; + } + } + return cur; } }
\ No newline at end of file diff --git a/app/lib/tray-animator.js b/app/lib/tray-animator.js deleted file mode 100644 index 9fb03b5588..0000000000 --- a/app/lib/tray-animator.js +++ /dev/null @@ -1,113 +0,0 @@ -import assert from 'assert'; - -/** - * Tray icon animator - * @class TrayAnimator - */ -export default class TrayAnimator { - - /** - * Whether animator has started. - * @readonly - * @memberOf TrayAnimator - */ - get isStarted() { return this._started; } - - /** - * Creates an instance of TrayAnimator. - * @param {Electron.Tray} tray - an instance of Tray - * @param {TrayAnimation} animation - an instance of TrayAnimation - * - * @memberOf TrayAnimator - */ - constructor(tray, animation) { - assert(tray); - assert(animation); - - this._tray = tray; - this._animation = animation; - this._started = false; - this._timer = null; - } - - /** - * Advance animation to the start * - * @memberOf TrayAnimator - */ - advanceToStart() { - this._animation.advanceToStart(); - this._updateTrayIcon(); - } - - /** - * Advance animation to the end - * @memberOf TrayAnimator - */ - advanceToEnd() { - this._animation.advanceToEnd(); - this._updateTrayIcon(); - } - - /** - * Start animating - * @memberOf TrayAnimator - */ - start() { - if(this._started) { return; } - - this._timer = this._nextFrame(); - this._started = true; - - // prepare animation - this._animation.prepare(); - - // update from initial frame - this._updateTrayIcon(); - } - - /** - * Stop animating - * @memberOf TrayAnimator - */ - stop() { - if(!this._started) { return; } - - this._started = false; - - clearTimeout(this._timer); - this._timer = null; - } - - /** - * Schedules next animation frame - * @returns {number} timer ID - * @memberOf TrayAnimator - */ - _nextFrame() { - return setTimeout(::this._updateAnimationFrame, this._animation.speed); - } - - /** - * Updates animation frame - * @memberOf TrayAnimator - */ - _updateAnimationFrame() { - if(!this._started) { return; } - - this._animation.advanceFrame(); - this._updateTrayIcon(); - - if(!this._animation.isFinished) { - this._nextFrame(); - } - } - - /** - * Update tray icon with current frame - * @memberOf TrayAnimator - */ - _updateTrayIcon() { - this._tray.setImage(this._animation.currentImage); - } - -} diff --git a/app/lib/tray-icon-manager.js b/app/lib/tray-icon-manager.js index 987698a23d..82c226d3f6 100644 --- a/app/lib/tray-icon-manager.js +++ b/app/lib/tray-icon-manager.js @@ -1,7 +1,7 @@ import assert from 'assert'; -import TrayAnimator from './tray-animator'; -import TrayIconProvider from './tray-icon-provider'; +import path from 'path'; import { TrayIconType } from '../enums'; +import KeyframeAnimation from './keyframe-animation'; /** * Tray icon manager @@ -14,17 +14,19 @@ export default class TrayIconManager { /** * Creates an instance of TrayIconManager. * @param {Electron.Tray} tray - * @param {TrayIconProvider} iconProvider * * @memberOf TrayIconManager */ - constructor(tray, iconProvider) { + constructor(tray) { assert(tray); - assert(iconProvider); - this._tray = tray; - this._iconProvider = iconProvider; - this._animator = null; + const basePath = path.join(path.resolve(__dirname, '..'), 'assets/images/menubar icons'); + let filePath = path.join(basePath, 'lock-{s}.png'); + let animation = KeyframeAnimation.fromFileSequence(filePath, [1, 9]); + animation.onFrame = (img) => tray.setImage(img); + animation.speed = 100; + + this._animation = animation; this._iconType = null; } @@ -33,9 +35,9 @@ export default class TrayIconManager { * @memberOf TrayIconManager */ destroy() { - if(this._animator) { - this._animator.stop(); - this._animator = null; + if(this._animation) { + this._animation.stop(); + this._animation = null; } this._iconType = null; } @@ -67,64 +69,47 @@ export default class TrayIconManager { * * @memberOf TrayIconManager */ - _updateIconType(type) { + _updateIconType(type) { // no-op if same animator requested if(this._iconType === type) { return; } - // skip animation if: - // 1. there was no icon set before (which is usually when app starts) - // 2. unsecured -> securing - // 3. securing -> unsecured - const skip = this._iconType === null || - type === TrayIconType.securing || // unsecured -> securing - (type === TrayIconType.unsecured && this._iconType === TrayIconType.securing); // securing -> unsecured - - // do not animate if setting icon for the first time - this._updateType(type, skip); - } - - /** - * Get animation for iconType - * - * @param {TrayIconType} type - * @returns TrayIconAnimator - * - * @memberOf TrayIconManager - */ - _animationForType(type) { - switch(type) { - case TrayIconType.secured: return this._iconProvider.lockAnimation(); - case TrayIconType.unsecured: return this._iconProvider.unlockAnimation(); - case TrayIconType.securing: return this._iconProvider.unlockAnimation(); - } + this._updateType(type); } /** * Update icon animator with new type * * @param {TrayIconType} type - * @param {boolean} [skipAnimation=false] whether animation should be skipped * * @memberOf TrayIconManager */ - _updateType(type, skipAnimation = false) { + _updateType(type) { assert(TrayIconType.isValid(type)); - let animator = new TrayAnimator(this._tray, this._animationForType(type)); + let options = { beginFromCurrentState: true }; - // destroy existing animator - if(this._animator) { - this._animator.stop(); - this._animator = null; + switch(type) { + case TrayIconType.secured: + this._animation.reverse = false; + break; + case TrayIconType.securing: + case TrayIconType.unsecured: + this._animation.reverse = true; + break; } - if(skipAnimation) { - animator.advanceToEnd(); - } else { - animator.start(); + if(this._iconType === null) { + options.advanceTo = 'end'; } + + this._animation.play(options); + + // if(skipAnimation) { + // animator.advanceToEnd(); + // } else { + // animator.start(); + // } - this._animator = animator; this._iconType = type; } diff --git a/app/lib/tray-icon-provider.js b/app/lib/tray-icon-provider.js deleted file mode 100644 index aa50a71a71..0000000000 --- a/app/lib/tray-icon-provider.js +++ /dev/null @@ -1,47 +0,0 @@ -import path from 'path'; -import { EventEmitter } from 'events'; -import TrayAnimation from './tray-animation'; -import Enum from './enum'; - -const menubarIcons = { - base: path.join(path.resolve(__dirname, '..'), 'assets/images/menubar icons'), - lock: 'lock-{s}.png' -}; - -/** - * Tray icon provider - * - * @export - * @class TrayIconProvider - */ -export default class TrayIconProvider { - - /** - * Get lock animation - * - * @param {boolean} [isReverse=false] whether animation should be reversed - * @returns TrayIconAnimator - * - * @memberOf TrayIconProvider - */ - lockAnimation(isReverse = false) { - let filePath = path.join(menubarIcons.base, menubarIcons.lock); - let animation = TrayAnimation.fromFileSequence(filePath, [1, 9]); - animation.speed = 100; - animation.reverse = isReverse; - - return animation; - } - - /** - * Get unlock animation - * - * @returns TrayIconAnimator - * - * @memberOf TrayIconProvider - */ - unlockAnimation() { - return this.lockAnimation(true); - } - -}
\ No newline at end of file diff --git a/app/main.js b/app/main.js index 374ece212b..753ac685a9 100644 --- a/app/main.js +++ b/app/main.js @@ -1,7 +1,6 @@ import path from 'path'; import { app, crashReporter, BrowserWindow, ipcMain, Tray, Menu, nativeImage } from 'electron'; import TrayIconManager from './lib/tray-icon-manager'; -import TrayIconProvider from './lib/tray-icon-provider'; // Override appData path to avoid collisions with old client // New userData path, i.e on macOS: ~/Library/Application Support/mullvad.vpn @@ -160,7 +159,7 @@ const createTray = () => { tray.on('click', toggleWindow); tray.setHighlightMode('selection'); - trayIconManager = new TrayIconManager(tray, new TrayIconProvider()); + trayIconManager = new TrayIconManager(tray); }; crashReporter.start({ diff --git a/test/keyframe-animation.spec.js b/test/keyframe-animation.spec.js new file mode 100644 index 0000000000..83e74b72c6 --- /dev/null +++ b/test/keyframe-animation.spec.js @@ -0,0 +1,227 @@ +import { expect } from 'chai'; +import KeyframeAnimation from '../app/lib/keyframe-animation'; +import { nativeImage } from 'electron'; + +describe('lib/keyframe-animation', function() { + this.timeout(1000); + + let animation, seq; + + beforeEach(() => { + const images = [1, 2, 3, 4, 5].map(() => nativeImage.createEmpty()); + animation = new KeyframeAnimation(images); + animation.speed = 1; + + seq = []; + }); + + afterEach(() => { + animation.stop(); + }); + + it('should play sequence', (done) => { + animation.onFrame = () => seq.push(animation._currentFrame); + animation.onFinish = () => { + expect(seq).to.be.deep.equal([0, 1, 2, 3, 4]); + expect(animation._currentFrame).to.be.equal(4); + done(); + }; + + animation.play(); + }); + + it('should play one frame', (done) => { + animation.onFrame = () => seq.push(animation._currentFrame); + animation.onFinish = () => { + expect(seq).to.be.deep.equal([3]); + expect(animation._currentFrame).to.be.equal(3); + done(); + }; + + animation.play({ startFrame: 3, endFrame: 3 }); + }); + + it('should play sequence with custom frames', (done) => { + animation.onFrame = () => seq.push(animation._currentFrame); + animation.onFinish = () => { + expect(seq).to.be.deep.equal([2, 3, 4]); + expect(animation._currentFrame).to.be.equal(4); + done(); + }; + + animation.play({ + startFrame: 2, + endFrame: 4 + }); + }); + + it('should play sequence with custom frames in reverse', (done) => { + animation.onFrame = () => seq.push(animation._currentFrame); + animation.onFinish = () => { + expect(seq).to.be.deep.equal([4, 3, 2]); + expect(animation._currentFrame).to.be.equal(2); + done(); + }; + + animation.reverse = true; + animation.play({ + startFrame: 4, + endFrame: 2 + }); + }); + + it('should begin from current state starting below range', (done) => { + animation.onFrame = () => seq.push(animation._currentFrame); + animation.onFinish = () => { + expect(seq).to.be.deep.equal([0, 1, 2, 3, 4]); + expect(animation._currentFrame).to.be.equal(4); + done(); + }; + + animation._currentFrame = 0; + animation._isFirstRun = false; + + animation.play({ + beginFromCurrentState: true, + startFrame: 3, + endFrame: 4 + }); + }); + + it('should begin from current state starting below range reverse', (done) => { + animation.onFrame = () => seq.push(animation._currentFrame); + animation.onFinish = () => { + expect(seq).to.be.deep.equal([0, 1, 2, 3]); + expect(animation._currentFrame).to.be.equal(3); + done(); + }; + + animation._currentFrame = 0; + animation._isFirstRun = false; + animation.reverse = true; + + animation.play({ + beginFromCurrentState: true, + startFrame: 3, + endFrame: 4 + }); + }); + + it('should begin from current state starting above range', (done) => { + animation.onFrame = () => seq.push(animation._currentFrame); + animation.onFinish = () => { + expect(seq).to.be.deep.equal([4, 3, 2]); + expect(animation._currentFrame).to.be.equal(2); + done(); + }; + + animation._currentFrame = 4; + animation._isFirstRun = false; + + animation.play({ + beginFromCurrentState: true, + startFrame: 1, + endFrame: 2 + }); + }); + + it('should begin from current state starting above range reverse', (done) => { + animation.onFrame = () => seq.push(animation._currentFrame); + animation.onFinish = () => { + expect(seq).to.be.deep.equal([4, 3, 2, 1]); + expect(animation._currentFrame).to.be.equal(1); + done(); + }; + + animation._currentFrame = 4; + animation._isFirstRun = false; + animation.reverse = true; + + animation.play({ + beginFromCurrentState: true, + startFrame: 1, + endFrame: 3 + }); + }); + + it('should play sequence in reverse', (done) => { + animation.onFrame = () => seq.push(animation._currentFrame); + animation.onFinish = () => { + expect(seq).to.be.deep.equal([4, 3, 2, 1, 0]); + expect(animation._currentFrame).to.be.equal(0); + done(); + }; + + animation.reverse = true; + animation.play(); + }); + + it('should play sequence on repeat', (done) => { + const expectedFrames = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]; + + animation.onFrame = () => { + if(seq.length === expectedFrames.length) { + expect(seq).to.be.deep.equal(expectedFrames); + done(); + } else { + seq.push(animation._currentFrame); + } + }; + + animation.repeat = true; + animation.play(); + }); + + it('should play sequence on repeat in reverse', (done) => { + const expectedFrames = [4, 3, 2, 1, 0, 4, 3, 2, 1, 0]; + + animation.onFrame = () => { + if(seq.length === expectedFrames.length) { + expect(seq).to.be.deep.equal(expectedFrames); + done(); + } else { + seq.push(animation._currentFrame); + } + }; + + animation.repeat = true; + animation.reverse = true; + animation.play(); + }); + + it('should alternate sequence', (done) => { + const expectedFrames = [0, 1, 2, 3, 4, 3, 2, 1, 0]; + + animation.onFrame = () => { + if(seq.length === expectedFrames.length) { + expect(seq).to.be.deep.equal(expectedFrames); + done(); + } else { + seq.push(animation._currentFrame); + } + }; + + animation.repeat = true; + animation.alternate = true; + animation.play(); + }); + + it('should alternate reverse sequence', (done) => { + const expectedFrames = [4, 3, 2, 1, 0, 1, 2, 3, 4]; + + animation.onFrame = () => { + if(seq.length === expectedFrames.length) { + expect(seq).to.be.deep.equal(expectedFrames); + done(); + } else { + seq.push(animation._currentFrame); + } + }; + + animation.repeat = true; + animation.reverse = true; + animation.alternate = true; + animation.play(); + }); + +});
\ No newline at end of file diff --git a/test/tray-animator.spec.js b/test/tray-animator.spec.js deleted file mode 100644 index c07bc6b632..0000000000 --- a/test/tray-animator.spec.js +++ /dev/null @@ -1,125 +0,0 @@ -import { expect } from 'chai'; -import TrayAnimation from '../app/lib/tray-animation'; -import { nativeImage } from 'electron'; - -describe('lib/tray-animation', function() { - this.timeout(1000); - - let animation; - - beforeEach(() => { - const images = [1, 2, 3, 4, 5].map(() => nativeImage.createEmpty()); - animation = new TrayAnimation(images); - animation.speed = 1; - }); - - afterEach(() => { - animation.stop(); - }); - - it('should play sequence', (done) => { - let seq = []; - - animation.onFrame = () => { - seq.push(animation._currentFrame); - }; - - animation.onFinish = () => { - expect(seq).to.be.deep.equal([0, 1, 2, 3, 4]); - expect(animation._currentFrame).to.be.equal(4); - done(); - }; - - animation.play(); - }); - - it('should play sequence in reverse', (done) => { - let seq = []; - - animation.onFrame = () => { - seq.push(animation._currentFrame); - }; - - animation.onFinish = () => { - expect(seq).to.be.deep.equal([4, 3, 2, 1, 0]); - expect(animation._currentFrame).to.be.equal(0); - done(); - }; - - animation.reverse = true; - animation.play(); - }); - - it('should play sequence on repeat', (done) => { - const expectedFrames = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]; - let receivedFrames = []; - - animation.onFrame = () => { - if(receivedFrames.length === expectedFrames.length) { - expect(receivedFrames).to.be.deep.equal(expectedFrames); - done(); - } else { - receivedFrames.push(animation._currentFrame); - } - }; - - animation.repeat = true; - animation.play(); - }); - - it('should play sequence on repeat in reverse', (done) => { - const expectedFrames = [4, 3, 2, 1, 0, 4, 3, 2, 1, 0]; - let receivedFrames = []; - - animation.onFrame = () => { - if(receivedFrames.length === expectedFrames.length) { - expect(receivedFrames).to.be.deep.equal(expectedFrames); - done(); - } else { - receivedFrames.push(animation._currentFrame); - } - }; - - animation.repeat = true; - animation.reverse = true; - animation.play(); - }); - - it('should alternate sequence', (done) => { - const expectedFrames = [0, 1, 2, 3, 4, 3, 2, 1, 0]; - let receivedFrames = []; - - animation.onFrame = () => { - if(receivedFrames.length === expectedFrames.length) { - expect(receivedFrames).to.be.deep.equal(expectedFrames); - done(); - } else { - receivedFrames.push(animation._currentFrame); - } - }; - - animation.repeat = true; - animation.alternate = true; - animation.play(); - }); - - it('should alternate reverse sequence', (done) => { - const expectedFrames = [4, 3, 2, 1, 0, 1, 2, 3, 4]; - let receivedFrames = []; - - animation.onFrame = () => { - if(receivedFrames.length === expectedFrames.length) { - expect(receivedFrames).to.be.deep.equal(expectedFrames); - done(); - } else { - receivedFrames.push(animation._currentFrame); - } - }; - - animation.repeat = true; - animation.reverse = true; - animation.alternate = true; - animation.play(); - }); - -});
\ No newline at end of file |
