From 7ea4e6c1cc49fdbd25f1309f168c014fedc24419 Mon Sep 17 00:00:00 2001 From: KJ Tsanaktsidis Date: Tue, 24 Feb 2015 14:55:58 +1100 Subject: [PATCH 1/2] Fixed race condition in the iText cursor flas animation The animation that flashes the iText cursor can race its own cancellation, meaning that you can have two _tick()'s and two _tickComplete() animations at the same time. On iOS in particular, most likely due to the lower frequency of timeouts, this can trap the cursor in a state where its opacity is always <0.1 and is essentially invisible. --- src/mixins/itext_behavior.mixin.js | 44 ++++++++++++++++++------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/mixins/itext_behavior.mixin.js b/src/mixins/itext_behavior.mixin.js index 0ab24f3d..10b270dd 100644 --- a/src/mixins/itext_behavior.mixin.js +++ b/src/mixins/itext_behavior.mixin.js @@ -61,10 +61,13 @@ * @private */ _tick: function() { - if (this._abortCursorAnimation) { - return; - } + var tickState = { + isAborted: false, + abort: function(){ + this.isAborted = true; + }, + }; var _this = this; this.animate('_currentCursorOpacity', 1, { @@ -72,7 +75,8 @@ duration: this.cursorDuration, onComplete: function() { - _this._onTickComplete(); + if (!tickState.isAborted) + _this._onTickComplete(); }, onChange: function() { @@ -80,20 +84,25 @@ }, abort: function() { - return _this._abortCursorAnimation; + return tickState.isAborted; } }); + + this._currentTickState = tickState; }, /** * @private */ _onTickComplete: function() { - if (this._abortCursorAnimation) { - return; - } var _this = this; + var tickState = { + isAborted: false, + abort: function(){ + this.isAborted = true; + }, + }; if (this._cursorTimeout1) { clearTimeout(this._cursorTimeout1); } @@ -101,16 +110,19 @@ _this.animate('_currentCursorOpacity', 0, { duration: this.cursorDuration / 2, onComplete: function() { - _this._tick(); + if (!tickState.isAborted) + _this._tick(); }, onChange: function() { _this.canvas && _this.canvas.renderAll(); }, abort: function() { - return _this._abortCursorAnimation; + return tickState.isAborted; } }); }, 100); + + this._currentTickCompleteState = tickState; }, /** @@ -121,7 +133,8 @@ delay = restart ? 0 : this.cursorDelay; if (restart) { - this._abortCursorAnimation = true; + this._currentTickState && this._currentTickState.abort(); + this._currentTickCompleteState && this._currentTickCompleteState.abort(); clearTimeout(this._cursorTimeout1); this._currentCursorOpacity = 1; this.canvas && this.canvas.renderAll(); @@ -130,7 +143,6 @@ clearTimeout(this._cursorTimeout2); } this._cursorTimeout2 = setTimeout(function() { - _this._abortCursorAnimation = false; _this._tick(); }, delay); }, @@ -139,18 +151,14 @@ * Aborts cursor animation and clears all timeouts */ abortCursorAnimation: function() { - this._abortCursorAnimation = true; + this._currentTickState && this._currentTickState.abort(); + this._currentTickCompleteState && this._currentTickCompleteState.abort(); clearTimeout(this._cursorTimeout1); clearTimeout(this._cursorTimeout2); this._currentCursorOpacity = 0; this.canvas && this.canvas.renderAll(); - - var _this = this; - setTimeout(function() { - _this._abortCursorAnimation = false; - }, 10); }, /** From b0e2caf88f00a242cfbf6d37db1a220ad68d86c6 Mon Sep 17 00:00:00 2001 From: KJ Tsanaktsidis Date: Tue, 24 Feb 2015 15:09:45 +1100 Subject: [PATCH 2/2] Fixed style errors from linter --- src/mixins/itext_behavior.mixin.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/mixins/itext_behavior.mixin.js b/src/mixins/itext_behavior.mixin.js index 10b270dd..4d684735 100644 --- a/src/mixins/itext_behavior.mixin.js +++ b/src/mixins/itext_behavior.mixin.js @@ -62,21 +62,22 @@ */ _tick: function() { - var tickState = { + var tickState, _this = this; + tickState = { isAborted: false, - abort: function(){ + abort: function() { this.isAborted = true; }, }; - var _this = this; this.animate('_currentCursorOpacity', 1, { duration: this.cursorDuration, onComplete: function() { - if (!tickState.isAborted) + if (!tickState.isAborted) { _this._onTickComplete(); + } }, onChange: function() { @@ -96,10 +97,10 @@ */ _onTickComplete: function() { - var _this = this; - var tickState = { + var tickState, _this = this; + tickState = { isAborted: false, - abort: function(){ + abort: function() { this.isAborted = true; }, }; @@ -110,8 +111,9 @@ _this.animate('_currentCursorOpacity', 0, { duration: this.cursorDuration / 2, onComplete: function() { - if (!tickState.isAborted) + if (!tickState.isAborted) { _this._tick(); + } }, onChange: function() { _this.canvas && _this.canvas.renderAll();