# interpolate.js

    import { Tweenable, composeEasingObject, tweenProps } from './tweenable'

    // Fake a Tweenable and patch some internals.  This approach allows us to
    // skip uneccessary processing and object recreation, cutting down on garbage
    // collection pauses.
    const mockTweenable = new Tweenable()
    const { filters } = Tweenable

    /**
     * Compute the midpoint of two Objects.  This method effectively calculates a
     * specific frame of animation that {@link shifty.Tweenable#tween} does many times
     * over the course of a full tween.
     *
     * ```
     * import { interpolate } from 'shifty';
     *
     * const interpolatedValues = interpolate({
     *     width: '100px',
     *     opacity: 0,
     *     color: '#fff'
     *   }, {
     *     width: '200px',
     *     opacity: 1,
     *     color: '#000'
     *   },
     *   0.5
     * );
     *
     * console.log(interpolatedValues); // Logs: {opacity: 0.5, width: "150px", color: "rgb(127,127,127)"}
     * ```
     *
     * @method shifty.interpolate
     * @param {Object} from The starting values to tween from.
     * @param {Object} to The ending values to tween to.
     * @param {number} position The normalized position value (between `0.0` and
     * `1.0`) to interpolate the values between `from` and `to` for.  `from`
     * represents `0` and `to` represents `1`.
     * @param {Object.<string|shifty.easingFunction>|string|shifty.easingFunction}
     * easing The easing curve(s) to calculate the midpoint against.  You can
     * reference any easing function attached to {@link shifty.Tweenable.formulas},
     * or provide the {@link shifty.easingFunction}(s) directly.  If omitted, this
     * defaults to "linear".
     * @param {number} [delay=0] Optional delay to pad the beginning of the
     * interpolated tween with.  This increases the range of `position` from (`0`
     * through `1`) to (`0` through `1 + delay`).  So, a delay of `0.5` would
     * increase all valid values of `position` to numbers between `0` and `1.5`.
     * @return {Object}
     */
    export const interpolate = (from, to, position, easing, delay = 0) => {
      const current = { ...from }
      const easingObject = composeEasingObject(from, easing)

      mockTweenable._filters.length = 0

      mockTweenable.set({})
      mockTweenable._currentState = current
      mockTweenable._originalState = from
      mockTweenable._targetState = to
      mockTweenable._easing = easingObject

      for (const name in filters) {
        if (filters[name].doesApply(mockTweenable)) {
          mockTweenable._filters.push(filters[name])
        }
      }

      // Any defined value transformation must be applied
      mockTweenable._applyFilter('tweenCreated')
      mockTweenable._applyFilter('beforeTween')

      const interpolatedValues = tweenProps(
        position,
        current,
        from,
        to,
        1,
        delay,
        easingObject
      )

      // Transform data in interpolatedValues back into its original format
      mockTweenable._applyFilter('afterTween')

      return interpolatedValues
    }