Files

1 line
56 KiB
Plaintext
Raw Permalink Normal View History

{"version":3,"file":"start.mjs","sources":["../../../src/view/start.ts"],"sourcesContent":["import { secondsToMilliseconds, warnOnce } from \"motion-utils\"\nimport { GroupAnimation } from \"../animation/GroupAnimation\"\nimport { NativeAnimation } from \"../animation/NativeAnimation\"\nimport { NativeAnimationWrapper } from \"../animation/NativeAnimationWrapper\"\nimport { AnimationPlaybackControls, DOMKeyframesDefinition } from \"../animation/types\"\nimport { getValueTransition } from \"../animation/utils/get-value-transition\"\nimport { mapEasingToNativeEasing } from \"../animation/waapi/easing/map-easing\"\nimport { applyGeneratorOptions } from \"../animation/waapi/utils/apply-generator\"\nimport { cornerRadiusProps } from \"../utils/border-radius\"\nimport { ElementOrSelector, resolveElements } from \"../utils/resolve-elements\"\nimport type { ViewTransitionBuilder } from \"./index\"\nimport { ViewTransitionTarget, ViewTransitionTargetDefinition } from \"./types\"\nimport {\n assignViewTransitionNames,\n releaseViewTransitionNames,\n} from \"./utils/assign-names\"\nimport { chooseLayerType } from \"./utils/choose-layer-type\"\nimport { css } from \"./utils/css\"\nimport { getViewAnimationLayerInfo } from \"./utils/get-layer-info\"\nimport { getViewAnimations } from \"./utils/get-view-animations\"\nimport { hasTarget } from \"./utils/has-target\"\n\nconst definitionNames = [\"layout\", \"enter\", \"exit\", \"new\", \"old\"] as const\n\ntype CornerRadii = Record<(typeof cornerRadiusProps)[number], string>\n\n/**\n * Whether a computed border-radius is square (every component zero). Splitting\n * on whitespace handles two-value/elliptical radii like \"0px 20px\" - a leading\n * `parseFloat` alone would misread the non-zero vertical radius as square.\n */\nconst isSquareRadius = (radius: string) =>\n radius.split(\" \").every((value) => parseFloat(value) === 0)\n\n/**\n * The `ViewTransitionTarget` buckets driving each generated layer type, in\n * priority order - the inverse of `chooseLayerType`. The new view is driven by\n * `new`/`enter`, the old by `old`/`exit`. `group-children`/`image-pair` have no\n * bucket; they follow the default layout timing.\n */\nconst typeBuckets: Record<string, (keyof ViewTransitionTarget)[]> = {\n group: [\"layout\"],\n new: [\"new\", \"enter\"],\n old: [\"old\", \"exit\"],\n}\n\n/**\n * Default \"absent\" origin for a single-value keyframe, by pseudo type, so e.g.\n * `enter({ scale: 1 })` grows in from 0.85 and `exit({ opacity: 0 })` fades\n * from 1. `enter` prefers the matching `exit` value over these (see below).\n */\nconst ORIGIN_DEFAULTS: Record<string, Record<string, number>> = {\n new: { opacity: 0, scale: 0.85 },\n old: { opacity: 1, scale: 1 },\n}\n\n/**\n * How much two box aspect ratios must differ before a morph is treated as\n * aspect-changing (and so worth cropping). Matches the projection engine's\n * `preserve-aspect` threshold, so small layout jitter doesn't trigger a crop.\n */\nconst ASPECT_TOLERANCE = 0.2\n\nexport function startViewAnimation(\n builder: ViewTransitionBuilder\n): Promise<GroupAnimation> {\n const {\n update,\n targets,\n resolveDefs,\n cropOverride,\n pairs,\n classNames,\n flatGroups,\n options: defaultOptions,\n } = builder\n\n if (!document.startViewTransition) {\n // An async IIFE (not `new Promise(async …)`) so a throwing/rejecting\n // update rejects this promise rather than leaving it unsettled.\n return (async () => {\n await update()\n return new GroupAnimation([])\n })()\n }\n\n /**\n * Resolve any selector/Element targets to layer names, assigning a\n * `view-transition-name` to each element as we go. We run this before the\n * update (so the elements are captured in the old snapshot) and again\n * after it (for the new snapshot). An element present in both keeps the\n * same name and animates as a single `group` layer.\n */\n const nameRegistry = new Map<Eleme