From a950bd92b7237fd6c8ef1d6089c156b52eed2dae Mon Sep 17 00:00:00 2001 From: Jamie Wong Date: Mon, 27 Nov 2017 23:43:37 -0800 Subject: [PATCH] Window resizing working --- flamechart.tsx | 130 ++++++++++++++++++++++++++++++++----------------- index.tsx | 13 ++++- 2 files changed, 97 insertions(+), 46 deletions(-) diff --git a/flamechart.tsx b/flamechart.tsx index 0ca579c..be886dd 100644 --- a/flamechart.tsx +++ b/flamechart.tsx @@ -137,6 +137,8 @@ export class Flamechart { } interface FlamechartViewProps { + width: number + height: number flamechart: Flamechart } @@ -185,6 +187,7 @@ const DEVICE_PIXEL_RATIO = window.devicePixelRatio export class FlamechartView extends Component { renderer: ReglCommand | null = null + ctx: WebGLRenderingContext | null = null canvas: HTMLCanvasElement | null = null overlayCanvas: HTMLCanvasElement | null = null overlayCtx: CanvasRenderingContext2D | null = null @@ -228,15 +231,15 @@ export class FlamechartView extends Component { new Vec2(this.viewSpaceViewportWidth(), this.viewSpaceViewportHeight()) ) - const ctx = this.canvas.getContext('webgl')! - this.renderer = rectangleBatchRenderer(ctx, configSpaceRects, colors) + this.ctx = this.canvas.getContext('webgl')! + this.renderer = rectangleBatchRenderer(this.ctx, configSpaceRects, colors) } private canvasRef = (element?: Element) => { if (element) { this.canvas = element as HTMLCanvasElement this.preprocess() - this.renderGL() + this.renderCanvas() } else { this.canvas = null } @@ -246,7 +249,7 @@ export class FlamechartView extends Component { if (element) { this.overlayCanvas = element as HTMLCanvasElement this.overlayCtx = this.overlayCanvas.getContext('2d') - this.renderLabels() + this.renderCanvas() } else { this.overlayCanvas = null this.overlayCtx = null @@ -300,9 +303,34 @@ export class FlamechartView extends Component { .times(this.configSpaceToWorldSpace()) } + private resizeOverlayCanvasIfNeeded() { + if (!this.overlayCanvas) return + let {width, height} = this.overlayCanvas.getBoundingClientRect() + {/* + We render text at a higher resolution then scale down to + ensure we're rendering at 1:1 device pixel ratio. + This ensures our text is rendered crisply. + */} + width = Math.floor(width) + height = Math.floor(height) + + // Still initializing: don't resize yet + if (width === 0 || height === 0) return + + const scaledWidth = width * DEVICE_PIXEL_RATIO + const scaledHeight = height * DEVICE_PIXEL_RATIO + + if (scaledWidth === this.overlayCanvas.width && + scaledHeight === this.overlayCanvas.height) return + + this.overlayCanvas.width = scaledWidth + this.overlayCanvas.height = scaledHeight + } + private renderLabels() { const ctx = this.overlayCtx if (!ctx) return + this.resizeOverlayCanvasIfNeeded() const configSpaceToOverlaySpace = this.viewSpaceToOverlaySpace() .times(this.worldSpaceToViewSpace()) @@ -338,12 +366,53 @@ export class FlamechartView extends Component { } } - private renderGL() { + private resizeCanvasIfNeeded() { + if (!this.canvas || !this.ctx) return + let {width, height} = this.canvas.getBoundingClientRect() + width = Math.floor(width) + height = Math.floor(height) + + // Still initializing: don't resize yet + if (width === 0 || height === 0) return + + // Already at the right size + if (width === this.canvas.width && height === this.canvas.height) return + + const oldWidth = this.canvas.width + const oldHeight = this.canvas.height + this.canvas.width = width + this.canvas.height = height + + // Resize the world-space viewport rectangle to match the + // window size. + this.worldSpaceViewportRect = this.worldSpaceViewportRect.withSize( + this.worldSpaceViewportRect.size.timesPointwise(new Vec2( + width / oldWidth, + height / oldHeight + )) + ) + this.ctx.viewport(0, 0, width, height) + } + + private renderRects() { if (!this.renderer || !this.canvas) return + this.resizeCanvasIfNeeded() + this.renderer({ configSpaceToNDC: this.configSpaceToNDC() }) - this.renderLabels() + } + + private renderCanvas() { + if (!this.canvas || this.canvas.getBoundingClientRect().width < 2) { + // If the canvs is still tiny, it means browser layout hasn't had + // a chance to run yet. Defer rendering until we have the real canvas + // size. + requestAnimationFrame(() => this.renderCanvas()) + } else { + this.renderRects() + this.renderLabels() + } } private pan(viewSpaceDelta: Vec2) { @@ -407,49 +476,24 @@ export class FlamechartView extends Component { this.pan(new Vec2(ev.deltaX, ev.deltaY)) } - this.renderGL() + // This is a fix to rendering in Firefox. + // TODO(jlfwong): Change this to do the more correct thing: + // debounce to rendering at most once a frame. + requestAnimationFrame(() => this.renderCanvas()) } - componentDidMount() { - window.addEventListener('resize', () => { - const width = window.innerWidth - const height = window.innerHeight - if (this.canvas) { - const oldWidth = this.canvas.width - const oldHeight = this.canvas.height - this.canvas.width = width - this.canvas.height = height - - // Resize the world-space viewport rectangle to match the - // window size. - this.worldSpaceViewportRect = this.worldSpaceViewportRect.withSize( - this.worldSpaceViewportRect.size.timesPointwise(new Vec2( - width / oldWidth, - height / oldHeight - )) - ) - this.renderGL() - } - if (this.overlayCanvas) { - this.overlayCanvas.width = width * DEVICE_PIXEL_RATIO - this.overlayCanvas.height = height * DEVICE_PIXEL_RATIO - this.renderLabels() - } - }) - } + componentDidUpdate() { this.renderCanvas() } + componentDidMount() { this.renderCanvas() } render() { - // TODO(jlfwong): Handle node and/or window resizing - - const width = window.innerWidth - const height = window.innerHeight + const {width, height} = this.props return (
{/* @@ -458,7 +502,7 @@ export class FlamechartView extends Component { This ensures our text is rendered crisply. */}
@@ -467,10 +511,6 @@ export class FlamechartView extends Component { } const style = StyleSheet.create({ - fullscreen: { - width: '100vw', - height: '100vh', - }, fill: { width: '100%', height: '100%', diff --git a/index.tsx b/index.tsx index 30ccee1..562dfaf 100644 --- a/index.tsx +++ b/index.tsx @@ -28,10 +28,21 @@ class Application extends Component<{}, ApplicaionState> { ev.preventDefault() } + componentDidMount() { + window.addEventListener('resize', () => { + this.forceUpdate() + }) + } + render() { const {flamechart} = this.state return
- {flamechart && } + {flamechart && + }
} }