# core/evaluators/card.fragment.evaluator.ts

> 102 lines of code and 31 definitions.

Tree: Bane's Lab Social Share
Language: typescript
Layer: runtime
Canonical: https://banes-lab.com/anatomy/social-share#file-social-share-core-evaluators-card-fragment-evaluator-ts
Source text: https://banes-lab.com/source/social-share/core/evaluators/card.fragment.evaluator.ts.txt

Listed in [core/evaluators](https://banes-lab.com/api/source/social-share/core/evaluators.md), after [core/evaluators/card.evaluator.ts](https://banes-lab.com/source/social-share/core/evaluators/card.evaluator.ts.md) and before [core/evaluators/page.evaluator.ts](https://banes-lab.com/source/social-share/core/evaluators/page.evaluator.ts.md).

## Definitions

- `slotAt` (lexical_declaration, line 87, exported)
- `orientationOf` (lexical_declaration, line 83, exported)
- `placeAt` (lexical_declaration, line 99, exported)
- `clamp` (lexical_declaration, line 33, exported)
- `squareHeight` (lexical_declaration, line 95, exported)
- `fontAt` (lexical_declaration, line 115, exported)
- `between` (lexical_declaration, line 49, exported)
- `grown` (lexical_declaration, line 104, exported)
- `alignAt` (lexical_declaration, line 119, exported)
- `shape` (lexical_declaration, line 42, exported)
- `wave` (lexical_declaration, line 53, exported)
- `pixels` (lexical_declaration, line 66, exported)
- `ease` (lexical_declaration, line 41, exported)
- `stagger` (lexical_declaration, line 57, exported)
- `CUBE` (lexical_declaration, line 14)
- `HALF` (lexical_declaration, line 15)
- `BACK` (lexical_declaration, line 16)
- `TURN` (lexical_declaration, line 17)
- `NOISE_A` (lexical_declaration, line 18)
- `NOISE_B` (lexical_declaration, line 19)
- `NOISE_SCALE` (lexical_declaration, line 20)
- `PIXEL_PRECISION` (lexical_declaration, line 21)
- `PIXEL_UNIT` (lexical_declaration, line 22)
- `CURVES` (lexical_declaration, line 24)
- `lerp` (lexical_declaration, line 37, exported)
- `noise` (lexical_declaration, line 61, exported)
- `value` (lexical_declaration, line 62, exported)
- `pixelValue` (lexical_declaration, line 70, exported)
- `bare` (lexical_declaration, line 71, exported)
- `percent` (lexical_declaration, line 75, exported)
- `swing` (lexical_declaration, line 79, exported)

## Contained in

- [core/evaluators](https://banes-lab.com/anatomy/social-share/folder-social-share-core-evaluators.md)

## Used by

- [core/factories/field.factory.ts](https://banes-lab.com/source/social-share/core/factories/field.factory.ts.md)
- [core/factories/page.factory.ts](https://banes-lab.com/source/social-share/core/factories/page.factory.ts.md)

## Linked from

- [core/evaluators](https://banes-lab.com/anatomy/social-share/folder-social-share-core-evaluators.md)
- [core/factories](https://banes-lab.com/anatomy/social-share/folder-social-share-core-factories.md)

## Source

```typescript
import type {
    Curve,
    FrameContext,
    LayoutSlot,
    Orientation,
    OrientedLayout,
    Placement,
    SlotOptions,
    TextAlign,
} from "#types/card.types";
import { DEFAULT_ALIGN, WIDE_RATIO } from "#configuration/constants/card.constants";
import { SUBJECT_SEPARATOR, UNKNOWN_CURVE } from "#configuration/strings/card.strings";

const CUBE = 3;
const HALF = 0.5;
const BACK = 1.70158;
const TURN = Math.PI * 2;
const NOISE_A = 12.9898;
const NOISE_B = 78.233;
const NOISE_SCALE = 43_758.5453;
const PIXEL_PRECISION = 100;
const PIXEL_UNIT = "px";

const CURVES: ReadonlyMap<Curve, (x: number) => number> = new Map<Curve, (x: number) => number>([
    ["linear", (x) => x],
    ["inCubic", (x) => x ** CUBE],
    ["outCubic", (x) => 1 - (1 - x) ** CUBE],
    ["inOutCubic", (x) => (x < HALF ? 4 * x ** CUBE : 1 - (-2 * x + 2) ** CUBE / 2)],
    ["inOutSine", (x) => -(Math.cos(Math.PI * x) - 1) / 2],
    ["outBack", (x) => 1 + (BACK + 1) * (x - 1) ** CUBE + BACK * (x - 1) ** 2],
]);

export const clamp = function clamp(value: number, low = 0, high = 1): number {
    return Math.min(high, Math.max(low, value));
};

export const lerp = function lerp(from: number, to: number, amount: number): number {
    return from + (to - from) * amount;
};

export const ease = function ease(progress: number, curve: Curve = "inOutCubic"): number {
    const shape = CURVES.get(curve);
    if (shape === undefined) {
        throw new Error(UNKNOWN_CURVE + SUBJECT_SEPARATOR + curve);
    }
    return shape(clamp(progress));
};

export const between = function between(progress: number, start: number, end: number): number {
    return end <= start ? Number(progress >= end) : clamp((progress - start) / (end - start));
};

export const wave = function wave(progress: number, cycles = 1, phase = 0): number {
    return (Math.sin((progress * cycles + phase) * TURN) + 1) / 2;
};

export const stagger = function stagger(progress: number, index: number, step: number, span: number): number {
    return between(progress, index * step, index * step + span);
};

export const noise = function noise(seed: number, x = 0): number {
    const value = Math.sin(seed * NOISE_A + x * NOISE_B) * NOISE_SCALE;
    return value - Math.floor(value);
};

export const pixels = function pixels(value: number): string {
    return `${String(Math.round(value * PIXEL_PRECISION) / PIXEL_PRECISION)}${PIXEL_UNIT}`;
};

export const pixelValue = function pixelValue(text: string): number {
    const bare = (text.endsWith(PIXEL_UNIT) ? text.slice(0, -PIXEL_UNIT.length) : text).trim();
    return bare === "" ? Number.NaN : Number(bare);
};

export const percent = function percent(value: number): string {
    return `${String(value)}%`;
};

export const swing = function swing(progress: number, cycles = 1, phase = 0): number {
    return Math.sin((progress * cycles + phase) * TURN);
};

export const orientationOf = function orientationOf(frame: FrameContext): Orientation {
    return frame.profile.width / frame.profile.height >= WIDE_RATIO ? "wide" : "square";
};

export const slotAt = function slotAt<K extends string>(
    layout: OrientedLayout<K>,
    key: K,
    frame: FrameContext,
): LayoutSlot {
    return layout[orientationOf(frame)][key];
};

export const squareHeight = function squareHeight(width: number, frame: FrameContext): number {
    return (width * frame.profile.width) / frame.profile.height;
};

export const placeAt = function placeAt<K extends string>(
    layout: OrientedLayout<K>,
    key: K,
    options: SlotOptions = {},
): Placement {
    const grown = (frame: FrameContext): number => slotAt(layout, key, frame).width * (options.grow?.(frame) ?? 1);
    return {
        anchor: (frame) => slotAt(layout, key, frame).anchor,
        height: (frame) =>
            options.square === true ? squareHeight(grown(frame), frame) : (slotAt(layout, key, frame).height ?? 0),
        width: grown,
        x: (frame) => slotAt(layout, key, frame).x,
        y: (frame) => slotAt(layout, key, frame).y + (options.lift?.(frame) ?? 0),
    };
};

export const fontAt = function fontAt<K extends string>(layout: OrientedLayout<K>, key: K) {
    return (frame: FrameContext): string => pixels(frame.profile.width * (slotAt(layout, key, frame).size ?? 0));
};

export const alignAt = function alignAt<K extends string>(layout: OrientedLayout<K>, key: K) {
    return (frame: FrameContext): TextAlign => slotAt(layout, key, frame).align ?? DEFAULT_ALIGN;
};
```
