All files / src/utils transform.ts

100% Statements 15/15
100% Branches 8/8
100% Functions 3/3
100% Lines 14/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 13132x                                                         32x 41x     41x                                                                                                                                                           32x 201x       41x 41x 41x 41x 41x 41x   41x         41x    
import { interpolate, Easing } from "popmotion"
import { CustomValueType } from "../types"
 
/**
 * @public
 */
export interface TransformOptions<T> {
    /**
     * Clamp values to within the given range. Defaults to `true`
     *
     * @public
     */
    clamp?: boolean
 
    /**
     * 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.
     *
     * @public
     */
    ease?: Easing | Easing[]
 
    /**
     * @internal
     */
    mixer?: (from: T, to: T) => (v: number) => any
}
 
const isCustomValueType = (v: any): v is CustomValueType => {
    return typeof v === "object" && v.mix
}
 
const getMixer = (v: any) => (isCustomValueType(v) ? v.mix : undefined)
 
/**
 * Transforms numbers into other values by mapping them from an input range to an output range.
 * Returns the type of the input provided.
 *
 * @remarks
 *
 * Given an input range of `[0, 200]` and an output range of
 * `[0, 1]`, this function will return a value between `0` and `1`.
 * The input range must be a linear series of numbers. The output range
 * can be any supported value type, such as numbers, colors, shadows, arrays, objects and more.
 * Every value in the output range must be of the same type and in the same format.
 *
 * ```jsx
 * import * as React from "react"
 * import { transform } from "framer-motion"
 *
 * export function MyComponent() {
 *    const inputRange = [0, 200]
 *    const outputRange = [0, 1]
 *    const output = transform(100, inputRange, outputRange)
 *
 *    // Returns 0.5
 *    return <div>{output}</div>
 * }
 * ```
 *
 * @param inputValue - A number to transform between the input and output ranges.
 * @param inputRange - A linear series of numbers (either all increasing or decreasing).
 * @param outputRange - A series of numbers, colors, strings, or arrays/objects of those. Must be the same length as `inputRange`.
 * @param options - Clamp: Clamp values to within the given range. Defaults to `true`.
 *
 * @public
 */
export function transform<T>(
    inputValue: number,
    inputRange: number[],
    outputRange: T[],
    options?: TransformOptions<T>
): T
/**
 *
 * Transforms numbers into other values by mapping them from an input range to an output range.
 *
 * Given an input range of `[0, 200]` and an output range of
 * `[0, 1]`, this function will return a value between `0` and `1`.
 * The input range must be a linear series of numbers. The output range
 * can be any supported value type, such as numbers, colors, shadows, arrays, objects and more.
 * Every value in the output range must be of the same type and in the same format.
 *
 * ```jsx
 * import * as React from "react"
 * import { Frame, transform } from "framer"
 *
 * export function MyComponent() {
 *     const inputRange = [-200, -100, 100, 200]
 *     const outputRange = [0, 1, 1, 0]
 *     const convertRange = transform(inputRange, outputRange)
 *     const output = convertRange(-150)
 *
 *     // Returns 0.5
 *     return <div>{output}</div>
 * }
 *
 * ```
 *
 * @param inputRange - A linear series of numbers (either all increasing or decreasing).
 * @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`.
 * @param options - Clamp: clamp values to within the given range. Defaults to `true`.
 *
 * @public
 */
export function transform<T>(
    inputRange: number[],
    outputRange: T[],
    options?: TransformOptions<T>
): (inputValue: number) => T
export function transform<T>(
    ...args:
        | [number, number[], T[], TransformOptions<T>?]
        | [number[], T[], TransformOptions<T>?]
) {
    const useImmediate = !Array.isArray(args[0])
    const argOffset = useImmediate ? 0 : -1
    const inputValue = args[0 + argOffset] as number
    const inputRange = args[1 + argOffset] as number[]
    const outputRange = args[2 + argOffset] as T[]
    const options = args[3 + argOffset] as TransformOptions<T>
 
    const interpolator = interpolate(inputRange, outputRange, {
        mixer: getMixer(outputRange[0]),
        ...options,
    })
 
    return useImmediate ? interpolator(inputValue) : interpolator
}