0.2.2 • Published 1 month ago

actual-client-rect v0.2.2

Weekly downloads
-
License
MIT
Repository
github
Last release
1 month ago

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>

Example.svelte render

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!

<3 anxpara

0.2.2

1 month ago

0.2.1

2 months ago

0.2.0

3 months ago

0.1.0

6 months ago

0.0.1

7 months ago