Permalink
Browse files

Merge pull request #467 from koenbok/feature/spring-improvements

Feature/spring improvements
  • Loading branch information...
nvh committed Feb 10, 2017
2 parents ad9fa7e + bf8b7d5 commit d50c3f8cd98e0324baeaeda873926d09c33434d3
View
@@ -5,8 +5,9 @@ Utils = require "./Utils"
{Config} = require "./Config"
{Defaults} = require "./Defaults"
{BaseClass} = require "./BaseClass"
-{Animator} = require "./Animator"
+{Animator} = require "./Animators/Animator"
{LinearAnimator} = require "./Animators/LinearAnimator"
+Curves = require "./Animators/Curves"
numberRE = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/
relativePropertyRE = new RegExp("^(?:([+-])=|)(" + numberRE.source + ")([a-z%]*)$", "i")
@@ -83,7 +84,10 @@ class exports.Animation extends BaseClass
if properties.origin
console.warn "Animation.origin: please use layer.originX and layer.originY"
- @options.curveOptions = Animator.curveOptionsFor(@options)
+ if _.isString @options.curve
+ @options.curve = Curves.fromString(@options.curve)
+ if @options.curve is Curves.Spring
+ @options.curve = @options.curve.call()
@_originalState = @_currentState()
@_repeatCounter = @options.repeat
@@ -105,8 +109,7 @@ class exports.Animation extends BaseClass
@define "isNoop", @simpleProperty("isNoop", false)
start: =>
-
- @_animator = Animation._createAnimator(@options) ? new LinearAnimator(@options.curveOptions)
+ @_animator = @options.curve(@options)
@_target = @layer
@_stateA = @_currentState()
@_stateB = {}
@@ -291,15 +294,6 @@ class exports.Animation extends BaseClass
_currentState: ->
return _.pick(@layer, _.keys(@properties))
- @_createAnimator: (options) ->
- AnimatorClass = Animator.classForCurve(options.curve)
- return null if not AnimatorClass?
- curveOptions = options.curveOptions ? Animator.curveOptionsFor(options)
- if options.debug
- console.log "Animation.start #{AnimatorClass.name}", curveOptions
-
- return new AnimatorClass curveOptions
-
@isAnimatable = (v) ->
_.isNumber(v) or _.isFunction(v) or isRelativeProperty(v) or Color.isColorObject(v)
View
@@ -1,100 +0,0 @@
-AnimatorClasses = {}
-
-AnimatorClassBezierPresets = ["ease", "ease-in", "ease-out", "ease-in-out"]
-
-class exports.Animator
-
- """
- The animator class is a very simple class that
- - Takes a set of input values at setup({input values})
- - Emits an output value for progress (0 -> 1) in value(progress)
- """
-
- constructor: (options={}) ->
- @setup options
-
- setup: (options) ->
- throw Error "Not implemented"
-
- next: (delta) ->
- throw Error "Not implemented"
-
- finished: ->
- throw Error "Not implemented"
-
- values: (delta=1/60, limit=100) ->
- values = []
- for i in [0..limit]
- values.push(@next(delta))
- if @finished()
- break
- return values
-
- @classForCurve: (curve) ->
- parsedCurve = Utils.parseFunction(curve)
- animatorClassName = parsedCurve.name.toLowerCase()
-
- if AnimatorClasses.hasOwnProperty(animatorClassName)
- return AnimatorClasses[animatorClassName]
-
- if animatorClassName in AnimatorClassBezierPresets
- return BezierCurveAnimator
-
- return null
-
- @curveOptionsFor: (options={}) ->
- curveOptions = options.curveOptions ? {}
- animatorClass = @classForCurve(options.curve)
- parsedCurve = Utils.parseFunction options.curve
- animatorClassName = parsedCurve.name.toLowerCase()
-
- # This is for compatibility with the direct Animation.time argument. This should
- # ideally also be passed as a curveOption
-
- if animatorClass in [LinearAnimator, BezierCurveAnimator]
- if _.isString(curveOptions) or _.isArray(curveOptions)
- curveOptions =
- values: curveOptions
-
- curveOptions.time ?= options.time
-
- # Support ease-in etc
- if animatorClass in [BezierCurveAnimator] and animatorClassName in AnimatorClassBezierPresets
- curveOptions.values = animatorClassName
- curveOptions.time ?= options.time
-
- # All this is to support curve: "spring(100, 20, 10)". In the future we'd like people
- # to start using curveOptions: {tension:100, friction:10} etc
-
- if parsedCurve.args.length
-
- # console.warn "Animation.curve arguments are deprecated. Please use Animation.curveOptions"
-
- if animatorClass is BezierCurveAnimator
- curveOptions.values = parsedCurve.args.map (v) -> parseFloat(v) or 0
-
- if animatorClass is SpringRK4Animator
- for k, i in ["tension", "friction", "velocity", "tolerance"]
- value = parseFloat parsedCurve.args[i]
- curveOptions[k] = value if value
-
- if animatorClass is SpringDHOAnimator
- for k, i in ["stiffness", "damping", "mass", "tolerance"]
- value = parseFloat parsedCurve.args[i]
- curveOptions[k] = value if value
- return curveOptions
-
- # start: -> Framer.Loop.on("update", )
- # stop: -> AnimationLoop.remove @
-
-{LinearAnimator} = require "./Animators/LinearAnimator"
-{BezierCurveAnimator} = require "./Animators/BezierCurveAnimator"
-{SpringRK4Animator} = require "./Animators/SpringRK4Animator"
-{SpringDHOAnimator} = require "./Animators/SpringDHOAnimator"
-AnimatorClasses["linear"] = LinearAnimator
-AnimatorClasses["bezier-curve"] = BezierCurveAnimator
-AnimatorClasses["spring-rk4"] = SpringRK4Animator
-AnimatorClasses["spring-dho"] = SpringDHOAnimator
-
-AnimatorClasses["spring"] = AnimatorClasses["spring-rk4"]
-AnimatorClasses["cubic-bezier"] = AnimatorClasses["bezier-curve"]
@@ -0,0 +1,27 @@
+class exports.Animator
+
+ """
+ The animator class is a very simple class that
+ - Takes a set of input values at setup({input values})
+ - Emits an output value for progress (0 -> 1) in value(progress)
+ """
+
+ constructor: (options={}) ->
+ @setup options
+
+ setup: (options) ->
+ throw Error "Not implemented"
+
+ next: (delta) ->
+ throw Error "Not implemented"
+
+ finished: ->
+ throw Error "Not implemented"
+
+ values: (delta=1/60, limit=100) ->
+ values = []
+ for i in [0..limit]
+ values.push(@next(delta))
+ if @finished()
+ break
+ return values
@@ -1,4 +1,4 @@
-{Animator} = require "../Animator"
+{Animator} = require "./Animator"
BezierCurveDefaults =
"linear": [0, 0, 1, 1]
@@ -26,7 +26,7 @@ class exports.BezierCurveAnimator extends Animator
@options = _.defaults options,
values: BezierCurveDefaults["ease-in-out"]
time: 1
- precision: 1/1000
+ precision: 1 / 1000
@_unitBezier = new UnitBezier \
@options.values[0],
@@ -0,0 +1,131 @@
+{BezierCurveAnimator} = require "./BezierCurveAnimator"
+{computeDerivedCurveOptions, computeDuration, computeDampingRatio} = require "./SpringCurveValueConverter"
+{SpringRK4Animator} = require "./SpringRK4Animator"
+{Defaults} = require "../Defaults"
+
+Bezier = (values...) ->
+ (options = {}) ->
+ options.values = values
+ new BezierCurveAnimator(options)
+
+BezierDefaults =
+ linear: Bezier(0, 0, 1, 1)
+ ease: Bezier(.25, .1, .25, 1)
+ easeIn: Bezier(.42, 0, 1, 1)
+ easeOut: Bezier(0, 0, .58, 1)
+ easeInOut: Bezier(.42, 0, .58, 1)
+
+
+Spring = (dampingRatio, mass, velocity) ->
+ curveOptions = {}
+ if dampingRatio? and _.isFinite(dampingRatio)
+ curveOptions.dampingRatio = dampingRatio
+ if mass?
+ curveOptions.mass = mass
+ if velocity?
+ curveOptions.velocity = velocity
+ if not _.isFinite(dampingRatio) and typeof dampingRatio is 'object'
+ curveOptions = dampingRatio
+ if curveOptions.damping? and not curveOptions.dampingRatio?
+ curveOptions.dampingRatio = curveOptions.damping
+ if not curveOptions.tension? and not curveOptions.friction?
+ curveOptions = Defaults.getDefaults "Spring", curveOptions
+
+
+ return (options) ->
+ if curveOptions.dampingRatio?
+ duration = options?.time ? 1
+ derivedOptions = computeDerivedCurveOptions(curveOptions.dampingRatio, duration, curveOptions.velocity, curveOptions.mass)
+ curveOptions = _.defaults derivedOptions, curveOptions
+ else
+ delete options?.time
+ options = _.defaults curveOptions, options
+ animator = new SpringRK4Animator(options)
+ if duration?
+ animator.time = duration
+ animator
+
+_.assign Bezier, BezierDefaults
+Spring.computeDerivedCurveOptions = computeDerivedCurveOptions
+Spring.computeDuration = computeDuration
+Spring.computeDampingRatio = computeDampingRatio
+
+
+exports.Spring = Spring
+exports.Bezier = Bezier
+parseFunction = (string) ->
+ return null unless _.isString string
+
+ # This parses functions with or without arguments like Bezier(0.1, 0.2, 0.3, 0.4) and Spring(damping: 0.5)
+ regex = /.*(Spring|Bezier)(?:\(\s*{?([\w:\s,.]*)}?\s*\)|\.(\w+))?/
+ matches = regex.exec(string)
+ return null unless matches?
+ [match, type, args, prop] = matches
+ result = {name: type, property: null, arguments: null}
+ if prop?
+ result.property = prop
+ return result
+ if not args?
+ return result
+ if args.length is 0
+ result.arguments = []
+
+
+ argumentsRegex = /\s*([\w]+)\s*:\s*([\d.]+)\s*,?/g
+ argumentObject = {}
+ while matches = argumentsRegex.exec(args)
+ [match, property, value] = matches
+ value = parseFloat(value)
+ if not isNaN(value)
+ argumentObject[property] = value
+ if _.size(argumentObject) > 0
+ result.arguments = argumentObject
+ return result
+
+ numbersRegex = /\s*([.\d]+)\s*/g
+ numbers = []
+ while matches = numbersRegex.exec(args)
+ [match, value] = matches
+ value = parseFloat(value)
+ numbers.push(value)
+ result.arguments = numbers
+ return result
+
+fromDefinition = (definition) ->
+ return null unless definition?
+ curve = Framer.Curves[definition.name]
+ return null unless curve?
+
+ if definition.property?
+ return curve[definition.property]
+ if not definition.arguments?
+ return curve
+
+ if _.isArray(definition.arguments)
+ return curve(definition.arguments...)
+
+ return curve(definition.arguments)
+
+exports.parseFunction = parseFunction
+exports.fromDefinition = fromDefinition
+exports.fromString = (string) ->
+ return null unless _.isString string
+ func = fromDefinition(parseFunction(string))
+ if func?
+ return func
+ func = Utils.parseFunction(string)
+ args = func.args.map(parseFloat)
+ switch func.name
+ when "linear" then Bezier.linear
+ when "ease" then Bezier.ease
+ when "ease-in" then Bezier.easeIn
+ when "ease-out" then Bezier.easeOut
+ when "ease-in-out" then Bezier.easeInOut
+ when "bezier-curve", "cubic-bezier"
+ Bezier(args...)
+ when "spring", "spring-rk4", "spring-dho"
+ pairs = _.zipWith(["tension", "friction", "velocity", "tolerance"], args, [250, 25, 0, 1 / 100], (key, value, defaults) -> [key, value ? defaults])
+ object = _.fromPairs(pairs)
+ Spring(object)
+ else
+ return Bezier.linear
@@ -1,12 +1,12 @@
-{Animator} = require "../Animator"
+{Animator} = require "./Animator"
class exports.LinearAnimator extends Animator
setup: (options) ->
@options = _.defaults options,
time: 1
- precision: 1/1000
+ precision: 1 / 1000
@_time = 0
Oops, something went wrong.

0 comments on commit d50c3f8

Please sign in to comment.