You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
radium/radium/engine.ease.coffee

288 lines
13 KiB
CoffeeScript

Engine::ease =
_calculateElasticValues: (duration, amplitude, period, change, inout = false) ->
if !period?
if inout
period = duration * (0.3 * 1.5)
else
period = duration * 0.3
if !amplitude? or amplitude < Math.abs(change)
amplitude = change
overshoot = period / 4
else
overshoot = period / (2 * Math.PI) * Math.asin(change / amplitude)
return [amplitude, period, change, overshoot]
backIn: (start, end, duration, next = null, infinite = false , invert_repeat = false, overshoot = 1.70158) ->
return new Ease(this.engine, "backIn", infinite, start, end, @engine.current_frame, duration, invert_repeat, next, overshoot)
backOut: (start, end, duration, next = null, infinite = false , invert_repeat = false, overshoot = 1.70158) ->
return new Ease(this.engine, "backOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next, overshoot)
backInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false, overshoot = 1.70158) ->
return new Ease(this.engine, "backInOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next, overshoot)
bounceOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "bounceOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
bounceIn: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "bounceIn", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
bounceInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "bounceInOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
circOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "circOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
circIn: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "circIn", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
circInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "circInOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
cubicOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "cubicOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
cubicIn: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "cubicIn", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
cubicInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "cubicInOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
elasticOut: (start, end, duration, next = null, infinite = false , invert_repeat = false, amplitude = null, period = null) ->
[amplitude, period, change, overshoot] = @_calculateElasticValues(duration, amplitude, period, end - start)
end = start + change
return new Ease(this.engine, "elasticOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next, amplitude, period, overshoot)
elasticIn: (start, end, duration, next = null, infinite = false , invert_repeat = false, amplitude = null, period = null) ->
[amplitude, period, change, overshoot] = @_calculateElasticValues(duration, amplitude, period, end - start)
end = start + change
return new Ease(this.engine, "elasticIn", infinite, start, end, @engine.current_frame, duration, invert_repeat, next, amplitude, period, overshoot)
elasticInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false, amplitude = null, period = null) ->
[amplitude, period, change, overshoot] = @_calculateElasticValues(duration, amplitude, period, end - start, true)
end = start + change
return new Ease(this.engine, "elasticInOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next, amplitude, period, overshoot)
expoOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "expoOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
expoIn: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "expoIn", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
expoInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "expoInOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
linearNone: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "linearNone", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
linearOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "linearNone", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
linearIn: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "linearNone", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
linearInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "linearNone", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
quadOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "quadOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
quadIn: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "quadIn", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
quadInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "quadInOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
quartOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "quartOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
quartIn: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "quartIn", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
quartInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "quartInOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
sineOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "sineOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
sineIn: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "sineIn", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
sineInOut: (start, end, duration, next = null, infinite = false , invert_repeat = false) ->
return new Ease(this.engine, "sineInOut", infinite, start, end, @engine.current_frame, duration, invert_repeat, next)
class Ease
# Port based on https://github.com/jimjeffers/Easie. I don't think this qualifies as a "bad thing" :)
constructor: (@engine, @type, @infinite, @start, @end, @start_frame, @duration, @invert_repeat, @next, @params...) ->
@func = this[@type]
@change = @end - @start
@value = @start
@last_updated = @start_frame
@finished = false
# TODO: Investigate whether JS engines cache deterministic outcomes by themselves. If not,
# the below could provide some performance gain.
#@bounce_constant_1 = 1 / 2.75
#@bounce_constant_2 = 2 / 2.75
#@bounce_constant_3 = 2.5 / 2.75
goToNext: =>
@func = this[@next.type]
@change = @next.change
@value = @next.value
@start_frame = @last_updated = @engine.current_frame
@infinite = @next.infinite
@end = @next.end
@start = @next.start
@change = @next.change
@invert_repeat = @next.invert_repeat
@params = @next.params
@duration = @next.duration
@finished = false
@next = @next.next
abort: =>
@finished = true
updateValue: (current_frame) =>
# We recalculate here, to deal with 'stacked' easings. If we don't do this, letting multiple
# easings operate on each other will result in the final value being different from 'end',
# because the change was calculated once based on the value of the previous easing *at that
# moment*, rather than the actual value.
@change = @end - @start
if current_frame >= @start_frame + @duration
if @infinite
@start_frame = current_frame
if @invert_repeat
@start = @start + @change
@change = -@change
@value = @start
else if @next?
@goToNext()
else
@finished = true
@value = @start + @change
else
@value = @func(current_frame - @start_frame)
valueOf: =>
if not @finished and @engine.current_frame > @last_updated
@updateValue(@engine.current_frame)
@last_updated = @engine.current_frame
return @value
backIn: (time) =>
time = time / @duration
overshoot = @params[0]
return @change * time * time * ((overshoot + 1) * time - overshoot) + @start
backOut: (time) =>
time = time / @duration - 1
overshoot = @params[0]
return @change * (time * time * ((overshoot + 1) * time + overshoot) + 1) + @start
backInOut: (time) =>
time = time / (@duration / 2)
overshoot = @params[0] * 1.525
if time < 1
return @change / 2 * (time * time * ((overshoot + 1) * time - overshoot)) + @start
else
time -= 2
return @change / 2 * (time * time * ((overshoot + 1) * time + overshoot) + 2) + @start
bounceOut: (time, start = null) =>
time = time / @duration
start = start ? @start
if time < 1 / 2.75
return @change * (7.5625 * time * time) + start
else if time < 2 / 2.75
time = time - (1.5 / 2.75)
return @change * (7.5625 * time * time + 0.75) + start
else if time < 2.5 / 2.75
time = time - (2.25 / 2.75)
return @change * (7.5625 * time * time + 0.9375) + start
else
time = time - (2.625 / 2.75)
return @change * (7.5625 * time * time + 0.984375) + start
bounceIn: (time, start = null) =>
start = start ? @start
return @change - @bounceOut(@duration - time, 0) + start
bounceInOut: (time) =>
if time < @duration / 2
return @bounceIn(time * 2, 0) + @start
else
return @bounceOut(time * 2 - @duration, 0) + @start
circIn: (time) =>
time = time / @duration
return -@change * (Math.sqrt(1 - time * time) - 1) + @start
circOut: (time) =>
time = time / @duration - 1
return @change * Math.sqrt(1 - time * time) + @start
circInOut: (time) =>
time = time / (@duration / 2)
if time < 1
return -@change / 2 * (Math.sqrt(1 - time * time) - 1) + @start
else
time = time - 2
return @change / 2 * (Math.sqrt(1 - time * time) + 1) + @start
cubicIn: (time) =>
time = time / @duration
return @change * time * time * time + @start
cubicOut: (time) =>
time = time / @duration - 1
return @change * (time * time * time + 1) + @start
cubicInOut: (time) =>
time = time / (@duration / 2)
if time < 1
return change / 2 * time * time * time + @start
else
time = time - 2
return change / 2 * (time * time * time + 2) + begin
elasticOut: (time) =>
time = time / @duration
amplitude = @params[0]
period = @params[1]
overshoot = @params[2]
return (amplitude * Math.pow(2, -10 * time)) * Math.sin((time * @duration - overshoot) * (2 * Math.PI) / period) + @change + @start
elasticIn: (time) =>
time = time / @duration
amplitude = @params[0]
period = @params[1]
overshoot = @params[2]
return -(amplitude * Math.pow(2, -10 * time)) * Math.sin((time * @duration - overshoot) * (2 * Math.PI) / period) + @start
elasticInOut: (time) =>
time = time / (@duration / 2) - 1
amplitude = @params[0]
period = @params[1]
overshoot = @params[2]
if time < 1
return -0.5 * (amplitude * Math.pow(2, -10 * time)) * Math.sin((time * @duration - overshoot) * ((2 * Math.PI) / period)) + @start
else
return amplitude * Math.pow(2, -10 * time) * Math.sin((time * @duration - overshoot) * (2 * Math.PI) / period) + @change + @start
expoIn: (time) =>
return @change * Math.pow(2, 10 * (time / @duration - 1)) + @start
expoOut: (time) =>
return @change * (-Math.pow(2, -10 * time / @duration) + 1) + @start
expoInOut: (time) =>
time = time / (@duration / 2)
if time < 1
return @change / 2 * Math.pow(2, 10 * (time - 1)) + @start
else
return @change / 2 * (-Math.pow(2, -10 * (time - 1)) + 2) + @start
linearNone: (time) =>
return @change * time / @duration + @start
quadIn: (time) =>
time = time / @duration
return @change * time * time + @start
quadOut: (time) =>
time = time / @duration
return -@change * time * (time - 2) + @start
quadInOut: (time) =>
time = time / (@duration / 2)
if time < 1
return @change / 2 * time * time + @start
else
time = time - 1
return -@change / 2 * (time * (time - 2) - 1) + @start