@fluritheme/spider
Version:
A tiny, expressive spider for your website. Delicate webs that follow DOM layers, blinking eyes, and playful pointer reactions.
58 lines • 2.7 kB
JavaScript
export function elementMatrix(element, win) {
const r = element.getBoundingClientRect(), center = { x: r.left + r.width / 2, y: r.top + r.height / 2 };
let matrix = new DOMMatrix(), ancestor = element;
while (ancestor) {
const transform = win.getComputedStyle(ancestor).transform;
if (transform !== 'none') {
const m = new DOMMatrix(transform);
if (!m.is2D) {
matrix = new DOMMatrix();
break;
}
matrix = new DOMMatrix([m.a, m.b, m.c, m.d, 0, 0]).multiply(matrix);
}
ancestor = ancestor.parentElement;
}
const width = element.offsetWidth || r.width, height = element.offsetHeight || r.height;
matrix.e = center.x - (matrix.a * width) / 2 - (matrix.c * height) / 2;
matrix.f = center.y - (matrix.b * width) / 2 - (matrix.d * height) / 2;
return matrix;
}
/** Cache geometry, including 2D rotations inherited through nested stacking contexts. */
export function pageObjects(doc, win, selector) {
return [...doc.querySelectorAll(selector)].slice(0, 100).flatMap((element) => {
if (element.closest('[data-spider-ignore]') || !element.isConnected)
return [];
const style = win.getComputedStyle(element), r = element.getBoundingClientRect();
if (style.visibility !== 'visible' ||
Number(style.opacity) < 0.1 ||
r.width < 18 ||
r.height < 14 ||
r.bottom < 0 ||
r.top > win.innerHeight ||
r.right < 0 ||
r.left > win.innerWidth ||
r.width * r.height > win.innerWidth * win.innerHeight * 0.85)
return [];
const box = { x: r.left, y: r.top, width: r.width, height: r.height };
const center = { x: r.left + r.width / 2, y: r.top + r.height / 2 };
const matrix = elementMatrix(element, win);
const width = element.offsetWidth || r.width, height = element.offsetHeight || r.height;
const local = new Path2D();
const radius = Math.min(parseFloat(style.borderRadius) || 0, width / 2, height / 2);
local.roundRect(0, 0, width, height, radius);
const path = new Path2D();
path.addPath(local, matrix);
return [{ element, box, path, center }];
});
}
export function outsideObjects(ctx, objects, width, height) {
// Separate clipping operations preserve union semantics when opaque objects overlap.
for (const object of objects) {
const clip = new Path2D();
clip.rect(-200, -200, width + 400, height + 400);
clip.addPath(object.path);
ctx.clip(clip, 'evenodd');
}
}
//# sourceMappingURL=objects.js.map