UNPKG

7.2 kBJavaScriptView Raw
1import invariant from 'invariant';
2import PropTypes from 'prop-types';
3import React from 'react';
4import { StyleSheet } from 'react-native';
5import { UnavailabilityError, CodedError } from '@unimodules/core';
6function getImageForAsset(asset) {
7 if (asset != null && typeof asset === 'object' && asset !== null && asset.downloadAsync) {
8 const dataURI = asset.localUri || asset.uri || '';
9 const image = new Image();
10 image.src = dataURI;
11 return image;
12 }
13 return asset;
14}
15function asExpoContext(gl) {
16 gl.endFrameEXP = function glEndFrameEXP() { };
17 if (!gl['_expo_texImage2D']) {
18 gl['_expo_texImage2D'] = gl.texImage2D;
19 gl.texImage2D = (...props) => {
20 let nextProps = [...props];
21 nextProps.push(getImageForAsset(nextProps.pop()));
22 return gl['_expo_texImage2D'](...nextProps);
23 };
24 }
25 if (!gl['_expo_texSubImage2D']) {
26 gl['_expo_texSubImage2D'] = gl.texSubImage2D;
27 gl.texSubImage2D = (...props) => {
28 let nextProps = [...props];
29 nextProps.push(getImageForAsset(nextProps.pop()));
30 return gl['_expo_texSubImage2D'](...nextProps);
31 };
32 }
33 return gl;
34}
35function ensureContext(canvas, contextAttributes) {
36 if (!canvas) {
37 throw new CodedError('ERR_GL_INVALID', 'Attempting to use the GL context before it has been created.');
38 }
39 const context = canvas.getContext('webgl', contextAttributes) ||
40 canvas.getContext('webgl-experimental', contextAttributes) ||
41 canvas.getContext('experimental-webgl', contextAttributes);
42 invariant(context, 'Browser does not support WebGL');
43 return asExpoContext(context);
44}
45function stripNonDOMProps(props) {
46 for (let k in propTypes) {
47 if (k in props) {
48 delete props[k];
49 }
50 }
51 return props;
52}
53const propTypes = {
54 onContextCreate: PropTypes.func.isRequired,
55 onContextRestored: PropTypes.func,
56 onContextLost: PropTypes.func,
57 webglContextAttributes: PropTypes.object,
58};
59export class GLView extends React.Component {
60 constructor() {
61 super(...arguments);
62 this.state = {
63 width: 0,
64 height: 0,
65 };
66 this._hasContextBeenCreated = false;
67 this._contextCreated = () => {
68 this.gl = this._createContext();
69 this.props.onContextCreate(this.gl);
70 if (this.canvas) {
71 this.canvas.addEventListener('webglcontextlost', this._contextLost);
72 this.canvas.addEventListener('webglcontextrestored', this._contextRestored);
73 }
74 };
75 this._updateLayout = () => {
76 if (this.container) {
77 const { clientWidth: width = 0, clientHeight: height = 0 } = this.container;
78 this.setState({ width, height });
79 }
80 };
81 this._contextLost = (event) => {
82 event.preventDefault();
83 this.gl = undefined;
84 if (this.props.onContextLost) {
85 this.props.onContextLost();
86 }
87 };
88 this._contextRestored = () => {
89 if (this.props.onContextRestored) {
90 this.gl = this._createContext();
91 this.props.onContextRestored(this.gl);
92 }
93 };
94 this._assignCanvasRef = (canvas) => {
95 this.canvas = canvas;
96 };
97 this._assignContainerRef = (element) => {
98 if (element) {
99 this.container = element;
100 }
101 else {
102 this.container = undefined;
103 }
104 this._updateLayout();
105 };
106 }
107 static async createContextAsync() {
108 const canvas = document.createElement('canvas');
109 canvas.width = window.innerWidth * window.devicePixelRatio;
110 canvas.height = window.innerHeight * window.devicePixelRatio;
111 return ensureContext(canvas);
112 }
113 static async destroyContextAsync(exgl) {
114 // Do nothing
115 return true;
116 }
117 static async takeSnapshotAsync(exgl, options = {}) {
118 invariant(exgl, 'GLView.takeSnapshotAsync(): canvas is not defined');
119 const canvas = exgl.canvas;
120 return await new Promise(resolve => {
121 canvas.toBlob((blob) => {
122 // TODO: Bacon: Should we add data URI?
123 resolve({
124 uri: blob,
125 localUri: '',
126 width: canvas.width,
127 height: canvas.height,
128 });
129 }, options.format, options.compress);
130 });
131 }
132 componentDidMount() {
133 if (window.addEventListener) {
134 window.addEventListener('resize', this._updateLayout);
135 }
136 }
137 componentWillUnmount() {
138 if (this.gl) {
139 const loseContextExt = this.gl.getExtension('WEBGL_lose_context');
140 if (loseContextExt) {
141 loseContextExt.loseContext();
142 }
143 this.gl = undefined;
144 }
145 if (this.canvas) {
146 this.canvas.removeEventListener('webglcontextlost', this._contextLost);
147 this.canvas.removeEventListener('webglcontextrestored', this._contextRestored);
148 }
149 window.removeEventListener('resize', this._updateLayout);
150 }
151 render() {
152 const { devicePixelRatio = 1 } = window;
153 const { style, ...props } = this.props;
154 const { width, height } = this.state;
155 const domProps = stripNonDOMProps(props);
156 const containerStyle = StyleSheet.flatten([{ flex: 1 }, style]);
157 return (<div ref={this._assignContainerRef} style={containerStyle}>
158 <canvas ref={this._assignCanvasRef} style={{ flex: 1, width, height }} width={width * devicePixelRatio} height={height * devicePixelRatio} {...domProps}/>
159 </div>);
160 }
161 componentDidUpdate() {
162 if (this.canvas && !this._hasContextBeenCreated) {
163 this._hasContextBeenCreated = true;
164 this._contextCreated();
165 }
166 }
167 _createContext() {
168 const { webglContextAttributes } = this.props;
169 const gl = ensureContext(this.canvas, webglContextAttributes);
170 this._webglContextAttributes = webglContextAttributes || {};
171 return gl;
172 }
173 _getGlOrReject() {
174 if (!this.gl) {
175 throw new CodedError('ERR_GL_INVALID', 'Attempting to use the GL context before it has been created.');
176 }
177 return this.gl;
178 }
179 async takeSnapshotAsync(options = {}) {
180 if (!GLView.takeSnapshotAsync) {
181 throw new UnavailabilityError('expo-gl', 'takeSnapshotAsync');
182 }
183 const gl = this._getGlOrReject();
184 return await GLView.takeSnapshotAsync(gl, options);
185 }
186 async startARSessionAsync() {
187 throw new UnavailabilityError('GLView', 'startARSessionAsync');
188 }
189 async createCameraTextureAsync() {
190 throw new UnavailabilityError('GLView', 'createCameraTextureAsync');
191 }
192 async destroyObjectAsync(glObject) {
193 throw new UnavailabilityError('GLView', 'destroyObjectAsync');
194 }
195}
196GLView.propTypes = propTypes;
197//# sourceMappingURL=GLView.web.js.map
\No newline at end of file