actual-client-rect
v0.2.2
Published
gets an element's actual client rect with transform. ALPHA
Downloads
11
Maintainers
Readme
getActualClientRect
ALPHA
npm i actual-client-rect --save
Problem
It was 2023 and web developers still didn't have any good options for obtaining the position and shape of an element on a page.
- People often use getBoundingClientRect(), which by nature obscures any transforms affecting the element.
- HTMLElement's offset api is annoying to work with, and the specs require the values to be rounded to the nearest pixel.
- Some libraries may have solved this problem internally, but to my knowledge no library exists that provides a direct solution, until now.
Solution
getActualClientRect() returns the element's basis DOMRect relative to the viewport, its computed transform origin, and its accumulated transform in 3 formats: css transform string, css transform's matrix3d substring, and a glMatrix mat4
API / Types / Documentation
export function getActualClientRect(element: HTMLElement, options?: ACROptions): ActualClientRect;
export type ActualClientRect = {
basis: DOMRect;
transformOrigin: string;
transform: string; // `matrix3d(${matrix3d})`
matrix3d: string;
transformMat4: mat4; // row-major formatted array
};
export type ACROptions = {
// optimal for animations. sometimes causes subpixel differences due to
// rendering differences between offsets and transforms
bakePositionIntoTransform?: boolean;
// return the transform relative to this origin on the element,
// rather than the element's own origin.
useTransformOrigin?: string;
};
Example.svelte
(getActualClientRect does not require Svelte, but Svelte rocks!)
<script lang="ts">
import { onMount } from 'svelte';
import { getActualClientRect } from 'actual-client-rect';
let baseElement: HTMLElement;
let matchingElement: HTMLElement;
onMount(() => {
match();
});
function match(): void {
const acr = getActualClientRect(baseElement);
matchingElement.style.left = `${acr.basis.left}px`;
matchingElement.style.top = `${acr.basis.top}px`;
matchingElement.style.width = `${acr.basis.width}px`;
matchingElement.style.height = `${acr.basis.height}px`;
matchingElement.style.transformOrigin = acr.transformOrigin;
matchingElement.style.transform = acr.transform;
}
</script>
<div bind:this={baseElement} class="base" />
<div bind:this={matchingElement} class="matching" />
<style lang="scss">
.base, .matching {
outline: solid 2px;
}
.base {
position: relative;
top: 5em;
left: 5em;
width: 5em;
height: 5em;
transform: rotate(15deg);
color: yellow;
}
.matching {
position: fixed; // absolute works if offset parent is flush with viewport
color: green;
}
</style>
Limitations
- getActualClientRect will not attempt to match, emulate, or mitigate bugs in rendering engines
- stackoverflow: -webkit-transform-style: preserve-3d not working
- some engines don't follow the preserve-3d used value specs, and still use preserve-3d even when combined with certain grouping properties:
- Chrome v123 / Blink -- contain: strict | content | paint, content-visibility: auto
- Firefox v124 / Gecko -- will-change: filter
- Safari v17.4 / Webkit -- will-change: filter | opacity
- (Properties not yet supported by particular browsers omitted from their respective lists)
- gACR performance has not yet been profiled
Contribute
All contributions are greatly appreciated!
- Feedback, feature requests, and help requests can be posted to the Projectrix Discord
- If you find a bug, please file an issue
- Join my Patreon
<3 anxpara