All files / svg-transform apply-svg-transforms.ts

100% Statements 15/15
100% Branches 0/0
100% Functions 4/4
100% Lines 15/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39                                    26x 26x 32x 31x   26x 31x 31x 34x 34x 34x 14x   34x 34x 34x   31x      
import { createMatrix, getOrderState, getOwnerSvg, getScreenMatrix } from './internal';
import type { SvgTransformOrder } from './svg-transform-order';
 
/**
 * Applies orders to an SVG element, in order, each on what the previous ones give: one transform per order
 * is added at the end of its `transform` list. Changing an order later with `set` updates its own transform
 * only, from what the element shows without it.
 *
 * @param element - A rendered SVG element.
 * @param orders - The orders, such as `svgRotateTo(0)`, `svgPlaceOn(target)`, `svgRotateBy(0)`.
 * @throws {TypeError} When the element is not rendered or not inside an `<svg>`, or an order was not created
 * by an order function.
 * @example
 * const rotation = svgRotateBy(0);
 * applySvgTransforms(needle, [svgPlaceOn(hub, 'center', 'bottom'), rotation]);
 * rotation.set(45); // at each frame: 45° more than its first position
 */
export function applySvgTransforms(element: SVGGraphicsElement, orders: readonly SvgTransformOrder<never>[]): void {
  const svg = getOwnerSvg(element);
  const steps = orders
    .map((order) => getOrderState(order))
    .map((state) => ({ state, transform: element.transform.baseVal.appendItem(svg.createSVGTransform()) }));
 
  for (const [index, { state, transform }] of steps.entries()) {
    const later = steps.slice(index + 1);
    state.update = (): void => {
      transform.setMatrix(createMatrix(element));
      let after = createMatrix(element);
      for (const next of later) {
        after = after.multiply(next.transform.matrix);
      }
      const screen = getScreenMatrix(element);
      const change = state.toChange(element, screen);
      transform.setMatrix(after.multiply(screen.inverse()).multiply(change).multiply(screen).multiply(after.inverse()));
    };
    state.update();
  }
}