Skip to content

svgRotateTo function ​

An order of applySvgTransforms that turns the element to an absolute angle on screen, clockwise from upright, around an anchor of itself or of another element: svgRotateTo(0) straightens it.

ts
import { svgRotateTo } from 'utils';

Signature ​

ts
export function svgRotateTo(
  angleDegrees: number,
  anchor: Anchor = 'center',
  reference?: SVGGraphicsElement,
): SvgTransformOrder<[angleDegrees: number, anchor?: Anchor, reference?: SVGGraphicsElement]>

Parameters ​

NameDescription
angleDegreesThe angle on screen, in degrees.
anchorThe point that stays in place. Defaults to 'center'.
referenceThe element giving the anchor, in any group. Defaults to the element itself.

Returns ​

The order, to change with set(angleDegrees, anchor, reference).

Example ​

ts
applySvgTransforms(label, [svgRotateTo(0)]); // upright, whatever its groups

Tests ​

Line coverage: 100 %.

  • ✅ straightens rotate(30 50 50) in place with 0
  • ✅ straightens rotate(30 50 50) scale(-1 1) translate(-100 0) in place with 0
  • ✅ turns to an absolute angle around an anchor
  • ✅ turns around an anchor of another element

Source ​

src/svg-transform/svg-rotate-to.ts (on GitHub)

Uses ​

  • Anchor: One of the 9 points of a box: its corners, the middles of its sides, its center.
  • getSvgAnchorPoint: Finds an anchor of the box an SVG element takes on screen: a corner, the middle of a side or the center.
  • SvgTransformOrder: An order of applySvgTransforms, created by svgRotateBy, svgRotateTo, svgFlipTo, svgScaleBy, svgTranslateBy or svgPlaceOn: keep it to change its values later with set.

Files it needs ​

src/svg-transform/svg-rotate-to.ts imports, directly or not:

Source code
ts
import type { Anchor } from './anchor';
import { getSvgAnchorPoint } from './get-svg-anchor-point';
import { createMatrix, createOrder, isMirrored } from './internal';
import type { SvgTransformOrder } from './svg-transform-order';

const HALF_TURN = 180;
const DEGREES_PER_RADIAN = HALF_TURN / Math.PI;

/**
 * An order of `applySvgTransforms` that turns the element to an absolute angle on screen, clockwise from
 * upright, around an anchor of itself or of another element: `svgRotateTo(0)` straightens it.
 *
 * @param angleDegrees - The angle on screen, in degrees.
 * @param anchor - The point that stays in place. Defaults to `'center'`.
 * @param reference - The element giving the anchor, in any group. Defaults to the element itself.
 * @returns The order, to change with `set(angleDegrees, anchor, reference)`.
 * @example
 * applySvgTransforms(label, [svgRotateTo(0)]); // upright, whatever its groups
 */
export function svgRotateTo(
  angleDegrees: number,
  anchor: Anchor = 'center',
  reference?: SVGGraphicsElement,
): SvgTransformOrder<[angleDegrees: number, anchor?: Anchor, reference?: SVGGraphicsElement]> {
  return createOrder([angleDegrees, anchor, reference], (element, screen, angle, point, other) => {
    const sign = isMirrored(screen) ? -1 : 1;
    const current = Math.atan2(sign * screen.b, sign * screen.a) * DEGREES_PER_RADIAN;
    const { x, y } = getSvgAnchorPoint(other ?? element, point);
    return createMatrix(element)
      .translate(x, y)
      .rotate(angle - current)
      .translate(-x, -y);
  });
}
Test file
ts
import { applySvgTransforms } from './apply-svg-transforms';
import { svgRotateTo } from './svg-rotate-to';
import { describeOnScreen, renderSvg } from './testing';

describe(svgRotateTo, () => {
  it.for([
    ['rotate(30 50 50)', false],
    ['rotate(30 50 50) scale(-1 1) translate(-100 0)', true],
  ] as const)('straightens %s in place with 0', ([transform, isFlipped]) => {
    const byId = renderSvg(
      `<g transform="rotate(20 50 50)"><rect id="symbol" transform="${transform}" x="40" y="45" width="20" height="10" /></g>`,
    );
    applySvgTransforms(byId('symbol'), [svgRotateTo(0)]);
    expect(describeOnScreen(byId('symbol'))).toMatchObject({
      x: 40,
      y: 45,
      width: 20,
      height: 10,
      rotation: 0,
      isFlipped,
    });
  });

  it('turns to an absolute angle around an anchor', () => {
    const byId = renderSvg('<rect id="flag" transform="rotate(45 40 40)" x="40" y="40" width="20" height="10" />');
    applySvgTransforms(byId('flag'), [svgRotateTo(90, 'top-left')]);
    expect(describeOnScreen(byId('flag'))).toMatchObject({ rotation: 90 });
  });

  it('turns around an anchor of another element', () => {
    const byId = renderSvg(
      '<rect id="needle" x="98" y="60" width="4" height="40" /><circle id="hub" cx="100" cy="100" r="5" />',
    );
    applySvgTransforms(byId('needle'), [svgRotateTo(90, 'center', byId('hub'))]);
    expect(describeOnScreen(byId('needle'))).toMatchObject({ x: 100, y: 98, width: 40, height: 4, rotation: 90 });
  });
});

Keywords: svg, rotate, to, svg transforms, function

Generated from the JSDoc of the sources.