zuljaman-banner-components 1.1.21 → 1.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"useWebGLRenderer.d.ts","sourceRoot":"","sources":["../../../../src/components/BannerRenderer/webgl/useWebGLRenderer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5E,MAAM,MAAM,eAAe,GACvB,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAC5C,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,gBAAgB,CAAC,EAAE,eAAe,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAwBD,wBAAgB,gBAAgB,IAAI,OAAO,CAgB1C;AA+HD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,EACpD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM;;EAqMrB"}
1
+ {"version":3,"file":"useWebGLRenderer.d.ts","sourceRoot":"","sources":["../../../../src/components/BannerRenderer/webgl/useWebGLRenderer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5E,MAAM,MAAM,eAAe,GACvB,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAC5C,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,gBAAgB,CAAC,EAAE,eAAe,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAwBD,wBAAgB,gBAAgB,IAAI,OAAO,CAgB1C;AA+HD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,EACpD,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM;;EAsQrB"}
@@ -153,15 +153,22 @@ function useWebGLRenderer(canvasRef, imageUrl, effects, canvasWidth, canvasHeigh
153
153
  const renderFrameRef = (0, react_1.useRef)(null);
154
154
  const effectsRef = (0, react_1.useRef)(effects);
155
155
  effectsRef.current = effects;
156
+ // Keep the latest imageUrl reachable from inside the init/restore handlers
157
+ // (which run outside the imageUrl effect's closure).
158
+ const imageUrlRef = (0, react_1.useRef)(imageUrl);
159
+ imageUrlRef.current = imageUrl;
156
160
  // Track whether any effect needs animation (grain animated or refraction with animation)
157
161
  const needsAnimation = effects.grainAnimated && (((_a = effects.grainIntensity) !== null && _a !== void 0 ? _a : 0) > 0 ||
158
162
  ((_b = effects.refractionIntensity) !== null && _b !== void 0 ? _b : 0) > 0);
159
163
  // Load image as texture
160
- const loadTexture = (0, react_1.useCallback)((gl, url) => {
164
+ const loadTexture = (0, react_1.useCallback)((_gl, url) => {
161
165
  const img = new Image();
162
166
  img.crossOrigin = 'anonymous';
163
167
  img.onload = () => {
164
- if (!glRef.current)
168
+ // Re-read the current context — the one captured at call time may have
169
+ // been lost (and a new one restored) while the image was downloading.
170
+ const gl = glRef.current;
171
+ if (!gl)
165
172
  return;
166
173
  let texture = textureRef.current;
167
174
  if (!texture) {
@@ -226,41 +233,103 @@ function useWebGLRenderer(canvasRef, imageUrl, effects, canvasWidth, canvasHeigh
226
233
  gl.drawArrays(gl.TRIANGLES, 0, 6);
227
234
  }, []);
228
235
  renderFrameRef.current = renderFrame;
229
- // Initialize WebGL
236
+ // Initialize WebGL lazily.
237
+ //
238
+ // Defer context creation until the canvas enters the viewport, and also
239
+ // re-initialize when a previously-evicted context is needed again. Browsers
240
+ // cap simultaneous WebGL contexts per tab (~16 in Chrome) — pages that mount
241
+ // dozens of banners (e.g. the style gallery) would otherwise exhaust the
242
+ // pool and leave most canvases blank.
230
243
  (0, react_1.useEffect)(() => {
231
244
  const canvas = canvasRef.current;
232
245
  if (!canvas)
233
246
  return;
234
- const gl = canvas.getContext('webgl', {
235
- alpha: false,
236
- antialias: false,
237
- premultipliedAlpha: false,
238
- preserveDrawingBuffer: true, // Needed for toBlob() export
239
- });
240
- if (!gl) {
241
- console.warn('WebGL not available, falling back to CSS rendering');
242
- return;
247
+ const initGL = () => {
248
+ if (glRef.current)
249
+ return true; // already initialized
250
+ const gl = canvas.getContext('webgl', {
251
+ alpha: false,
252
+ antialias: false,
253
+ premultipliedAlpha: false,
254
+ preserveDrawingBuffer: true, // Needed for toBlob() export
255
+ });
256
+ if (!gl) {
257
+ console.warn('WebGL not available, falling back to CSS rendering');
258
+ return false;
259
+ }
260
+ glRef.current = gl;
261
+ const program = createProgram(gl);
262
+ if (!program)
263
+ return false;
264
+ programRef.current = program;
265
+ gl.useProgram(program);
266
+ buffersRef.current = setupGeometry(gl, program);
267
+ uniformsRef.current = getUniformLocations(gl, program);
268
+ if (imageUrlRef.current) {
269
+ imageLoadedRef.current = false;
270
+ loadTexture(gl, imageUrlRef.current);
271
+ }
272
+ return true;
273
+ };
274
+ // preventDefault on 'webglcontextlost' is required for 'webglcontextrestored' to fire.
275
+ const handleContextLost = (e) => {
276
+ e.preventDefault();
277
+ glRef.current = null;
278
+ programRef.current = null;
279
+ uniformsRef.current = null;
280
+ textureRef.current = null;
281
+ buffersRef.current = null;
282
+ imageLoadedRef.current = false;
283
+ };
284
+ const handleContextRestored = () => {
285
+ initGL();
286
+ };
287
+ canvas.addEventListener('webglcontextlost', handleContextLost);
288
+ canvas.addEventListener('webglcontextrestored', handleContextRestored);
289
+ let observer = null;
290
+ if (typeof IntersectionObserver === 'undefined') {
291
+ initGL();
292
+ }
293
+ else {
294
+ observer = new IntersectionObserver((entries) => {
295
+ for (const entry of entries) {
296
+ if (entry.isIntersecting) {
297
+ // (Re-)init when this canvas (re)enters view and lacks a live
298
+ // context — covers first appearance and contexts that were
299
+ // dropped while offscreen.
300
+ if (!glRef.current)
301
+ initGL();
302
+ }
303
+ else if (glRef.current) {
304
+ // Free the context slot when this canvas leaves the viewport,
305
+ // so visible banners on a long page (gallery view) can claim
306
+ // their own context. Triggers webglcontextlost → state cleared
307
+ // → re-init runs the next time the canvas re-enters view.
308
+ const ext = glRef.current.getExtension('WEBGL_lose_context');
309
+ if (ext)
310
+ ext.loseContext();
311
+ }
312
+ }
313
+ }, { rootMargin: '200px' });
314
+ observer.observe(canvas);
243
315
  }
244
- glRef.current = gl;
245
- const program = createProgram(gl);
246
- if (!program)
247
- return;
248
- programRef.current = program;
249
- gl.useProgram(program);
250
- buffersRef.current = setupGeometry(gl, program);
251
- uniformsRef.current = getUniformLocations(gl, program);
252
316
  return () => {
253
- // Cleanup all GPU resources
254
- if (textureRef.current)
255
- gl.deleteTexture(textureRef.current);
256
- if (buffersRef.current) {
257
- if (buffersRef.current.positionBuffer)
258
- gl.deleteBuffer(buffersRef.current.positionBuffer);
259
- if (buffersRef.current.texCoordBuffer)
260
- gl.deleteBuffer(buffersRef.current.texCoordBuffer);
317
+ observer === null || observer === void 0 ? void 0 : observer.disconnect();
318
+ canvas.removeEventListener('webglcontextlost', handleContextLost);
319
+ canvas.removeEventListener('webglcontextrestored', handleContextRestored);
320
+ const gl = glRef.current;
321
+ if (gl) {
322
+ if (textureRef.current)
323
+ gl.deleteTexture(textureRef.current);
324
+ if (buffersRef.current) {
325
+ if (buffersRef.current.positionBuffer)
326
+ gl.deleteBuffer(buffersRef.current.positionBuffer);
327
+ if (buffersRef.current.texCoordBuffer)
328
+ gl.deleteBuffer(buffersRef.current.texCoordBuffer);
329
+ }
330
+ if (programRef.current)
331
+ gl.deleteProgram(programRef.current);
261
332
  }
262
- if (programRef.current)
263
- gl.deleteProgram(programRef.current);
264
333
  textureRef.current = null;
265
334
  buffersRef.current = null;
266
335
  programRef.current = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuljaman-banner-components",
3
- "version": "1.1.21",
3
+ "version": "1.1.23",
4
4
  "description": "Shared banner components package for Next.js and AWS Lambda platforms",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",