import { Observable } from '../../Observable'; 
 | 
import { TimestampProvider } from '../../types'; 
 | 
import { performanceTimestampProvider } from '../../scheduler/performanceTimestampProvider'; 
 | 
import { animationFrameProvider } from '../../scheduler/animationFrameProvider'; 
 | 
  
 | 
/** 
 | 
 * An observable of animation frames 
 | 
 * 
 | 
 * Emits the amount of time elapsed since subscription and the timestamp on each animation frame. 
 | 
 * Defaults to milliseconds provided to the requestAnimationFrame's callback. Does not end on its own. 
 | 
 * 
 | 
 * Every subscription will start a separate animation loop. Since animation frames are always scheduled 
 | 
 * by the browser to occur directly before a repaint, scheduling more than one animation frame synchronously 
 | 
 * should not be much different or have more overhead than looping over an array of events during 
 | 
 * a single animation frame. However, if for some reason the developer would like to ensure the 
 | 
 * execution of animation-related handlers are all executed during the same task by the engine, 
 | 
 * the `share` operator can be used. 
 | 
 * 
 | 
 * This is useful for setting up animations with RxJS. 
 | 
 * 
 | 
 * ## Examples 
 | 
 * 
 | 
 * Tweening a div to move it on the screen 
 | 
 * 
 | 
 * ```ts 
 | 
 * import { animationFrames, map, takeWhile, endWith } from 'rxjs'; 
 | 
 * 
 | 
 * function tween(start: number, end: number, duration: number) { 
 | 
 *   const diff = end - start; 
 | 
 *   return animationFrames().pipe( 
 | 
 *     // Figure out what percentage of time has passed 
 | 
 *     map(({ elapsed }) => elapsed / duration), 
 | 
 *     // Take the vector while less than 100% 
 | 
 *     takeWhile(v => v < 1), 
 | 
 *     // Finish with 100% 
 | 
 *     endWith(1), 
 | 
 *     // Calculate the distance traveled between start and end 
 | 
 *     map(v => v * diff + start) 
 | 
 *   ); 
 | 
 * } 
 | 
 * 
 | 
 * // Setup a div for us to move around 
 | 
 * const div = document.createElement('div'); 
 | 
 * document.body.appendChild(div); 
 | 
 * div.style.position = 'absolute'; 
 | 
 * div.style.width = '40px'; 
 | 
 * div.style.height = '40px'; 
 | 
 * div.style.backgroundColor = 'lime'; 
 | 
 * div.style.transform = 'translate3d(10px, 0, 0)'; 
 | 
 * 
 | 
 * tween(10, 200, 4000).subscribe(x => { 
 | 
 *   div.style.transform = `translate3d(${ x }px, 0, 0)`; 
 | 
 * }); 
 | 
 * ``` 
 | 
 * 
 | 
 * Providing a custom timestamp provider 
 | 
 * 
 | 
 * ```ts 
 | 
 * import { animationFrames, TimestampProvider } from 'rxjs'; 
 | 
 * 
 | 
 * // A custom timestamp provider 
 | 
 * let now = 0; 
 | 
 * const customTSProvider: TimestampProvider = { 
 | 
 *   now() { return now++; } 
 | 
 * }; 
 | 
 * 
 | 
 * const source$ = animationFrames(customTSProvider); 
 | 
 * 
 | 
 * // Log increasing numbers 0...1...2... on every animation frame. 
 | 
 * source$.subscribe(({ elapsed }) => console.log(elapsed)); 
 | 
 * ``` 
 | 
 * 
 | 
 * @param timestampProvider An object with a `now` method that provides a numeric timestamp 
 | 
 */ 
 | 
export function animationFrames(timestampProvider?: TimestampProvider) { 
 | 
  return timestampProvider ? animationFramesFactory(timestampProvider) : DEFAULT_ANIMATION_FRAMES; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Does the work of creating the observable for `animationFrames`. 
 | 
 * @param timestampProvider The timestamp provider to use to create the observable 
 | 
 */ 
 | 
function animationFramesFactory(timestampProvider?: TimestampProvider) { 
 | 
  return new Observable<{ timestamp: number; elapsed: number }>((subscriber) => { 
 | 
    // If no timestamp provider is specified, use performance.now() - as it 
 | 
    // will return timestamps 'compatible' with those passed to the run 
 | 
    // callback and won't be affected by NTP adjustments, etc. 
 | 
    const provider = timestampProvider || performanceTimestampProvider; 
 | 
  
 | 
    // Capture the start time upon subscription, as the run callback can remain 
 | 
    // queued for a considerable period of time and the elapsed time should 
 | 
    // represent the time elapsed since subscription - not the time since the 
 | 
    // first rendered animation frame. 
 | 
    const start = provider.now(); 
 | 
  
 | 
    let id = 0; 
 | 
    const run = () => { 
 | 
      if (!subscriber.closed) { 
 | 
        id = animationFrameProvider.requestAnimationFrame((timestamp: DOMHighResTimeStamp | number) => { 
 | 
          id = 0; 
 | 
          // Use the provider's timestamp to calculate the elapsed time. Note that 
 | 
          // this means - if the caller hasn't passed a provider - that 
 | 
          // performance.now() will be used instead of the timestamp that was 
 | 
          // passed to the run callback. The reason for this is that the timestamp 
 | 
          // passed to the callback can be earlier than the start time, as it 
 | 
          // represents the time at which the browser decided it would render any 
 | 
          // queued frames - and that time can be earlier the captured start time. 
 | 
          const now = provider.now(); 
 | 
          subscriber.next({ 
 | 
            timestamp: timestampProvider ? now : timestamp, 
 | 
            elapsed: now - start, 
 | 
          }); 
 | 
          run(); 
 | 
        }); 
 | 
      } 
 | 
    }; 
 | 
  
 | 
    run(); 
 | 
  
 | 
    return () => { 
 | 
      if (id) { 
 | 
        animationFrameProvider.cancelAnimationFrame(id); 
 | 
      } 
 | 
    }; 
 | 
  }); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * In the common case, where the timestamp provided by the rAF API is used, 
 | 
 * we use this shared observable to reduce overhead. 
 | 
 */ 
 | 
const DEFAULT_ANIMATION_FRAMES = animationFramesFactory(); 
 |