Pull render animation frame logic into a helper class

This commit is contained in:
Daniel Imms
2018-01-05 08:15:21 -08:00
parent c332aeb51f
commit a846e8c08d
3 changed files with 63 additions and 55 deletions
+8 -22
View File
@@ -5,6 +5,7 @@
import { ITerminal, IBuffer, IDisposable } from './Interfaces';
import { isMac } from './utils/Browser';
import { RenderDebouncer } from './utils/RenderDebouncer';
const MAX_ROWS_TO_READ = 20;
@@ -15,9 +16,7 @@ export class AccessibilityManager implements IDisposable {
private _liveRegion: HTMLElement;
private _liveRegionLineCount: number = 0;
private _refreshRowStart: number;
private _refreshRowEnd: number;
private _refreshAnimationFrame: number = null;
private _renderRowsDebouncer: RenderDebouncer;
private _disposables: IDisposable[] = [];
@@ -44,6 +43,8 @@ export class AccessibilityManager implements IDisposable {
this._refreshRowsDimensions();
this._accessibilityTreeRoot.appendChild(this._rowContainer);
this._renderRowsDebouncer = new RenderDebouncer(this._terminal, this._renderRows.bind(this));
this._liveRegion = document.createElement('div');
this._liveRegion.classList.add('live-region');
this._liveRegion.setAttribute('aria-live', 'assertive');
@@ -80,10 +81,7 @@ export class AccessibilityManager implements IDisposable {
}
public dispose(): void {
if (this._refreshAnimationFrame) {
window.cancelAnimationFrame(this._refreshAnimationFrame);
this._refreshAnimationFrame = null;
}
this._renderRowsDebouncer.dispose();
this._terminal.element.removeChild(this._accessibilityTreeRoot);
this._accessibilityTreeRoot = null;
this._rowContainer = null;
@@ -169,27 +167,15 @@ export class AccessibilityManager implements IDisposable {
}
private _refreshRows(start?: number, end?: number): void {
start = start || 0;
end = end || this._terminal.rows - 1;
this._refreshRowStart = this._refreshRowStart ? Math.min(this._refreshRowStart, start) : start;
this._refreshRowEnd = this._refreshRowEnd ? Math.max(this._refreshRowEnd, end) : end;
if (this._refreshAnimationFrame) {
return;
}
this._refreshAnimationFrame = window.requestAnimationFrame(() => this._innerRefreshRows());
this._renderRowsDebouncer.refresh(start, end);
}
private _innerRefreshRows(): void {
private _renderRows(start: number, end: number): void {
const buffer: IBuffer = (<any>this._terminal.buffer);
for (let i = this._refreshRowStart; i <= this._refreshRowEnd; i++) {
for (let i = start; i <= end; i++) {
const lineData = buffer.translateBufferLineToString(buffer.ybase + i, true);
this._rowElements[i].textContent = lineData;
}
this._refreshRowStart = null;
this._refreshRowEnd = null;
this._refreshAnimationFrame = null;
}
private _refreshRowsDimensions(): void {
+5 -33
View File
@@ -13,11 +13,10 @@ import { BaseRenderLayer } from './BaseRenderLayer';
import { IRenderLayer, IColorSet, IRenderer, IRenderDimensions } from './Interfaces';
import { LinkRenderLayer } from './LinkRenderLayer';
import { EventEmitter } from '../EventEmitter';
import { RenderDebouncer } from '../utils/RenderDebouncer';
export class Renderer extends EventEmitter implements IRenderer {
/** A queue of the rows to be refreshed */
private _refreshRowsQueue: {start: number, end: number}[] = [];
private _refreshAnimationFrame: number = null;
private _renderDebouncer: RenderDebouncer;
private _renderLayers: IRenderLayer[];
private _devicePixelRatio: number;
@@ -53,6 +52,7 @@ export class Renderer extends EventEmitter implements IRenderer {
};
this._devicePixelRatio = window.devicePixelRatio;
this._updateDimensions();
this._renderDebouncer = new RenderDebouncer(this._terminal, this._refreshLoop.bind(this));
}
public onWindowResize(devicePixelRatio: number): void {
@@ -129,42 +129,14 @@ export class Renderer extends EventEmitter implements IRenderer {
* @param {number} end The end row.
*/
public queueRefresh(start: number, end: number): void {
this._refreshRowsQueue.push({ start: start, end: end });
if (!this._refreshAnimationFrame) {
this._refreshAnimationFrame = window.requestAnimationFrame(this._refreshLoop.bind(this));
}
this._renderDebouncer.refresh(start, end);
}
/**
* Performs the refresh loop callback, calling refresh only if a refresh is
* necessary before queueing up the next one.
*/
private _refreshLoop(): void {
let start;
let end;
if (this._refreshRowsQueue.length > 4) {
// Just do a full refresh when 5+ refreshes are queued
start = 0;
end = this._terminal.rows - 1;
} else {
// Get start and end rows that need refreshing
start = this._refreshRowsQueue[0].start;
end = this._refreshRowsQueue[0].end;
for (let i = 1; i < this._refreshRowsQueue.length; i++) {
if (this._refreshRowsQueue[i].start < start) {
start = this._refreshRowsQueue[i].start;
}
if (this._refreshRowsQueue[i].end > end) {
end = this._refreshRowsQueue[i].end;
}
}
}
this._refreshRowsQueue = [];
this._refreshAnimationFrame = null;
// Render
start = Math.max(start, 0);
end = Math.min(end, this._terminal.rows - 1);
private _refreshLoop(start: number, end: number): void {
this._renderLayers.forEach(l => l.onGridChanged(this._terminal, start, end));
this._terminal.emit('refresh', {start, end});
}
+50
View File
@@ -0,0 +1,50 @@
import { ITerminal, IDisposable } from '../Interfaces';
/**
* Debounces calls to render terminal rows using animation frames.
*/
export class RenderDebouncer implements IDisposable {
private _rowStart: number;
private _rowEnd: number;
private _animationFrame: number = null;
constructor(
private _terminal: ITerminal,
private _callback: (start: number, end: number) => void
) {
}
public dispose(): void {
if (this._animationFrame) {
window.cancelAnimationFrame(this._animationFrame);
this._animationFrame = null;
}
}
public refresh(rowStart?: number, rowEnd?: number): void {
rowStart = rowStart || 0;
rowEnd = rowEnd || this._terminal.rows - 1;
this._rowStart = this._rowStart ? Math.min(this._rowStart, rowStart) : rowStart;
this._rowEnd = this._rowEnd ? Math.max(this._rowEnd, rowEnd) : rowEnd;
if (this._animationFrame) {
return;
}
this._animationFrame = window.requestAnimationFrame(() => this._innerRefresh());
}
private _innerRefresh(): void {
// Clamp values
this._rowStart = Math.max(this._rowStart, 0);
this._rowEnd = Math.min(this._rowEnd, this._terminal.rows - 1);
// Run render callback
this._callback(this._rowStart, this._rowEnd);
// Reset debouncer
this._rowStart = null;
this._rowEnd = null;
this._animationFrame = null;
}
}