Skip to content

Instantly share code, notes, and snippets.

@nilium
Created January 2, 2012 09:34
Show Gist options
  • Select an option

  • Save nilium/1550039 to your computer and use it in GitHub Desktop.

Select an option

Save nilium/1550039 to your computer and use it in GitHub Desktop.
class AnimationBlock
constructor: (@fps) ->
@millisecondsPerFrame = 1000.0 / fps
@animations = []
@interval = null
@callback =>
time = AnimationBlock.getTime()
animsCopy = @animations.slice(0) # copy the array
for anim in animsCopy
anim.tick time if anim.isRunning()
@removeAnimation anim if not anim.isRunning()
if @animations.length > 0 and AnimationBlock.requestAnimationFrame?
AnimationBlock.requestAnimationFrame(@callback)
@requestAnimationFrame: (->
reqFrame = window.requestAnimationFrame or
window.webkitRequestAnimationFrame or
window.mozRequestAnimationFrame or
window.oRequestAnimationFrame or
null
if reqFrame?
return ((callback) -> return reqFrame.call(window, callback))
else
return null)()
@getTime: (->
if window.mozRequestAnimationFrame?
return (-> return window.mozAnimationStartTime)
else
return -> return new Date().getTime())()
@setInterval: (callback, milliseconds) ->
return setInterval(callback, milliseconds)
@clearInterval: (interval) -> clearInterval(interval)
addAnimation: (newAnim) ->
for anim in @animations
return if anim is newAnim
@animations.push anim
anim.start() unless anim.isRunning()
if @animations.length is 1
if AnimationBlock.requestAnimationFrame?
AnimationBlock.requestAnimationFrame(@callback)
else
@interval = AnimationBlock.setInterval(@callback, @millisecondsPerFrame)
removeAnimation: (animToRemove) ->
# not very pretty
@animations = for anim in animations.slice(0) when anim isnt animToRemove
anim
if @animations.length is 0 and not AnimationBlock.requestAnimationFrame?
AnimationBlock.clearInterval @interval
class Animation
constructor: (duration, @responder, @timingFunc) ->
@running = no
@start = 0
@elapsed = 0
@setDuration(duration)
@block = null
@chain = []
@timingFunc ?= Animation.TIMING.linear()
duration: ->
return @duration
setDuration: (@duration) ->
@duration = 1 if @duration < 1
@durationOverOne = 1.0 / @duration
return this
stop: ->
return this unless @running
@running = no
@elapsed = AnimationBlock.getTime() - @start
return this
start: (block) ->
return this if @running
@start = AnimationBlock.getTime() - @elapsed
@running = yes
@responder?.animationStarted? this if @elapsed is 0
block ?= @block || Animation.animationBlock
block.addAnimation this
@block = block
return this
restart: (block) ->
block ?= this.block || Animation.animationBlock
@reset().start(block)
tick: (time) ->
if responder
time ?= @start + @elapsed
progress = @progress(time)
if @responder? and progress < 1.0
easedProgress = if @timingFunc? then @timingFunc(progress) else progress
@responder.animationFrame this, easedProgress
if @running and progress >= 1
@stop()
@responder?.animationFinished this
anim.restart() for anim in @chain
return this
progress: (time) ->
return 1.0 if not @running and @duration <= @elapsed
time = if time? then time - @start else @elapsed
return Math.min(1, Math.max(0, time * @durationOverOne))
reset: ->
@elapsed = 0
return this
isRunning: ->
return @running
chainAnimation: (anim) ->
@chian.push anim
return this
setChain: (chain) ->
if typeof chain isnt 'Array'
return undefined
@chain = chain.slice(0)
return this
@SHORT_DURATION = 120
@NORMAL_DURATION = 200
@LONG_DURATION = 320
@TIMING =
linear: -> null
easeInOut: (->
easingTable = []
for index in [0 .. 999]
delta = index * 0.001
delta = (Math.sin((delta * Math.PI) - (Math.PI * 0.5)) * 0.5) + 0.5
easingTable[index] = Math.min(1.0, Math.max(0.0, delta))
return ->
return (progress) ->
easingTable[ Math.floor(progress * (easingTable.length - 1)) ]
)()
easeIn: (->
easingTable = []
for index in [0 .. 999]
delta = index * 0.001
delta = Math.sin(Math.PI * 0.5 + delta * (Math.PI * 0.5))
easingTable[index] = 1.0 - Math.min(1.0, Math.max(0.0, delta))
return ->
return (progress) ->
easingTable[ Math.floor(progress * (easingTable.length - 1)) ]
)()
easeOut: (->
easingTable = []
for index in [0 .. 999]
delta = index * 0.001
delta = Math.sin(delta * (Math.PI * 0.5))
easingTable[index] = Math.min(1.0, Math.max(0.0, delta))
return ->
return (progress) ->
easingTable[ Math.floor(progress * (easingTable.length - 1)) ]
)()
bounce: (bounceDelta, bounces, bounceScale, originalEasing) ->
bounceDelta ?= 0.25
bounces ?= 3
bounceScale ?= 0.1
easingTable = []
deltaLength = Math.floor bounceDelta * 1000
insertLength = 1000 - deltaLength
for index in [0 .. insertLength - 1]
delta = index / insertLength
easingTable[index] = if originalEasing? then originalEasing(delta) else delta
offset = insertLength
for bounce in [bounces .. 1]
for index in [0 .. deltaLength - 1]
bounceIndex = offset + index
bounceDelta = Math.sin((index / deltaLength) * Math.PI) * bounceScale
easingTable[bounceIndex] = 1.0 - bounceDelta
bounceScale *= 0.25
offset += deltaLength
return (progress) ->
easingTable[ Math.floor(progress * (easingTable.length - 1)) ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment