UNPKG

ag-grid

Version:

Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components

128 lines (105 loc) 4.15 kB
import {Autowired, Bean, PostConstruct} from "../context/context"; import {GridPanel} from "../gridPanel/gridPanel"; import {LinkedList} from "./linkedList"; import {GridOptionsWrapper} from "../gridOptionsWrapper"; import {AnimationQueueEmptyEvent} from "../events"; import {Events} from "../eventKeys"; import {EventService} from "../eventService"; @Bean('animationFrameService') export class AnimationFrameService { @Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper; @Autowired('eventService') private eventService: EventService; private gridPanel: GridPanel; private p1Tasks = new LinkedList<()=>void>(); private p2Tasks = new LinkedList<()=>void>(); private ticking = false; private useAnimationFrame: boolean; public registerGridComp(gridPanel: GridPanel): void { this.gridPanel = gridPanel; } @PostConstruct private init(): void { this.useAnimationFrame = !this.gridOptionsWrapper.isSuppressAnimationFrame(); } // this method is for our ag-Grid sanity only - if animation frames are turned off, // then no place in the code should be looking to add any work to be done in animation // frames. this stops bugs - where some code is asking for a frame to be executed // when it should not. private verifyAnimationFrameOn(methodName: string): void { if (this.useAnimationFrame===false) { console.warn(`ag-Grid: AnimationFrameService.${methodName} called but animation frames are off`); } } public addP1Task(task: ()=>void): void { this.verifyAnimationFrameOn('addP1Task'); this.p1Tasks.add(task); this.schedule(); } public addP2Task(task: ()=>void): void { this.verifyAnimationFrameOn('addP2Task'); this.p2Tasks.add(task); this.schedule(); } private executeFrame(millis: number): void { this.verifyAnimationFrameOn('executeFrame'); let frameStart = new Date().getTime(); let duration = (new Date().getTime()) - frameStart; let gridPanelNeedsAFrame = true; // 16ms is 60 fps let noMaxMillis = millis <= 0; while (noMaxMillis || duration < millis) { if (gridPanelNeedsAFrame) { gridPanelNeedsAFrame = this.gridPanel.executeFrame(); } else if (!this.p1Tasks.isEmpty()) { let task = this.p1Tasks.remove(); task(); } else if (!this.p2Tasks.isEmpty()) { let task = this.p2Tasks.remove(); task(); } else { break; } duration = (new Date().getTime()) - frameStart; } if (gridPanelNeedsAFrame || !this.p1Tasks.isEmpty() || !this.p2Tasks.isEmpty()) { this.requestFrame(); } else { this.stopTicking(); } } private stopTicking(): void { this.ticking = false; let event: AnimationQueueEmptyEvent = { type: Events.EVENT_ANIMATION_QUEUE_EMPTY, columnApi: this.gridOptionsWrapper.getColumnApi(), api: this.gridOptionsWrapper.getApi() }; this.eventService.dispatchEvent(event); } public flushAllFrames(): void { if (!this.useAnimationFrame) { return; } this.executeFrame(-1); } public schedule(): void { if (!this.useAnimationFrame) { return; } if (!this.ticking) { this.ticking = true; this.requestFrame(); } } private requestFrame(): void { // check for the existence of requestAnimationFrame, and if // it's missing, then we polyfill it with setTimeout() let callback = this.executeFrame.bind(this, 60); if (window.requestAnimationFrame) { window.requestAnimationFrame(callback); } else if (window.webkitRequestAnimationFrame) { window.webkitRequestAnimationFrame(callback); } else { setTimeout(callback, 0); } } public isQueueEmpty(): boolean { return this.ticking; } }