@@ -120,6 +120,77 @@ suite('Environment', function() {
120120 test ( 'p5.prototype.getFrameRate' , function ( ) {
121121 assert . strictEqual ( myp5 . getFrameRate ( ) , 0 ) ;
122122 } ) ;
123+
124+ suite ( 'drawing with target frame rates' , function ( ) {
125+ let clock ;
126+ let prevRequestAnimationFrame ;
127+ let nextFrameCallback = ( ) => { } ;
128+ let controlledP5 ;
129+
130+ setup ( function ( ) {
131+ clock = sinon . useFakeTimers ( 0 ) ;
132+ sinon . stub ( window . performance , 'now' , Date . now ) ;
133+
134+ // Save the real requestAnimationFrame so we can restore it later
135+ prevRequestAnimationFrame = window . requestAnimationFrame ;
136+ // Use a fake requestAnimationFrame that just stores a ref to the callback
137+ // so that we can call it manually
138+ window . requestAnimationFrame = function ( cb ) {
139+ nextFrameCallback = cb ;
140+ } ;
141+
142+ return new Promise ( function ( resolve ) {
143+ controlledP5 = new p5 ( function ( p ) {
144+ p . setup = function ( ) {
145+ p . createCanvas ( 10 , 10 ) ;
146+ p . frameRate ( 60 ) ;
147+ p . loop ( ) ;
148+ resolve ( p ) ;
149+ } ;
150+
151+ p . draw = function ( ) { } ;
152+ } ) ;
153+ } ) ;
154+ } ) ;
155+
156+ teardown ( function ( ) {
157+ clock . restore ( ) ;
158+ window . performance . now . restore ( ) ;
159+ window . requestAnimationFrame = prevRequestAnimationFrame ;
160+ nextFrameCallback = function ( ) { } ;
161+ controlledP5 . remove ( ) ;
162+ } ) ;
163+
164+ test ( 'draw() is called at the correct frame rate given a faster display' , function ( ) {
165+ sinon . spy ( controlledP5 , 'draw' ) ;
166+
167+ clock . tick ( 1000 / 200 ) ; // Simulate a 200Hz refresh rate
168+ nextFrameCallback ( ) ; // trigger the next requestAnimationFrame
169+ assert ( controlledP5 . draw . notCalled , 'draw() should not be called before 1s/60' ) ;
170+
171+ // Advance until 5ms before the next frame should render.
172+ // This should be within p5's threshold for rendering the frame.
173+ clock . tick ( 1000 / 60 - 1000 / 200 - 5 ) ;
174+ nextFrameCallback ( ) ; // trigger the next requestAnimationFrame
175+ assert ( controlledP5 . draw . calledOnce , 'one frame should have been drawn' ) ;
176+ // deltaTime should reflect real elapsed time
177+ assert . equal ( controlledP5 . deltaTime , 1000 / 60 - 5 ) ;
178+
179+ // Advance enough time forward to be 1s/60 - 5ms from the last draw
180+ clock . tick ( 1000 / 60 - 5 ) ;
181+ nextFrameCallback ( ) ; // trigger the next requestAnimationFrame
182+ // Even though this is 1s/60 - 5ms from the last draw, the last frame came
183+ // in early, so we still shouldn't draw
184+ assert ( controlledP5 . draw . calledOnce , 'draw() should not be called before 1s/60 past the last target draw time' ) ;
185+
186+ // Advance enough time forward to be 1s/60 from the last draw
187+ clock . tick ( 5 ) ;
188+ nextFrameCallback ( ) ;
189+ assert ( controlledP5 . draw . calledTwice ) ; // Now it should draw again!
190+ // deltaTime should reflect real elapsed time
191+ assert . equal ( controlledP5 . deltaTime , 1000 / 60 ) ;
192+ } ) ;
193+ } ) ;
123194 } ) ;
124195
125196 suite ( 'p5.prototype.getTargetFrameRate' , function ( ) {
0 commit comments