UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

122 lines (116 loc) 4.27 kB
'use strict'; import { logger, ReanimatedError } from '../../common'; import { PERCENTAGE_REGEX } from '../constants'; export const ERROR_MESSAGES = { invalidPointsCount: () => `Invalid linear easing points count. There should be at least two points`, invalidInputProgressValue: inputProgress => `Invalid input progress ${inputProgress} value, it should be a percentage between 0% and 100%` }; export const WARN_MESSAGES = { inputProgressLessThanPrecedingPoint: (x, precedingX) => `Linear easing point x value ${x} is less than value of the preceding control point ${precedingX}. Value will be overridden by ${precedingX}` }; const parsePercentage = percentage => { let result; if (typeof percentage === 'number') { result = percentage; } else if (PERCENTAGE_REGEX.test(percentage)) { result = parseFloat(percentage) / 100; } if (result === undefined || result < 0 || result > 1) { throw new ReanimatedError(ERROR_MESSAGES.invalidInputProgressValue(percentage)); } return result; }; const extrapolate = (x, point1, point2) => { const slope = (point2.y - point1.y) / (point2.x - point1.x); return point1.y + slope * (x - point1.x); }; export class LinearEasing { static easingName = 'linear'; constructor(points) { if (points.length < 2) { throw new ReanimatedError(ERROR_MESSAGES.invalidPointsCount()); } this.points = points.map(p => Array.isArray(p) && p.length === 1 ? p[0] : p); } toString() { return `${LinearEasing.easingName}(${this.points.map(point => Array.isArray(point) ? `[${point.map(p => typeof p === 'string' ? `"${p}"` : p).join(', ')}]` : point).join(', ')})`; } normalize() { const points = this.canonicalize(); // Extrapolate points if the input progress of the first one is greater than 0 // or the input progress of the last one is less than 1 if (points[0].x > 0) { points.unshift({ x: 0, y: extrapolate(0, points[0], points[1]) }); } if (points[points.length - 1].x < 1) { points.push({ x: 1, y: extrapolate(1, points[points.length - 2], points[points.length - 1]) }); } return { name: LinearEasing.easingName, points }; } canonicalize() { const result = this.points.flatMap(point => Array.isArray(point) ? point.slice(1).map(x => ({ x: parsePercentage(x), y: point[0] })) : [{ y: point }]); // 1. If the first control point lacks an input progress value, // set its input progress value to 0. if (result[0].x === undefined) { result[0].x = 0; } // 2.If the last control point lacks an input progress value, // set its input progress value to 1. if (result[result.length - 1].x === undefined) { result[result.length - 1].x = 1; } // 3. If any control point has an input progress value that is less // than the input progress value of any preceding control point, set // its input progress value to the largest input progress value of // any preceding control point. let maxPrecedingX = 0; for (let i = 1; i < result.length - 1; i++) { const x = result[i].x; if (x !== undefined) { if (x < maxPrecedingX) { logger.warn(WARN_MESSAGES.inputProgressLessThanPrecedingPoint(x, maxPrecedingX)); result[i].x = maxPrecedingX; } else { maxPrecedingX = x; } } } // 4. If any control point still lacks an input progress value, then // for each contiguous run of such control points, set their input // progress values so that they are evenly spaced between the preceding // and following control points with input progress values. let precedingX = result[0].x; let missingCount = 0; for (let i = 1; i < result.length; i++) { const x = result[i].x; if (x === undefined) { missingCount++; continue; } if (missingCount > 0) { const range = x - precedingX; for (let j = 0; j < missingCount; j++) { result[i - missingCount + j].x = precedingX + range * (j + 1) / (missingCount + 1); } } precedingX = x; missingCount = 0; } return result; } } //# sourceMappingURL=linear.js.map