framer-motion
Version:
A simple and powerful JavaScript animation library
1 lines • 8.93 kB
Source Map (JSON)
{"version":3,"file":"use-transform.mjs","sources":["../../../src/value/use-transform.ts"],"sourcesContent":["\"use client\"\n\nimport {\n AnyResolvedKeyframe,\n MotionValue,\n transform,\n TransformOptions,\n} from \"motion-dom\"\nimport { useConstant } from \"../utils/use-constant\"\nimport { useCombineMotionValues } from \"./use-combine-values\"\nimport { useComputed } from \"./use-computed\"\n\nexport type InputRange = number[]\ntype SingleTransformer<I, O> = (input: I) => O\ntype MultiTransformer<I, O> = (input: I[]) => O\ntype Transformer<I, O> =\n | SingleTransformer<I, O>\n /**\n * Ideally, this would be typed <I, O> in all instances, but to type this\n * more accurately requires the tuple support in TypeScript 4:\n * https://gist.github.com/InventingWithMonster/c4d23752a0fae7888596c4ff6d92733a\n */\n | MultiTransformer<AnyResolvedKeyframe, O>\n\ninterface OutputMap<O> {\n [key: string]: O[]\n}\n\n/**\n * Create multiple `MotionValue`s that transform the output of another `MotionValue` by mapping it from one range of values into multiple output ranges.\n *\n * @remarks\n *\n * This is useful when you want to derive multiple values from a single input value.\n * The keys of the output map must remain constant across renders.\n *\n * ```jsx\n * export const MyComponent = () => {\n * const x = useMotionValue(0)\n * const { opacity, scale } = useTransform(x, [0, 100], {\n * opacity: [0, 1],\n * scale: [0.5, 1]\n * })\n *\n * return (\n * <motion.div style={{ opacity, scale, x }} />\n * )\n * }\n * ```\n *\n * @param inputValue - `MotionValue`\n * @param inputRange - A linear series of numbers (either all increasing or decreasing)\n * @param outputMap - An object where keys map to output ranges. Each output range must be the same length as `inputRange`.\n * @param options - Transform options applied to all outputs\n *\n * @returns An object with the same keys as `outputMap`, where each value is a `MotionValue`\n *\n * @public\n */\nexport function useTransform<T extends Record<string, any[]>>(\n inputValue: MotionValue<number>,\n inputRange: InputRange,\n outputMap: T,\n options?: TransformOptions<T[keyof T][number]>\n): { [K in keyof T]: MotionValue<T[K][number]> }\n\n/**\n * Create a `MotionValue` that transforms the output of another `MotionValue` by mapping it from one range of values into another.\n *\n * @remarks\n *\n * Given an input range of `[-200, -100, 100, 200]` and an output range of\n * `[0, 1, 1, 0]`, the returned `MotionValue` will:\n *\n * - When provided a value between `-200` and `-100`, will return a value between `0` and `1`.\n * - When provided a value between `-100` and `100`, will return `1`.\n * - When provided a value between `100` and `200`, will return a value between `1` and `0`\n *\n *\n * The input range must be a linear series of numbers. The output range\n * can be any value type supported by Motion: numbers, colors, shadows, etc.\n *\n * Every value in the output range must be of the same type and in the same format.\n *\n * ```jsx\n * export const MyComponent = () => {\n * const x = useMotionValue(0)\n * const xRange = [-200, -100, 100, 200]\n * const opacityRange = [0, 1, 1, 0]\n * const opacity = useTransform(x, xRange, opacityRange)\n *\n * return (\n * <motion.div\n * animate={{ x: 200 }}\n * style={{ opacity, x }}\n * />\n * )\n * }\n * ```\n *\n * @param inputValue - `MotionValue`\n * @param inputRange - A linear series of numbers (either all increasing or decreasing)\n * @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`.\n * @param options -\n *\n * - clamp: boolean. Clamp values to within the given range. Defaults to `true`\n * - ease: EasingFunction[]. Easing functions to use on the interpolations between each value in the input and output ranges. If provided as an array, the array must be one item shorter than the input and output ranges, as the easings apply to the transition between each.\n *\n * @returns `MotionValue`\n *\n * @public\n */\nexport function useTransform<I, O>(\n value: MotionValue<number>,\n inputRange: InputRange,\n outputRange: O[],\n options?: TransformOptions<O>\n): MotionValue<O>\n\n/**\n * Create a `MotionValue` that transforms the output of another `MotionValue` through a function.\n * In this example, `y` will always be double `x`.\n *\n * ```jsx\n * export const MyComponent = () => {\n * const x = useMotionValue(10)\n * const y = useTransform(x, value => value * 2)\n *\n * return <motion.div style={{ x, y }} />\n * }\n * ```\n *\n * @param input - A `MotionValue` that will pass its latest value through `transform` to update the returned `MotionValue`.\n * @param transform - A function that accepts the latest value from `input` and returns a new value.\n * @returns `MotionValue`\n *\n * @public\n */\nexport function useTransform<I, O>(\n input: MotionValue<I>,\n transformer: SingleTransformer<I, O>\n): MotionValue<O>\n\n/**\n * Pass an array of `MotionValue`s and a function to combine them. In this example, `z` will be the `x` multiplied by `y`.\n *\n * ```jsx\n * export const MyComponent = () => {\n * const x = useMotionValue(0)\n * const y = useMotionValue(0)\n * const z = useTransform([x, y], ([latestX, latestY]) => latestX * latestY)\n *\n * return <motion.div style={{ x, y, z }} />\n * }\n * ```\n *\n * @param input - An array of `MotionValue`s that will pass their latest values through `transform` to update the returned `MotionValue`.\n * @param transform - A function that accepts the latest values from `input` and returns a new value.\n * @returns `MotionValue`\n *\n * @public\n */\nexport function useTransform<I, O>(\n input:\n | MotionValue<string>[]\n | MotionValue<number>[]\n | MotionValue<AnyResolvedKeyframe>[],\n transformer: MultiTransformer<I, O>\n): MotionValue<O>\nexport function useTransform<I, O>(transformer: () => O): MotionValue<O>\n\nexport function useTransform<I, O, K extends string>(\n input:\n | MotionValue<I>\n | MotionValue<string>[]\n | MotionValue<number>[]\n | MotionValue<AnyResolvedKeyframe>[]\n | (() => O),\n inputRangeOrTransformer?: InputRange | Transformer<I, O>,\n outputRangeOrMap?: O[] | OutputMap<O>,\n options?: TransformOptions<O>\n): MotionValue<O> | { [key in K]: MotionValue<O> } {\n if (typeof input === \"function\") {\n return useComputed(input)\n }\n\n /**\n * Detect if outputRangeOrMap is an output map (object with keys)\n * rather than an output range (array).\n */\n const isOutputMap =\n outputRangeOrMap !== undefined &&\n !Array.isArray(outputRangeOrMap) &&\n typeof inputRangeOrTransformer !== \"function\"\n\n if (isOutputMap) {\n return useMapTransform(\n input as MotionValue<number>,\n inputRangeOrTransformer as InputRange,\n outputRangeOrMap as OutputMap<O>,\n options\n ) as { [key in K]: MotionValue<O> }\n }\n\n const outputRange = outputRangeOrMap as O[] | undefined\n const transformer =\n typeof inputRangeOrTransformer === \"function\"\n ? inputRangeOrTransformer\n : transform(inputRangeOrTransformer!, outputRange!, options)\n\n return Array.isArray(input)\n ? useListTransform(\n input,\n transformer as MultiTransformer<AnyResolvedKeyframe, O>\n )\n : useListTransform([input], ([latest]) =>\n (transformer as SingleTransformer<I, O>)(latest)\n )\n}\n\nfunction useListTransform<I, O>(\n values: MotionValue<I>[],\n transformer: MultiTransformer<I, O>\n): MotionValue<O> {\n const latest = useConstant<I[]>(() => [])\n\n return useCombineMotionValues(values, () => {\n latest.length = 0\n const numValues = values.length\n for (let i = 0; i < numValues; i++) {\n latest[i] = values[i].get()\n }\n\n return transformer(latest)\n })\n}\n\nfunction useMapTransform<O>(\n inputValue: MotionValue<number>,\n inputRange: InputRange,\n outputMap: OutputMap<O>,\n options?: TransformOptions<O>\n): { [key: string]: MotionValue<O> } {\n /**\n * Capture keys once to ensure hooks are called in consistent order.\n */\n const keys = useConstant(() => Object.keys(outputMap))\n const output = useConstant<{ [key: string]: MotionValue<O> }>(() => ({}))\n\n for (const key of keys) {\n output[key] = useTransform(inputValue, inputRange, outputMap[key], options)\n }\n\n return output\n}\n"],"names":[],"mappings":";;;;;;AA2KM;AAWF;AACI;;AAGJ;;;AAGG;AACH;AAEI;;;;;;AAaJ;AAEQ;;AAGR;AACI;AAIA;AAGR;AAEA;;AAMI;AACI;AACA;AACA;;;AAIA;AACJ;AACJ;AAEA;AAMI;;AAEG;AACH;;AAGA;AACI;;AAGJ;AACJ;;"}