diff --git a/src/mixins/animation.mixin.js b/src/mixins/animation.mixin.js index 031933c25fe..34ece2d0a7d 100644 --- a/src/mixins/animation.mixin.js +++ b/src/mixins/animation.mixin.js @@ -174,6 +174,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot propPair = property.split('.'); } + var propIsColor = _this.colorProperties.indexOf(property) > -1 || (propPair && _this.colorProperties.indexOf(propPair[1]) > -1); + var currentValue = propPair ? this.get(propPair[0])[propPair[1]] : this.get(property); @@ -182,23 +184,25 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot options.from = currentValue; } - if (~to.indexOf('=')) { - to = currentValue + parseFloat(to.replace('=', '')); - } - else { - to = parseFloat(to); + if (!propIsColor) { + if (~to.indexOf('=')) { + to = currentValue + parseFloat(to.replace('=', '')); + } + else { + to = parseFloat(to); + } } - fabric.util.animate({ + var _options = { startValue: options.from, endValue: to, byValue: options.by, easing: options.easing, duration: options.duration, - abort: options.abort && function() { + abort: options.abort && function () { return options.abort.call(_this); }, - onChange: function(value, valueProgress, timeProgress) { + onChange: function (value, valueProgress, timeProgress) { if (propPair) { _this[propPair[0]][propPair[1]] = value; } @@ -210,7 +214,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot } options.onChange && options.onChange(value, valueProgress, timeProgress); }, - onComplete: function(value, valueProgress, timeProgress) { + onComplete: function (value, valueProgress, timeProgress) { if (skipCallbacks) { return; } @@ -218,6 +222,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot _this.setCoords(); options.onComplete && options.onComplete(value, valueProgress, timeProgress); } - }); + }; + + if (propIsColor) { + fabric.util.animateColor(_options.startValue, _options.endValue, _options.duration, _options); + } + else { + fabric.util.animate(_options); + } } }); diff --git a/src/shapes/object.class.js b/src/shapes/object.class.js index b9effec0ad1..a70ffbfdc9f 100644 --- a/src/shapes/object.class.js +++ b/src/shapes/object.class.js @@ -596,6 +596,14 @@ ' strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit backgroundColor clipPath' ).split(' '), + /** + * List of properties to consider for animating colors. + * @type Array + */ + colorProperties: ( + 'fill stroke backgroundColor' + ).split(' '), + /** * a fabricObject that, without stroke define a clipping area with their shape. filled in black * the clipPath object gets used when the object has rendered, and the context is placed in the center diff --git a/test/unit/animation.js b/test/unit/animation.js index 6ead4387647..eb9d9156dc2 100644 --- a/test/unit/animation.js +++ b/test/unit/animation.js @@ -65,6 +65,24 @@ }, 1000); }); + QUnit.test('animate with color', function(assert) { + var done = assert.async(), properties = fabric.Object.prototype.colorProperties, + object = new fabric.Object(); + + properties.forEach(function (prop, index) { + object.set(prop, 'red'); + object.animate(prop, 'blue'); + assert.ok(true, 'animate without options does not crash'); + + setTimeout(function () { + assert.equal(object[prop], new fabric.Color('blue').toRgba(), 'property [' + prop + '] has been animated'); + if (index === properties.length - 1) { + done(); + } + }, 1000); + }); + }); + QUnit.test('animate with decrement', function(assert) { var done = assert.async(); var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 43 });