//@flow import React, { Component } from "react"; import PropTypes from "prop-types"; import invariant from "invariant"; import getContext from "./getContext"; import loseGL from "./loseGL"; const __DEV__ = process.env.NODE_ENV === "development"; /** * WebGL context initial options. */ type WebGLContextAttributes = { alpha?: boolean, depth?: boolean, stencil?: boolean, antialias?: boolean, premultipliedAlpha?: boolean, preserveDrawingBuffer?: boolean, preferLowPowerToHighPerformance?: boolean, failIfMajorPerformanceCaveat?: boolean, }; const propTypes = { onContextCreate: PropTypes.func.isRequired, onContextFailure: PropTypes.func.isRequired, onContextLost: PropTypes.func.isRequired, onContextRestored: PropTypes.func.isRequired, webglContextAttributes: PropTypes.object, width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, style: PropTypes.object, pixelRatio: PropTypes.number, version: PropTypes.string, }; class ErrorDebug extends Component { render() { const { error } = this.props; let title = String(error.rawError || error.message || error); let detail = String(error.longMessage || error.rawError || ""); const style = { width: "100%", height: "100%", position: "absolute", top: 0, left: 0, padding: "1em", background: "#a00", color: "#fff", fontSize: "12px", lineHeight: "1.2em", fontStyle: "normal", fontWeight: "normal", fontFamily: "monospace", overflow: "auto", }; const titleStyle = { fontWeight: "bold", marginBottom: "1em", }; const detailStyle = { whiteSpace: "pre", }; return (
{title}
{detail}
); } } export default class GLViewDOM extends Component< { onContextCreate: (gl: WebGLRenderingContext) => void, onContextFailure: (e: Error) => void, onContextLost: () => void, onContextRestored: (gl: ?WebGLRenderingContext) => void, webglContextAttributes?: WebGLContextAttributes, pixelRatio?: number, width: number, height: number, style?: any, debug?: number, version?: string, }, { error: ?Error, } > { state = { error: null, }; static propTypes = propTypes; webglContextAttributes: WebGLContextAttributes; canvas: ?HTMLCanvasElement; gl: ?WebGLRenderingContext; componentDidMount() { const { onContextCreate, onContextFailure } = this.props; const gl: ?WebGLRenderingContext = this._createContext(); if (gl) { this.gl = gl; onContextCreate(gl); const { canvas } = this; invariant(canvas, "canvas is not settled in GLViewDOM#componentDidMount"); canvas.addEventListener("webglcontextlost", this._onContextLost); canvas.addEventListener("webglcontextrestored", this._onContextRestored); } else { onContextFailure(new Error("no-webgl-context")); } } componentWillUnmount() { if (this.gl) { loseGL(this.gl); this.gl = null; } const { canvas } = this; if (canvas) { canvas.removeEventListener("webglcontextlost", this._onContextLost); canvas.removeEventListener( "webglcontextrestored", this._onContextRestored ); } } render() { const { error } = this.state; let { width, height, pixelRatio, style, debug, version, ...rest } = this.props; if (!pixelRatio) pixelRatio = Number( (typeof window === "object" && window.devicePixelRatio) || 1 ); for (let k in propTypes) { if (rest.hasOwnProperty(k)) { delete rest[k]; } } return ( {error ? : null} ); } _createContext() { const { webglContextAttributes, debug, version } = this.props; const gl: ?WebGLRenderingContext = getContext( this.canvas, debug ? { ...webglContextAttributes, preserveDrawingBuffer: true } : webglContextAttributes, version || "auto" ); this.webglContextAttributes = webglContextAttributes || {}; return gl; } _onContextLost = (e: Event) => { e.preventDefault(); this.gl = null; this.props.onContextLost(); }; _onContextRestored = () => { this.gl = this._createContext(); this.props.onContextRestored(this.gl); }; onRef = (ref: HTMLCanvasElement) => { this.canvas = ref; }; debugError = !__DEV__ ? null : (error: Error) => { this.setState({ error }); }; afterDraw = !__DEV__ ? null : () => { if (this.state.error) { this.setState({ error: null }); } }; captureAsDataURL(...args: any): string { if (!this.webglContextAttributes.preserveDrawingBuffer) { console.warn( "Surface#captureAsDataURL is likely to not work if you don't define webglContextAttributes={{ preserveDrawingBuffer: true }}" ); } invariant(this.canvas, "canvas is no longer available"); return this.canvas.toDataURL(...args); } captureAsBlob(...args: any): Promise { if (!this.webglContextAttributes.preserveDrawingBuffer) { console.warn( "Surface#captureAsBlob is likely to not work if you don't define webglContextAttributes={{ preserveDrawingBuffer: true }}" ); } return Promise.resolve().then( () => new Promise((resolve, reject) => this.canvas ? this.canvas.toBlob(resolve, ...args) : reject(new Error("canvas is no longer available")) ) ); } }