From e85b8669e2a4f3b638c2798d225172c2b0e39a3c Mon Sep 17 00:00:00 2001 From: Philip Rogers Date: Fri, 7 Nov 2025 17:00:03 -0800 Subject: [PATCH] Switch to CSS transforms for hit testing --- Examples/complex-text.html | 8 ++------ Examples/text-input.html | 19 ++++--------------- Examples/webGL.html | 9 ++------- README.md | 7 +++---- 4 files changed, 11 insertions(+), 32 deletions(-) diff --git a/Examples/complex-text.html b/Examples/complex-text.html index f36ba96..42db6f1 100644 --- a/Examples/complex-text.html +++ b/Examples/complex-text.html @@ -11,7 +11,7 @@ -
+
Hello world!
I'm multi-line, formatted, rotated text with emoji (😀), RTL text من فارسی صحبت میکنم, @@ -41,11 +41,7 @@ ctx.rotate((15 * Math.PI) / 180); let x = 80 * devicePixelRatio; let y = -20 * devicePixelRatio; - if (typeof ctx.drawElementImage === 'function') { - ctx.drawElementImage(drawElement, x, y); - } else { - ctx.drawElement(drawElement, x, y); - } + ctx.drawElementImage(drawElement, x, y); }); // See: https://web.dev/articles/device-pixel-content-box observer.observe(canvas, {box: ['device-pixel-content-box']}); diff --git a/Examples/text-input.html b/Examples/text-input.html index 72e5e39..4430b17 100644 --- a/Examples/text-input.html +++ b/Examples/text-input.html @@ -53,21 +53,10 @@ function draw() { ctx.reset(); - let x = 30 * devicePixelRatio; - let y = 30 * devicePixelRatio; - if (typeof ctx.drawElementImage === 'function') { - ctx.drawElementImage(drawElement, x, y); - } else { - ctx.drawElement(drawElement, x, y); - } - rect = drawElement.getBoundingClientRect(); - ctx.setHitTestRegions([ - { - element: drawElement, - rect: {x: x, y: y, width: rect.width * devicePixelRatio, - height: rect.height * devicePixelRatio} - } - ]); + let x = 30; + let y = 30 + ctx.drawElementImage(drawElement, x * devicePixelRatio, y * devicePixelRatio); + drawElement.style.transform = `translate(${x}px, ${y}px)`; } onload = () => { diff --git a/Examples/webGL.html b/Examples/webGL.html index 4ff7ba7..8685845 100644 --- a/Examples/webGL.html +++ b/Examples/webGL.html @@ -23,7 +23,7 @@ -
+
Hello world!
I'm multi-line, formatted, rotated text with emoji (😀), RTL text من فارسی صحبت میکنم, @@ -60,13 +60,8 @@ const srcFormat = gl.RGBA; const srcType = gl.UNSIGNED_BYTE; - if (typeof gl.texElementImage2D === 'function') { - gl.texElementImage2D(gl.TEXTURE_2D, level, internalFormat, + gl.texElementImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, drawElement); - } else { - gl.texElement2D(gl.TEXTURE_2D, level, internalFormat, - srcFormat, srcType, drawElement); - } // Linear texture filtering produces better results than mipmap with text. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); diff --git a/README.md b/README.md index 24904d8..b3bd795 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,11 @@ There is no web API to easily render complex layouts of text and other content i * **Composing HTML Elements with Shaders.** A limited set of CSS shaders, such as filter effects, are already available, but there is a desire to use general WebGL shaders with HTML. * **HTML Rendering in a 3D Context.** 3D aspects of sites and games need to render rich 2D content into surfaces within a 3D scene. -## Proposed solution: `layoutsubtree`, `drawElementImage`/`texElementImage2D`, `fireOnEveryPaint`, and `setHitTestRegions` +## Proposed solution: `layoutsubtree`, `drawElementImage`/`texElementImage2D`, and `fireOnEveryPaint` -* The `layoutsubtree` attribute on a `` element allows its descendant elements to have layout (*), and causes the direct children of the `` to have a stacking context and become a containing block for all descendants. Descendant elements of the `` still do not paint or hit-test, and are not discovered by UA algorithms like find-in-page. +* The `layoutsubtree` attribute on a `` element allows its descendant elements to have layout (*), participate in hit testing, accessibility, etc, and causes the direct children of the `` to have a stacking context and become a containing block for all descendants. Descendant elements of the `` still do not paint. * The `CanvasRenderingContext2D.drawElementImage(element, x, y)` method renders `element` and its subtree into a 2D canvas at offset x and y, so long as `element` is a direct child of the ``. It has no effect if `layoutsubtree` is not specified on the ``. * The `WebGLRenderingContext.texElementImage2D(..., element)` method renders `element` into a WebGL texture. It has no effect if `layoutsubtree` is not specified on the ``. -* The `CanvasRenderingContext2D.setHitTestRegions([{element: ., rect: {x: x, y: y, width: ..., height: ...}, ...])` (and `WebGLRenderingContext.setHitTestRegions(...)`) API takes a list of elements and ``-relative rects indicating where the element paints relative to the backing buffer of the canvas. These rects are then used to redirect hit tests for mouse and touch events automatically from the `` element to the drawn element. (*) Without `layoutsubtree`, geometry APIs such as `getBoundingClientRect()` on these elements return an empty rect. They do have computed styles, however, and are keyboard-focusable. @@ -84,7 +83,7 @@ A demo of the same thing using an experimental extension of [three.js](https://t #### [See here](Examples/text-input.html) for an example of interactive content in canvas. -This example uses the `setHitTestRegions` API to forward input to a form element drawn with `drawElementImage`. The `fireOnEveryPaint` resize observer option is used to update the canvas as needed. The effect is a fully interactive form in canvas. +The `fireOnEveryPaint` resize observer option is used to update the canvas as needed. The effect is a fully interactive form in canvas. text-input