@@ -6,15 +6,64 @@ import { scheduleOnce } from '@ember/runloop';
6
6
import { setupRouter , reset , whenRouteIdle } from 'ember-app-scheduler' ;
7
7
8
8
let requestId ;
9
+ let callbackRequestId ;
10
+ let idleRequestId ;
11
+
12
+ class CounterPool {
13
+ constructor ( ) {
14
+ this . _counter = 0 ;
15
+ this . onFinishedPromise = null ;
16
+ this . onFinishedCallback = null ;
17
+
18
+ this . flush ( ) ;
19
+ }
20
+
21
+ get counter ( ) {
22
+ return this . _counter ;
23
+ }
24
+ set counter ( value ) {
25
+ // put a cap so flush queue doesn't take too many paint cycles
26
+ this . _counter = Math . min ( value , 2 ) ;
27
+ }
28
+
29
+ flush ( ) {
30
+ if ( this . counter === 0 && this . onFinishedPromise && this . onFinishedPromise . then ) {
31
+ // when we are done, attach a then callback and update scroll position
32
+ this . onFinishedPromise . then ( ( ) => {
33
+ this . onFinishedCallback && this . onFinishedCallback ( ) ;
34
+ } ) ;
35
+ }
36
+
37
+ idleRequestId = window . requestAnimationFrame ( ( ) => {
38
+ this . decrement ( ) ;
39
+ this . flush ( ) ;
40
+ } ) ;
41
+ }
42
+
43
+ decrement ( ) {
44
+ this . counter = this . counter - 1 ;
45
+ }
46
+
47
+ destroy ( ) {
48
+ window . cancelAnimationFrame ( idleRequestId ) ;
49
+ this . counter = 0 ;
50
+ this . onFinishedPromise = null ;
51
+ this . onFinishedCallback = null ;
52
+ }
53
+ }
9
54
10
55
// to prevent scheduleOnce calling multiple times, give it the same ref to this function
11
56
const CALLBACK = function ( transition ) {
12
- this . updateScrollPosition ( transition ) ;
57
+ callbackRequestId = window . requestAnimationFrame ( ( ) => {
58
+ this . updateScrollPosition ( transition ) ;
59
+ } ) ;
13
60
}
14
61
15
62
class EmberRouterScroll extends EmberRouter {
16
63
@inject ( 'router-scroll' ) service ;
17
64
65
+ idlePool ;
66
+
18
67
@computed
19
68
get isFastBoot ( ) {
20
69
const fastboot = getOwner ( this ) . lookup ( 'service:fastboot' ) ;
@@ -42,6 +91,10 @@ class EmberRouterScroll extends EmberRouter {
42
91
window . cancelAnimationFrame ( requestId ) ;
43
92
}
44
93
94
+ if ( callbackRequestId ) {
95
+ window . cancelAnimationFrame ( callbackRequestId ) ;
96
+ }
97
+
45
98
super . destroy ( ...arguments ) ;
46
99
}
47
100
@@ -52,6 +105,11 @@ class EmberRouterScroll extends EmberRouter {
52
105
* @param {transition|transition[] } transition If before Ember 3.6, this will be an array of transitions, otherwise
53
106
*/
54
107
updateScrollPosition ( transition ) {
108
+ if ( this . idlePool ) {
109
+ this . idlePool . destroy ( ) ;
110
+ this . idlePool = null ;
111
+ }
112
+
55
113
const url = get ( this , 'currentURL' ) ;
56
114
const hashElement = url ? document . getElementById ( url . split ( '#' ) . pop ( ) ) : null ;
57
115
@@ -109,13 +167,20 @@ class EmberRouterScroll extends EmberRouter {
109
167
110
168
if ( ! scrollWhenIdle && ! scrollWhenAfterRender ) {
111
169
// out of the option, this happens on the tightest schedule
112
- scheduleOnce ( 'render' , this , CALLBACK . bind ( this , transition ) ) ;
113
- } else if ( scrollWhenAfterRender ) {
170
+ scheduleOnce ( 'render' , this , CALLBACK , transition ) ;
171
+ } else if ( scrollWhenAfterRender && ! scrollWhenIdle ) {
114
172
// out of the option, this happens on the tightest schedule
115
- scheduleOnce ( 'afterRender' , this , CALLBACK . bind ( this , transition ) ) ;
173
+ scheduleOnce ( 'afterRender' , this , CALLBACK , transition ) ;
116
174
} else {
117
- // as described in ember-app-scheduler, this addon can be used to delay rendering until after the route is idle
118
- whenRouteIdle ( ) . then ( CALLBACK . bind ( this , transition ) ) ;
175
+ if ( ! this . idlePool ) {
176
+ this . idlePool = new CounterPool ( ) ;
177
+ }
178
+
179
+ // increments happen all in one batch (before processing flush queue) and happens indeterminately
180
+ // e.g. 4, 6, 10 times this could be called
181
+ this . idlePool . counter = this . idlePool . counter + 1 ;
182
+ this . idlePool . onFinishedPromise = whenRouteIdle ( ) ;
183
+ this . idlePool . onFinishedCallback = this . updateScrollPosition . bind ( this , transition ) ;
119
184
}
120
185
}
121
186
}
0 commit comments