-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathloop.coffee
105 lines (85 loc) · 3.18 KB
/
loop.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# Look for a native version of requestAnimationFrame, or deal with the situation.
# We export the actual functions we use so that they can be overridden.
do ->
if window?
if actualRAF = window.requestAnimationFrame
actualCAF = window.cancelAnimationFrame ||
window.cancelRequestAnimationFrame
else
# Look for a prefixed version.
for prefix in ['moz', 'webkit', 'ms', 'o']
if actualRAF = window["#{prefix}RequestAnimationFrame"]
actualCAF = window["#{prefix}CancelAnimationFrame"] ||
window["#{prefix}CancelRequestAnimationFrame"]
break
# Browsers don't like it when these are called on anything other than window.
if actualRAF
actualRAF = actualRAF.bind window
actualCAF = actualCAF.bind window if actualCAF
# Emulate by calling back immediately. No handle is returned.
unless actualRAF
actualRAF = (callback) ->
callback()
return null
actualCAF = (timeout) ->
return null
else
# Assume Node.js.
actualRAF = process.nextTick
actualCAF = null
# If the request is not cancellable, deal with it by adding some state.
unless actualCAF
exports.requestAnimationFrame = (callback) ->
state = active: yes
actualRAF () -> callback() if state.active
return state
exports.cancelAnimationFrame = (state) ->
state.active = no
else
exports.requestAnimationFrame = actualRAF
exports.cancelAnimationFrame = actualCAF
# Create a loop. Only takes options, and returns a handle object
# with `start` and `stop` methods. The options are:
#
# - `rate`: tick rate in milliseconds between ticks.
# - `tick`: function called for each simulation tick.
# - `idle`: function called between tick processing, (not necessarily on every tick.)
# - `frame`: function called when we are ready to draw a frame.
#
exports.createLoop = (options={}) ->
lastTick = timerReq = frameReq = null
# `setTimeout` callback.
timerCallback = ->
timerReq = null
# Simulate remaining ticks. We run ticks at a fixed rate, regardless of the actual timer
# rate. We also allow for adjustments in rate, even between ticks inside this callback,
# hence we always reference `options`.
now = Date.now()
while now - lastTick >= options.rate
options.tick()
lastTick += options.rate
options.idle?()
# Schedule a frame, but only if we have a frame callback.
if options.frame and !frameReq
frameReq = exports.requestAnimationFrame frameCallback
# Schedule next run. Use `setTimeout` so that the tick rate may be adjusted at runtime. Also
# has the advantage of stopping the loop when things go awry.
timerReq = setTimeout timerCallback, options.rate
# `requestAnimationFrame` callback.
frameCallback = ->
frameReq = null
options.frame()
# Handle and interface.
handle =
start: ->
unless timerReq
lastTick = Date.now()
timerReq = setTimeout timerCallback, options.rate
stop: ->
if timerReq
clearInterval timerReq
timerReq = null
if frameReq
exports.cancelAnimationFrame frameReq
frameReq = null
return handle