mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(cli): check no longer treats a transparent canvas as covering the frame (#6681)
* fix(cli): check no longer treats a transparent canvas as covering the frame * fix(cli): a canvas with an opaque CSS background still counts as covering
This commit is contained in:
@@ -1276,6 +1276,33 @@
|
||||
// An element hides text beneath it when it paints opaque pixels at near-full
|
||||
// opacity: raster content (img/video/canvas), a background image, or a solid
|
||||
// background colour. Low-opacity overlays (grain, scrims) do not occlude.
|
||||
const canvasProbe = document.createElement("canvas");
|
||||
canvasProbe.width = 1;
|
||||
canvasProbe.height = 1;
|
||||
|
||||
// The drawn alpha under the point, or null when the canvas cannot be read (then it counts as opaque).
|
||||
function canvasAlphaAt(element, x, y) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
if (rect.width <= 0 || rect.height <= 0 || !element.width || !element.height) return null;
|
||||
const px = Math.min(
|
||||
element.width - 1,
|
||||
Math.max(0, Math.floor(((x - rect.left) / rect.width) * element.width)),
|
||||
);
|
||||
const py = Math.min(
|
||||
element.height - 1,
|
||||
Math.max(0, Math.floor(((y - rect.top) / rect.height) * element.height)),
|
||||
);
|
||||
try {
|
||||
const context = canvasProbe.getContext("2d", { willReadFrequently: true });
|
||||
if (!context) return null;
|
||||
context.clearRect(0, 0, 1, 1);
|
||||
context.drawImage(element, px, py, 1, 1, 0, 0, 1, 1);
|
||||
return context.getImageData(0, 0, 1, 1).data[3] / 255;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isOpaqueOccluder(element, x, y) {
|
||||
const opacity = opacityChain(element);
|
||||
if (opacity < OCCLUDING_ALPHA) return false;
|
||||
@@ -1284,6 +1311,11 @@
|
||||
const alpha = imageAlphaAt(element, x, y);
|
||||
if (alpha !== null) return alpha * opacity >= OCCLUDING_ALPHA;
|
||||
}
|
||||
if (element.tagName === "CANVAS") {
|
||||
const alpha = canvasAlphaAt(element, x, y);
|
||||
if (alpha !== null)
|
||||
return alpha * opacity >= OCCLUDING_ALPHA || hasOpaqueBackground(getComputedStyle(element));
|
||||
}
|
||||
const matte = matteFrameFor(element);
|
||||
if (matte) return matteOccludesAt(matte, x, y);
|
||||
if (RASTER_TAGS.has(element.tagName)) return true;
|
||||
|
||||
@@ -427,6 +427,34 @@ describe.runIf(executablePath)("layout audit in Chromium", () => {
|
||||
expect(codes.includes("content_overlap")).toBe(overlap);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ name: "transparent", fill: "", css: "", covered: false },
|
||||
{
|
||||
name: "opaque",
|
||||
fill: "ctx.fillStyle='#123';ctx.fillRect(0,0,1920,1080);",
|
||||
css: "",
|
||||
covered: true,
|
||||
},
|
||||
{
|
||||
name: "glyph-only with an opaque CSS background",
|
||||
fill: "",
|
||||
css: "background:#112233;",
|
||||
covered: true,
|
||||
},
|
||||
])(
|
||||
"judges a full-frame $name canvas over a caption by what it draws",
|
||||
async ({ fill, css, covered }) => {
|
||||
const codes = await auditCodes(`
|
||||
<div data-composition-id="main" data-width="1920" data-height="1080" style="position:relative;width:1920px;height:1080px">
|
||||
<div style="position:absolute;inset:0;background:#0b0d12"></div>
|
||||
<p style="position:absolute;left:100px;top:100px;margin:0;font:64px Arial;color:#fff">Caption</p>
|
||||
<canvas id="c" width="1920" height="1080" style="position:absolute;inset:0;width:1920px;height:1080px;z-index:2;${css}"></canvas>
|
||||
<script>const ctx=document.getElementById("c").getContext("2d");${fill}ctx.fillStyle="#fff";ctx.font="64px Arial";ctx.fillText("Drawn",900,900);</script>
|
||||
</div>`);
|
||||
expect(codes.includes("canvas_covered")).toBe(covered);
|
||||
},
|
||||
);
|
||||
|
||||
describe("craft sampling", () => {
|
||||
async function sample(body: string) {
|
||||
const page = await browser.newPage();
|
||||
|
||||
Reference in New Issue
Block a user