mysticsymbolic.github.io/lib/svg-transform.tsx

96 wiersze
2.4 KiB
TypeScript

import React from "react";
import { Point } from "../vendor/bezier-js";
import { reversePoint } from "./point";
export type SvgTransform =
| {
kind: "translate";
amount: Point;
}
| {
kind: "rotate";
degrees: number;
}
| {
kind: "scale";
amount: Point;
}
| {
kind: "transformOrigin";
amount: Point;
transforms: SvgTransform[];
};
function getSvgCodeForTransform(t: SvgTransform): string {
switch (t.kind) {
case "translate":
return `translate(${t.amount.x} ${t.amount.y})`;
case "scale":
return `scale(${t.amount.x} ${t.amount.y})`;
case "rotate":
return `rotate(${t.degrees})`;
case "transformOrigin":
/**
* We originally used the SVG "transform-origin" attribute here but
* that's not currently supported by Safari. Instead, we'll set the origin
* of our SVG to the transform origin, do the transform, and then move our
* origin back to the original origin, which does the same thing.
**/
return getSvgCodeForTransforms([
svgTranslate(t.amount),
...t.transforms,
svgTranslate(reversePoint(t.amount)),
]);
}
}
function getSvgCodeForTransforms(transforms: SvgTransform[]): string {
return transforms.map(getSvgCodeForTransform).join(" ");
}
/**
* Apply the given SVG transforms (e.g. rotate, scale)
* centered at the given origin point.
*/
export function svgTransformOrigin(
amount: Point,
transforms: SvgTransform[]
): SvgTransform {
return { kind: "transformOrigin", amount, transforms };
}
export function svgTranslate(amount: Point): SvgTransform {
return { kind: "translate", amount };
}
export function svgScale(amount: Point | number): SvgTransform {
if (typeof amount === "number") {
amount = { x: amount, y: amount };
}
return { kind: "scale", amount };
}
export function svgRotate(degrees: number): SvgTransform {
return { kind: "rotate", degrees };
}
/**
* Creates a SVG `<g>` element with the given children and transforms.
*
* Like the SVG `transform` attribute, the transforms are applied in
* the *reverse* order that they are specified.
*/
export const SvgTransform: React.FC<{
transform: SvgTransform[] | SvgTransform;
children: any;
}> = ({ transform, children }) => {
if (!Array.isArray(transform)) {
transform = [transform];
}
return <g transform={getSvgCodeForTransforms(transform)}>{children}</g>;
};