blob: 8269f87218301b177a00b0b51ab389e232b6ac83 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const CANVAS_OVERDRAW_FACTOR = 2;
// TODO: The word 'Controller' makes it sound like it lives on the controller
// thread. Should find a better name.
/**
* Creates a canvas with a context that is set up for compositor scrolling. The
* canvas has a fixed height of visibleHeight * CANVAS_OVERDRAW_FACTOR. This
* class is in charge of accepting new scrollTop value for the container element
* of the canvas, so it can compute the top offset required to recenter the
* canvas.
*/
export class CanvasController {
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
private scrollOffset = 0;
// Number of additional pixels above/below for compositor scrolling.
private extraHeightPerSide = 0;
private canvasHeight = 0;
private canvasWidth = 0;
constructor() {
this.canvas = document.createElement('canvas');
const ctx = this.canvas.getContext('2d');
if (!ctx) {
throw new Error('Could not create canvas context');
}
this.ctx = ctx;
}
setDimensions(width: number, visibleCanvasHeight: number) {
this.canvasWidth = width;
this.canvasHeight = visibleCanvasHeight * CANVAS_OVERDRAW_FACTOR;
this.extraHeightPerSide =
Math.round((this.canvasHeight - visibleCanvasHeight) / 2);
const dpr = window.devicePixelRatio;
this.canvas.style.width = this.canvasWidth.toString() + 'px';
this.canvas.style.height = this.canvasHeight.toString() + 'px';
this.canvas.width = this.canvasWidth * dpr;
this.canvas.height = this.canvasHeight * dpr;
this.ctx.scale(dpr, dpr);
}
clear(): void {
this.ctx.fillStyle = 'white';
this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
}
getCanvasElement(): HTMLCanvasElement {
return this.canvas;
}
/**
* Returns the canvas 2D rendering context so it doesn't have to be recreated
* from the canvas element.
*/
get2DContext(): CanvasRenderingContext2D {
return this.ctx;
}
/**
* Places the canvas and its contents at the correct position.
* Re-centers the canvas element in the current viewport, and sets the context
* offsets such that the contents move up as we scroll, while rendering the
* first track within the viewport.
*/
updateScrollOffset(scrollOffset: number): void {
this.scrollOffset = scrollOffset;
}
/**
* Returns the desired y position of canvas relative to the
* ScrollingTrackDisplay that owns this so the canvas is centered in the
* visible area. Since we overdraw the canvas on top, this value can be
* negative.
*/
getCanvasYStart(): number {
return this.scrollOffset - this.extraHeightPerSide;
}
// TODO(dproy): Need to write tests for this.
isYBoundsOnCanvas(bounds: {yStart: number, yEnd: number}) {
const canvasYStart = this.getCanvasYStart();
const canvasYEnd = canvasYStart + this.canvasHeight;
return (bounds.yEnd >= canvasYStart && bounds.yStart <= canvasYEnd);
}
}