Skip to content

Commit dc8aa29

Browse files
committed
Fix animation frames
1 parent 1d28cd6 commit dc8aa29

File tree

1 file changed

+97
-17
lines changed

1 file changed

+97
-17
lines changed

src/Elm/Kernel/Browser.js

Lines changed: 97 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -96,41 +96,121 @@ var _Browser_document = __Debugger_document || F4(function(impl, flagDecoder, de
9696
// ANIMATION
9797

9898

99-
var _Browser_cancelAnimationFrame =
100-
typeof cancelAnimationFrame !== 'undefined'
101-
? cancelAnimationFrame
102-
: function(id) { clearTimeout(id); };
99+
var _Browser_requestAnimationFrame_queue = {};
100+
var _Browser_inAnimationFrame = false;
101+
var _Browser_pendingAnimationFrame = false;
102+
var _Browser_requestAnimationFrame_id = 0;
103103

104-
var _Browser_requestAnimationFrame =
104+
function _Browser_cancelAnimationFrame(id)
105+
{
106+
delete _Browser_requestAnimationFrame_queue[id];
107+
}
108+
109+
function _Browser_requestAnimationFrame(callback)
110+
{
111+
var id = _Browser_requestAnimationFrame_id;
112+
_Browser_requestAnimationFrame_id++;
113+
_Browser_requestAnimationFrame_queue[id] = callback;
114+
if (!_Browser_pendingAnimationFrame)
115+
{
116+
_Browser_pendingAnimationFrame = true;
117+
_Browser_requestAnimationFrame_raw(function() {
118+
_Browser_pendingAnimationFrame = false;
119+
_Browser_inAnimationFrame = true;
120+
var maxId = _Browser_requestAnimationFrame_id;
121+
for (var id2 in _Browser_requestAnimationFrame_queue)
122+
{
123+
if (id2 >= maxId)
124+
{
125+
break;
126+
}
127+
var callback = _Browser_requestAnimationFrame_queue[id2];
128+
delete _Browser_requestAnimationFrame_queue[id2];
129+
callback();
130+
}
131+
_Browser_inAnimationFrame = false;
132+
});
133+
}
134+
return id;
135+
}
136+
137+
var _Browser_requestAnimationFrame_raw =
105138
typeof requestAnimationFrame !== 'undefined'
106139
? requestAnimationFrame
107140
: function(callback) { return setTimeout(callback, 1000 / 60); };
108141

142+
// Whether `draw` is currently running. `draw` can cause side effects:
143+
// If the user renders a custom element, they can dispatch an event in
144+
// its `connectedCallback`, which happens synchronously. That causes
145+
// `update` to run while we’re in the middle of drawing, which then
146+
// causes another call to the returned function below. We can’t start
147+
// another draw while before the first one is finished.
148+
// Another thing you can do in `connectedCallback`, is to initialize
149+
// another Elm app. Even different app instances can conflict with each other,
150+
// since they all use the same `_VirtualDom_renderCount` variable.
151+
var _Browser_drawing = false;
152+
var _Browser_drawSync_queue = [];
109153

110154
function _Browser_makeAnimator(model, draw)
111155
{
112-
draw(model);
156+
// Whether we have already requested an animation frame for drawing.
157+
var pendingFrame = false;
113158

114-
var state = __4_NO_REQUEST;
159+
// Whether we have already requested to draw right after the current draw has finished.
160+
var pendingSync = false;
161+
162+
function drawHelp()
163+
{
164+
// If we’re already drawing, wait until that draw is done.
165+
if (_Browser_drawing)
166+
{
167+
if (!pendingSync)
168+
{
169+
pendingSync = true;
170+
_Browser_drawSync_queue.push(drawHelp);
171+
}
172+
return;
173+
}
174+
175+
pendingFrame = false;
176+
pendingSync = false;
177+
_Browser_drawing = true;
178+
draw(model);
179+
_Browser_drawing = false;
180+
181+
while (_Browser_drawSync_queue.length > 0)
182+
{
183+
var callback = _Browser_drawSync_queue.shift();
184+
callback();
185+
}
186+
}
115187

116188
function updateIfNeeded()
117189
{
118-
state = state === __4_EXTRA_REQUEST
119-
? __4_NO_REQUEST
120-
: ( _Browser_requestAnimationFrame(updateIfNeeded), draw(model), __4_EXTRA_REQUEST );
190+
if (pendingFrame)
191+
{
192+
drawHelp();
193+
}
121194
}
122195

196+
drawHelp();
197+
123198
return function(nextModel, isSync)
124199
{
125200
model = nextModel;
126201

127-
isSync
128-
? ( draw(model),
129-
state === __4_PENDING_REQUEST && (state = __4_EXTRA_REQUEST)
130-
)
131-
: ( state === __4_NO_REQUEST && _Browser_requestAnimationFrame(updateIfNeeded),
132-
state = __4_PENDING_REQUEST
133-
);
202+
// When using `Browser.Events.onAnimationFrame` we already are
203+
// in an animation frame, so draw straight away. Otherwise we’ll
204+
// be drawing one frame late all the time.
205+
if (isSync || _Browser_inAnimationFrame)
206+
{
207+
drawHelp();
208+
}
209+
else if (!pendingFrame)
210+
{
211+
pendingFrame = true;
212+
_Browser_requestAnimationFrame(updateIfNeeded);
213+
}
134214
};
135215
}
136216

0 commit comments

Comments
 (0)