diff --git a/packages/react/package.json b/packages/react/package.json index 0dc1ef4..3e32cbb 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@wethegit/react-wtc-gl", - "version": "0.1.0", + "version": "0.1.1", "description": "React components and hooks for wtc-gl recipes.", "type": "module", "files": [ diff --git a/packages/react/src/scroll-renderer/scroll-renderer-provider.tsx b/packages/react/src/scroll-renderer/scroll-renderer-provider.tsx index 2b22ced..83515f5 100644 --- a/packages/react/src/scroll-renderer/scroll-renderer-provider.tsx +++ b/packages/react/src/scroll-renderer/scroll-renderer-provider.tsx @@ -39,7 +39,7 @@ const defaultCanvasStyle: CSSProperties = { position: 'fixed', inset: 0, width: '100%', - height: '100%', + height: '100lvh', pointerEvents: 'none', zIndex: 0 } diff --git a/packages/site/vite.config.js b/packages/site/vite.config.js index d66c9ce..be8ee7f 100644 --- a/packages/site/vite.config.js +++ b/packages/site/vite.config.js @@ -15,5 +15,6 @@ export default defineConfig({ '@wethegit/react-wtc-gl': resolvePath('../react/src/index.ts') }, dedupe: ['react', 'react-dom'] - } + }, + server: { host: true } }) diff --git a/packages/wtc-gl/package.json b/packages/wtc-gl/package.json index c0d24ea..3f4f0af 100644 --- a/packages/wtc-gl/package.json +++ b/packages/wtc-gl/package.json @@ -1,6 +1,6 @@ { "name": "wtc-gl", - "version": "1.3.0", + "version": "1.3.1", "description": "Typescript simple Web GL library.", "type": "module", "files": [ diff --git a/packages/wtc-gl/src/recipes/ScrollRenderer/ScrollRenderer.ts b/packages/wtc-gl/src/recipes/ScrollRenderer/ScrollRenderer.ts index d8c9884..6320c89 100644 --- a/packages/wtc-gl/src/recipes/ScrollRenderer/ScrollRenderer.ts +++ b/packages/wtc-gl/src/recipes/ScrollRenderer/ScrollRenderer.ts @@ -79,6 +79,7 @@ export class ScrollRenderer { #playing: boolean = false #ownsCanvas: boolean #cleared: boolean = false + #resizeObserver: ResizeObserver | null = null constructor({ rendererProps = {}, @@ -99,7 +100,10 @@ export class ScrollRenderer { this.render = this.render.bind(this) this.resize = this.resize.bind(this) - window.addEventListener('resize', this.resize) + if (typeof ResizeObserver !== 'undefined') { + this.#resizeObserver = new ResizeObserver(this.resize) + this.#resizeObserver.observe(this.canvas) + } this.resize() } @@ -109,19 +113,29 @@ export class ScrollRenderer { } /** - * Synchronises the GL canvas buffer size with the layout viewport. + * Synchronises the GL canvas buffer size with the canvas element's CSS size. * - * Uses `document.documentElement.clientWidth/clientHeight` rather than - * `window.innerWidth/innerHeight` because on systems with classic - * (non-overlay) scrollbars `innerWidth` includes the scrollbar gutter, - * while a `position:fixed; width:100%` canvas does not — causing every - * scissor rect to clip a few pixels short on the trailing edge. + * Measures the canvas (`clientWidth`/`clientHeight`) so the buffer + * can never disagree with how the element is laid out. Falls back to the + * document's client size when the canvas isn't in the DOM yet (or has no + * layout size), which also avoids the scrollbar-gutter offset that + * `window.innerWidth/innerHeight` would introduce. * - * Called automatically on construction and on every `resize` event. + * Called automatically on construction, and when canvas resizeObsever triggers + * and at the start of every rendered frame. */ resize() { - const el = document.documentElement - this.renderer.dimensions = new Vec2(el.clientWidth, el.clientHeight) + const canvas = this.canvas + let width = canvas.clientWidth + let height = canvas.clientHeight + if (!width || !height) { + const el = document.documentElement + width = el.clientWidth + height = el.clientHeight + } + const current = this.renderer.dimensions + if (current && current.width === width && current.height === height) return + this.renderer.dimensions = new Vec2(width, height) } /** @@ -177,6 +191,9 @@ export class ScrollRenderer { } this.#cleared = false + // Catch element size changes that haven't been observed yet + this.resize() + const { gl } = this const { dpr } = this.renderer const canvasWidth = this.renderer.dimensions.width * dpr @@ -268,7 +285,7 @@ export class ScrollRenderer { } /** - * Stops the render loop, removes the resize listener, and destroys all + * Stops the render loop, disconnects the resize observer, and destroys all * registered scenes (disconnecting their `IntersectionObserver`s). * * When the renderer created its own canvas, the WebGL context is also @@ -288,7 +305,8 @@ export class ScrollRenderer { */ destroy(): HTMLCanvasElement { this.playing = false - window.removeEventListener('resize', this.resize) + this.#resizeObserver?.disconnect() + this.#resizeObserver = null this.#scenes.forEach((s) => s.destroy()) this.#scenes = [] if (this.#ownsCanvas)