diff --git a/extras/Studio.framer/framer/version b/extras/Studio.framer/framer/version index c7930257d..301160a93 100644 --- a/extras/Studio.framer/framer/version +++ b/extras/Studio.framer/framer/version @@ -1 +1 @@ -7 \ No newline at end of file +8 \ No newline at end of file diff --git a/framer/Animation.coffee b/framer/Animation.coffee index 093bde7ba..d46d4748f 100644 --- a/framer/Animation.coffee +++ b/framer/Animation.coffee @@ -282,6 +282,10 @@ class exports.Animation extends BaseClass @_valueUpdaters[k] = @_updateGradientValue # If the begin state is not set, animate from the same state but with alpha 0 @_stateA[k] ?= Gradient.multiplyAlpha(v, 0) + else if k is "borderWidth" + @_valueUpdaters[k] = @_updateNumericObjectValue.bind(this, ["top", "left", "bottom", "right"]) + else if k is "borderRadius" + @_valueUpdaters[k] = @_updateNumericObjectValue.bind(this, ["topLeft", "topRight", "bottomRight", "bottomLeft"]) else @_valueUpdaters[k] = @_updateNumberValue @@ -292,6 +296,26 @@ class exports.Animation extends BaseClass _updateNumberValue: (key, value) => @_target[key] = Utils.mapRange(value, 0, 1, @_stateA[key], @_stateB[key]) + _updateNumericObjectValue: (propKeys, key, value) => + valueA = @_stateA[key] + valueB = @_stateB[key] + + result = {} + + for propKey in propKeys + keyValueA = if _.isNumber(valueA) then valueA else valueA[propKey] + keyValueB = if _.isNumber(valueB) then valueB else valueB[propKey] + # If the key value is undefined in one state, use the value from the other + keyValueA ?= keyValueB + keyValueB ?= keyValueA + result[propKey] = Utils.mapRange(value, 0, 1, keyValueA, keyValueB) + + # Flatten to a single number if all properties have the same value + if _.uniq(_.values(result)).length is 1 + result = result[propKeys[0]] + + @_target[key] = result + _updateColorValue: (key, value) => @_target[key] = Color.mix(@_stateA[key], @_stateB[key], value, false, @options.colorModel) @@ -317,6 +341,10 @@ class exports.Animation extends BaseClass @isAnimatable = (v) -> _.isNumber(v) or _.isFunction(v) or isRelativeProperty(v) or Color.isColorObject(v) or Gradient.isGradientObject(v) + # Special cases that animate with different types of objects + @isAnimatableKey = (k) -> + k in ["gradient", "borderWidth", "borderRadius"] + @filterAnimatableProperties = (properties) -> # Function to filter only animatable properties out of a given set animatableProperties = {} @@ -338,7 +366,7 @@ class exports.Animation extends BaseClass animatableProperties[k] = v else if Color.isValidColorProperty(k, v) animatableProperties[k] = new Color(v) - else if k is "gradient" + else if @isAnimatableKey(k) animatableProperties[k] = v return animatableProperties diff --git a/test/tests/LayerAnimationTest.coffee b/test/tests/LayerAnimationTest.coffee index 01fef1ffc..00aceb534 100644 --- a/test/tests/LayerAnimationTest.coffee +++ b/test/tests/LayerAnimationTest.coffee @@ -879,3 +879,99 @@ describe "LayerAnimation", -> done() layer.animate gradient: null + + describe "Border radius animations", (done) -> + + it "should animate border radius from number to number", (done) -> + layer = new Layer + borderRadius: 20 + layer.on Events.AnimationEnd, -> + layer.borderRadius.should.equal 40 + done() + layer.animate + borderRadius: 40 + + it "should animate border radius from number to object", (done) -> + layer = new Layer + borderRadius: 20 + layer.on Events.AnimationEnd, -> + layer.borderRadius.bottomLeft.should.equal 40 + layer.borderRadius.bottomRight.should.equal 20 + done() + layer.animate + borderRadius: + bottomLeft: 40 + + it "should animate border radius from object to object", (done) -> + layer = new Layer + borderRadius: + bottomLeft: 40 + bottomRight: 20 + layer.on Events.AnimationEnd, -> + layer.borderRadius.bottomLeft.should.equal 10 + layer.borderRadius.bottomRight.should.equal 20 + layer.borderRadius.topLeft.should.equal 0 + layer.borderRadius.topRight.should.equal 20 + done() + layer.animate + borderRadius: + bottomLeft: 10 + topRight: 20 + + it "should animate border radius from object to number", (done) -> + layer = new Layer + borderRadius: + bottomLeft: 40 + layer.on Events.AnimationEnd, -> + layer.borderRadius.should.equal 20 + done() + layer.animate + borderRadius: 20 + + describe "Border width animations", (done) -> + + it "should animate border width from number to number", (done) -> + layer = new Layer + borderWidth: 10 + layer.on Events.AnimationEnd, -> + layer.borderWidth.should.equal 30 + done() + layer.animate + borderWidth: 30 + + it "should animate border width from number to object", (done) -> + layer = new Layer + borderWidth: 10 + layer.on Events.AnimationEnd, -> + layer.borderWidth.top.should.equal 30 + layer.borderWidth.bottom.should.equal 10 + done() + layer.animate + borderWidth: + top: 30 + + it "should animate border width from object to object", (done) -> + layer = new Layer + borderWidth: + top: 30 + bottom: 10 + layer.on Events.AnimationEnd, -> + layer.borderWidth.top.should.equal 10 + layer.borderWidth.bottom.should.equal 10 + layer.borderWidth.left.should.equal 20 + layer.borderWidth.right.should.equal 0 + done() + layer.animate + borderWidth: + top: 10 + left: 20 + + it "should animate border width from object to number", (done) -> + layer = new Layer + borderWidth: + top: 30 + layer.on Events.AnimationEnd, -> + layer.borderWidth.should.equal 10 + done() + layer.animate + borderWidth: 10