diff --git a/README.md b/README.md index 0b919dbd..24af9eb4 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,63 @@ Example usage: ``` +## SVG + +### `parsePath(SVGPath: String): ReanimatedPath` + +Given an SVG Path, returns an denormalized object of values that can be used for animations on that path. +From the perspective of the user, the returned value should be considered a black box. +Here is an example below: + +```ts +// We get the data from the SVG Path denormalized a way that can be handled with Reanimated +const path = parsePath(d); +const { y, x } = getPointAtLength(path, length); +``` + +### `getPointAtLength(path): { x: Node, y: Node }` + +Implementation of (getPointAtLength)[https://developer.mozilla.org/en-US/docs/Web/API/SVGPathElement/getPointAtLength] for Reanimated. + +```ts +// We get the data from the SVG Path denormalized a way that can be handled with Reanimated +const path = parsePath(d); +const { y, x } = getPointAtLength(path, length); +``` + +### `interpolatePath(path1, path2, progress): path` + +Interpolate from one SVG point to the other, this function assumes that each path has the same number of points. + +```tsx +const rhino = "M 217.765 29.683 C 225.855 29.683 "; +const rhinoPath = parsePath(rhino); +const elephant = "M 223.174 43.413 ..."; +const elephantPath = parsePath(elephant); +return ( + <> + + {() => + set( + progress, + runTiming(clock, progress, { + toValue: 1, + duration: 2000, + easing: Easing.linear + }) + ) + } + + + + + + ); +``` + ## Math ### `toRad(node)` @@ -93,6 +150,13 @@ Returns the angle in the plane (in radians) between the positive x-axis and the atan2(y: Node, x Node) => Node ``` +### `cubicBezier(t, p0, p1, p2, p3)` + +Returns the coordinate of a cubic bezier curve. +`t` is the length of the curve from 0 to 1. `cubicBezier(0, p0, p1, p2, p3) => p0` and `cubicBezier(1, p0, p1, p2, p3) => p3`. +`p0` and `p3` are respectively the starting and ending point of the curve. +`p1` and `p2` are the control points. + ## Arrays ### `find(nodes, index, notFound)` @@ -111,16 +175,6 @@ Returns 1 if the node value is contained in the array of nodes, 0 otherwise. contains(values: Node[], value: Node) => Node ``` -## SVG - -### `getX(SVGPath: String, y: Node): Node` - -Given an SVG Path, returns the `x` coordinate of the path given an `y` coordinate. - -### `getY(SVGPath: String, x: Node): Node` - -Given an SVG Path, returns the `y` coordinate of the path given an `x` coordinate. - ## Animations ### `runTiming(clock, value, config)` @@ -150,7 +204,7 @@ Convenience function to run a decay animation. runDecay(clock: Clock, value: Node, velocity: Node, rerunDecaying: Node): Node ``` -### `binaryInterpolation(node, from, to)` +### `bInterpolate(node, from, to)` Interpolate the node from 0 to 1 without clamping. diff --git a/package.json b/package.json index be94d6c8..d8d80d99 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@types/react": "^16.8.15", "@types/react-native": "^0.57.51", "eslint": "^5.16.0", - "eslint-config-react-native-wcandillon": "^1.0.6", + "eslint-config-react-native-wcandillon": "1.1.3", "react": "^16.8.6", "react-native": "^0.59.5", "react-native-gesture-handler": "^1.2.1", @@ -55,6 +55,8 @@ ] }, "dependencies": { - "svg-path-properties": "0.4.8" + "abs-svg-path": "^0.1.1", + "normalize-svg-path": "^1.0.1", + "parse-svg-path": "^0.1.2" } } diff --git a/src/AnimationRunners.ts b/src/AnimationRunners.ts index a2177901..2a1b7763 100644 --- a/src/AnimationRunners.ts +++ b/src/AnimationRunners.ts @@ -11,24 +11,24 @@ const { set, startClock, clockRunning, - onChange, + onChange } = Animated; export function runDecay( clock: Animated.Clock, value: Animated.Adaptable, velocity: Animated.Adaptable, - rerunDecaying: Animated.Value, + rerunDecaying: Animated.Value ) { const state = { finished: new Value(0), velocity: new Value(0), position: new Value(0), - time: new Value(0), + time: new Value(0) }; const config = { - deceleration: 0.99, + deceleration: 0.99 }; return [ @@ -39,12 +39,12 @@ export function runDecay( set(state.velocity, velocity), set(state.position, value), set(state.time, 0), - startClock(clock), - ]), + startClock(clock) + ]) ]), decay(clock, state, config), cond(state.finished, stopClock(clock)), - state.position, + state.position ]; } @@ -59,14 +59,14 @@ export function runSpring( stiffness: 121.6, overshootClamping: false, restSpeedThreshold: 0.001, - restDisplacementThreshold: 0.001, - }, + restDisplacementThreshold: 0.001 + } ) { const state = { finished: new Value(0), velocity: new Value(0), position: new Value(0), - time: new Value(0), + time: new Value(0) }; return block([ @@ -76,24 +76,24 @@ export function runSpring( set(state.position, value), set(state.velocity, 0), set(config.toValue as any, dest), - startClock(clock), + startClock(clock) ]), spring(clock, state, config), cond(state.finished, stopClock(clock)), - state.position, + state.position ]); } export function runTiming( clock: Animated.Clock, value: Animated.Adaptable, - config: Animated.TimingConfig, + config: Animated.TimingConfig ) { const state = { finished: new Value(0), position: new Value(0), time: new Value(0), - frameTime: new Value(0), + frameTime: new Value(0) }; return block([ @@ -103,10 +103,10 @@ export function runTiming( set(state.time, 0), set(state.position, value), set(state.frameTime, 0), - startClock(clock), + startClock(clock) ]), timing(clock, state, config), cond(state.finished, stopClock(clock)), - state.position, + state.position ]); } diff --git a/src/Animations.ts b/src/Animations.ts new file mode 100644 index 00000000..72bffde9 --- /dev/null +++ b/src/Animations.ts @@ -0,0 +1,46 @@ +import Animated from "react-native-reanimated"; + +import { min } from "./Math"; + +const { + Value, + add, + multiply, + cond, + eq, + abs, + sub, + interpolate, + divide +} = Animated; + +export const snapPoint = ( + value: Animated.Adaptable, + velocity: Animated.Adaptable, + points: number[] +) => { + const point = add(value, multiply(0.2, velocity)); + const diffPoint = (p: Animated.Adaptable) => abs(sub(point, p)); + const deltas = points.map(p => diffPoint(p)); + const minDelta = min(...deltas); + return points.reduce( + (acc: Animated.Node, p: number) => + cond(eq(diffPoint(p), minDelta), p, acc), + new Value() + ); +}; + +export const bInterpolate = ( + value: Animated.Adaptable, + origin: Animated.Adaptable, + destination: Animated.Adaptable +) => + interpolate(value, { + inputRange: [0, 1], + outputRange: [origin, destination] + }); + +export const translateZ = ( + perspective: Animated.Adaptable, + z: Animated.Adaptable +) => ({ scale: divide(perspective, sub(perspective, z)) }); diff --git a/src/Arrays.ts b/src/Arrays.ts index d7105b44..cc43401f 100644 --- a/src/Arrays.ts +++ b/src/Arrays.ts @@ -1,16 +1,15 @@ import Animated from "react-native-reanimated"; -const { - Value, cond, eq, or, -} = Animated; +const { Value, cond, eq, or } = Animated; export const find = ( array: Animated.Adaptable[], index: Animated.Adaptable, - notFound: Animated.Node = new Value(), + notFound: Animated.Node = new Value() ) => array.reduce((acc, v, i) => cond(eq(i, index), v, acc), notFound); export const contains = ( values: Animated.Node[], - value: Animated.Node, -): Animated.Node => values.reduce((acc, v) => or(acc, eq(value, v)), new Value(0)); + value: Animated.Node +): Animated.Node => + values.reduce((acc, v) => or(acc, eq(value, v)), new Value(0)); diff --git a/src/Colors.ts b/src/Colors.ts index 601c10d0..1ee07129 100644 --- a/src/Colors.ts +++ b/src/Colors.ts @@ -12,7 +12,7 @@ const { divide, sub, color, - Extrapolate, + Extrapolate } = Animated; interface RGBColor { @@ -21,23 +21,27 @@ interface RGBColor { b: number; } -function match(condsAndResPairs: Animated.Adaptable[], offset = 0): any { +function match( + condsAndResPairs: Animated.Adaptable[], + offset = 0 +): any { if (condsAndResPairs.length - offset === 1) { return condsAndResPairs[offset]; - } if (condsAndResPairs.length - offset === 0) { + } + if (condsAndResPairs.length - offset === 0) { return undefined; } return cond( condsAndResPairs[offset], condsAndResPairs[offset + 1], - match(condsAndResPairs, offset + 2), + match(condsAndResPairs, offset + 2) ); } function colorHSV( h: Animated.Adaptable /* 0 - 360 */, s: Animated.Adaptable /* 0 - 1 */, - v: Animated.Adaptable, /* 0 - 1 */ + v: Animated.Adaptable /* 0 - 1 */ ) { // Converts color from HSV format into RGB // Formula explained here: https://www.rapidtables.com/convert/color/hsv-to-rgb.html @@ -50,12 +54,13 @@ function colorHSV( const colorRGB = ( r: Animated.Adaptable, g: Animated.Adaptable, - b: Animated.Adaptable, - ) => color( - round(multiply(255, add(r, m))), - round(multiply(255, add(g, m))), - round(multiply(255, add(b, m))), - ); + b: Animated.Adaptable + ) => + color( + round(multiply(255, add(r, m))), + round(multiply(255, add(g, m))), + round(multiply(255, add(b, m))) + ); return match([ lessThan(h, 60), @@ -68,7 +73,7 @@ function colorHSV( colorRGB(0, x, c), lessThan(h, 300), colorRGB(x, 0, c), - colorRGB(c, 0, x) /* else */, + colorRGB(c, 0, x) /* else */ ]); } @@ -88,57 +93,71 @@ const rgbToHsv = (c: RGBColor) => { h = 0; // achromatic } else { switch (ma) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - default: // do nothing + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + default: // do nothing } h /= 6; } return { h: h * 360, s, v }; }; -const interpolateColorsHSV = (animationValue: Animated.Adaptable, inputRange: number[], colors: RGBColor[]) => { +const interpolateColorsHSV = ( + animationValue: Animated.Adaptable, + inputRange: number[], + colors: RGBColor[] +) => { const colorsAsHSV = colors.map(c => rgbToHsv(c)); const h = interpolate(animationValue, { inputRange, outputRange: colorsAsHSV.map(c => c.h), - extrapolate: Extrapolate.CLAMP, + extrapolate: Extrapolate.CLAMP }); const s = interpolate(animationValue, { inputRange, outputRange: colorsAsHSV.map(c => c.s), - extrapolate: Extrapolate.CLAMP, + extrapolate: Extrapolate.CLAMP }); const v = interpolate(animationValue, { inputRange, outputRange: colorsAsHSV.map(c => c.v), - extrapolate: Extrapolate.CLAMP, + extrapolate: Extrapolate.CLAMP }); return colorHSV(h, s, v); }; -const interpolateColorsRGB = (animationValue: Animated.Adaptable, inputRange: number[], colors: RGBColor[]) => { +const interpolateColorsRGB = ( + animationValue: Animated.Adaptable, + inputRange: number[], + colors: RGBColor[] +) => { const r = round( interpolate(animationValue, { inputRange, outputRange: colors.map(c => c.r), - extrapolate: Extrapolate.CLAMP, - }), + extrapolate: Extrapolate.CLAMP + }) ); const g = round( interpolate(animationValue, { inputRange, outputRange: colors.map(c => c.g), - extrapolate: Extrapolate.CLAMP, - }), + extrapolate: Extrapolate.CLAMP + }) ); const b = round( interpolate(animationValue, { inputRange, outputRange: colors.map(c => c.b), - extrapolate: Extrapolate.CLAMP, - }), + extrapolate: Extrapolate.CLAMP + }) ); return color(r, g, b); }; @@ -147,8 +166,9 @@ export const interpolateColors = ( animationValue: Animated.Adaptable, inputRange: number[], colors: RGBColor[], - colorSpace: "hsv" | "rgb" = "hsv", + colorSpace: "hsv" | "rgb" = "hsv" ) => { - if (colorSpace === "hsv") return interpolateColorsHSV(animationValue, inputRange, colors); + if (colorSpace === "hsv") + return interpolateColorsHSV(animationValue, inputRange, colors); return interpolateColorsRGB(animationValue, inputRange, colors); }; diff --git a/src/CubicBezierLength.ts b/src/CubicBezierLength.ts new file mode 100644 index 00000000..24dd1f3b --- /dev/null +++ b/src/CubicBezierLength.ts @@ -0,0 +1,780 @@ +interface Point { + x: number; + y: number; +} + +type CtrlPoint = [number, number, number, number]; + +// Cubic bezier curve length from http://bl.ocks.org/hnakamur/e7efd0602bfc15f66fc5 +// Legendre-Gauss abscissae (xi values, defined at i=n as the roots of the nth order Legendre polynomial Pn(x)) +const tValues = [ + [], + [], + [ + -0.5773502691896257645091487805019574556476, + 0.5773502691896257645091487805019574556476 + ], + [ + 0, + -0.7745966692414833770358530799564799221665, + 0.7745966692414833770358530799564799221665 + ], + [ + -0.3399810435848562648026657591032446872005, + 0.3399810435848562648026657591032446872005, + -0.8611363115940525752239464888928095050957, + 0.8611363115940525752239464888928095050957 + ], + [ + 0, + -0.5384693101056830910363144207002088049672, + 0.5384693101056830910363144207002088049672, + -0.9061798459386639927976268782993929651256, + 0.9061798459386639927976268782993929651256 + ], + [ + 0.6612093864662645136613995950199053470064, + -0.6612093864662645136613995950199053470064, + -0.2386191860831969086305017216807119354186, + 0.2386191860831969086305017216807119354186, + -0.9324695142031520278123015544939946091347, + 0.9324695142031520278123015544939946091347 + ], + [ + 0, + 0.4058451513773971669066064120769614633473, + -0.4058451513773971669066064120769614633473, + -0.7415311855993944398638647732807884070741, + 0.7415311855993944398638647732807884070741, + -0.9491079123427585245261896840478512624007, + 0.9491079123427585245261896840478512624007 + ], + [ + -0.1834346424956498049394761423601839806667, + 0.1834346424956498049394761423601839806667, + -0.5255324099163289858177390491892463490419, + 0.5255324099163289858177390491892463490419, + -0.7966664774136267395915539364758304368371, + 0.7966664774136267395915539364758304368371, + -0.9602898564975362316835608685694729904282, + 0.9602898564975362316835608685694729904282 + ], + [ + 0, + -0.8360311073266357942994297880697348765441, + 0.8360311073266357942994297880697348765441, + -0.9681602395076260898355762029036728700494, + 0.9681602395076260898355762029036728700494, + -0.3242534234038089290385380146433366085719, + 0.3242534234038089290385380146433366085719, + -0.6133714327005903973087020393414741847857, + 0.6133714327005903973087020393414741847857 + ], + [ + -0.1488743389816312108848260011297199846175, + 0.1488743389816312108848260011297199846175, + -0.4333953941292471907992659431657841622, + 0.4333953941292471907992659431657841622, + -0.6794095682990244062343273651148735757692, + 0.6794095682990244062343273651148735757692, + -0.8650633666889845107320966884234930485275, + 0.8650633666889845107320966884234930485275, + -0.9739065285171717200779640120844520534282, + 0.9739065285171717200779640120844520534282 + ], + [ + 0, + -0.2695431559523449723315319854008615246796, + 0.2695431559523449723315319854008615246796, + -0.5190961292068118159257256694586095544802, + 0.5190961292068118159257256694586095544802, + -0.7301520055740493240934162520311534580496, + 0.7301520055740493240934162520311534580496, + -0.8870625997680952990751577693039272666316, + 0.8870625997680952990751577693039272666316, + -0.9782286581460569928039380011228573907714, + 0.9782286581460569928039380011228573907714 + ], + [ + -0.1252334085114689154724413694638531299833, + 0.1252334085114689154724413694638531299833, + -0.3678314989981801937526915366437175612563, + 0.3678314989981801937526915366437175612563, + -0.587317954286617447296702418940534280369, + 0.587317954286617447296702418940534280369, + -0.7699026741943046870368938332128180759849, + 0.7699026741943046870368938332128180759849, + -0.9041172563704748566784658661190961925375, + 0.9041172563704748566784658661190961925375, + -0.9815606342467192506905490901492808229601, + 0.9815606342467192506905490901492808229601 + ], + [ + 0, + -0.2304583159551347940655281210979888352115, + 0.2304583159551347940655281210979888352115, + -0.4484927510364468528779128521276398678019, + 0.4484927510364468528779128521276398678019, + -0.6423493394403402206439846069955156500716, + 0.6423493394403402206439846069955156500716, + -0.8015780907333099127942064895828598903056, + 0.8015780907333099127942064895828598903056, + -0.9175983992229779652065478365007195123904, + 0.9175983992229779652065478365007195123904, + -0.9841830547185881494728294488071096110649, + 0.9841830547185881494728294488071096110649 + ], + [ + -0.1080549487073436620662446502198347476119, + 0.1080549487073436620662446502198347476119, + -0.3191123689278897604356718241684754668342, + 0.3191123689278897604356718241684754668342, + -0.5152486363581540919652907185511886623088, + 0.5152486363581540919652907185511886623088, + -0.6872929048116854701480198030193341375384, + 0.6872929048116854701480198030193341375384, + -0.8272013150697649931897947426503949610397, + 0.8272013150697649931897947426503949610397, + -0.928434883663573517336391139377874264477, + 0.928434883663573517336391139377874264477, + -0.986283808696812338841597266704052801676, + 0.986283808696812338841597266704052801676 + ], + [ + 0, + -0.2011940939974345223006283033945962078128, + 0.2011940939974345223006283033945962078128, + -0.3941513470775633698972073709810454683627, + 0.3941513470775633698972073709810454683627, + -0.5709721726085388475372267372539106412383, + 0.5709721726085388475372267372539106412383, + -0.7244177313601700474161860546139380096308, + 0.7244177313601700474161860546139380096308, + -0.8482065834104272162006483207742168513662, + 0.8482065834104272162006483207742168513662, + -0.9372733924007059043077589477102094712439, + 0.9372733924007059043077589477102094712439, + -0.9879925180204854284895657185866125811469, + 0.9879925180204854284895657185866125811469 + ], + [ + -0.0950125098376374401853193354249580631303, + 0.0950125098376374401853193354249580631303, + -0.281603550779258913230460501460496106486, + 0.281603550779258913230460501460496106486, + -0.45801677765722738634241944298357757354, + 0.45801677765722738634241944298357757354, + -0.6178762444026437484466717640487910189918, + 0.6178762444026437484466717640487910189918, + -0.7554044083550030338951011948474422683538, + 0.7554044083550030338951011948474422683538, + -0.8656312023878317438804678977123931323873, + 0.8656312023878317438804678977123931323873, + -0.9445750230732325760779884155346083450911, + 0.9445750230732325760779884155346083450911, + -0.9894009349916499325961541734503326274262, + 0.9894009349916499325961541734503326274262 + ], + [ + 0, + -0.1784841814958478558506774936540655574754, + 0.1784841814958478558506774936540655574754, + -0.3512317634538763152971855170953460050405, + 0.3512317634538763152971855170953460050405, + -0.5126905370864769678862465686295518745829, + 0.5126905370864769678862465686295518745829, + -0.6576711592166907658503022166430023351478, + 0.6576711592166907658503022166430023351478, + -0.7815140038968014069252300555204760502239, + 0.7815140038968014069252300555204760502239, + -0.8802391537269859021229556944881556926234, + 0.8802391537269859021229556944881556926234, + -0.9506755217687677612227169578958030214433, + 0.9506755217687677612227169578958030214433, + -0.9905754753144173356754340199406652765077, + 0.9905754753144173356754340199406652765077 + ], + [ + -0.0847750130417353012422618529357838117333, + 0.0847750130417353012422618529357838117333, + -0.2518862256915055095889728548779112301628, + 0.2518862256915055095889728548779112301628, + -0.4117511614628426460359317938330516370789, + 0.4117511614628426460359317938330516370789, + -0.5597708310739475346078715485253291369276, + 0.5597708310739475346078715485253291369276, + -0.6916870430603532078748910812888483894522, + 0.6916870430603532078748910812888483894522, + -0.8037049589725231156824174550145907971032, + 0.8037049589725231156824174550145907971032, + -0.8926024664975557392060605911271455154078, + 0.8926024664975557392060605911271455154078, + -0.9558239495713977551811958929297763099728, + 0.9558239495713977551811958929297763099728, + -0.9915651684209309467300160047061507702525, + 0.9915651684209309467300160047061507702525 + ], + [ + 0, + -0.1603586456402253758680961157407435495048, + 0.1603586456402253758680961157407435495048, + -0.3165640999636298319901173288498449178922, + 0.3165640999636298319901173288498449178922, + -0.4645707413759609457172671481041023679762, + 0.4645707413759609457172671481041023679762, + -0.6005453046616810234696381649462392798683, + 0.6005453046616810234696381649462392798683, + -0.7209661773352293786170958608237816296571, + 0.7209661773352293786170958608237816296571, + -0.8227146565371428249789224867127139017745, + 0.8227146565371428249789224867127139017745, + -0.9031559036148179016426609285323124878093, + 0.9031559036148179016426609285323124878093, + -0.960208152134830030852778840687651526615, + 0.960208152134830030852778840687651526615, + -0.9924068438435844031890176702532604935893, + 0.9924068438435844031890176702532604935893 + ], + [ + -0.0765265211334973337546404093988382110047, + 0.0765265211334973337546404093988382110047, + -0.227785851141645078080496195368574624743, + 0.227785851141645078080496195368574624743, + -0.3737060887154195606725481770249272373957, + 0.3737060887154195606725481770249272373957, + -0.5108670019508270980043640509552509984254, + 0.5108670019508270980043640509552509984254, + -0.6360536807265150254528366962262859367433, + 0.6360536807265150254528366962262859367433, + -0.7463319064601507926143050703556415903107, + 0.7463319064601507926143050703556415903107, + -0.8391169718222188233945290617015206853296, + 0.8391169718222188233945290617015206853296, + -0.9122344282513259058677524412032981130491, + 0.9122344282513259058677524412032981130491, + -0.963971927277913791267666131197277221912, + 0.963971927277913791267666131197277221912, + -0.9931285991850949247861223884713202782226, + 0.9931285991850949247861223884713202782226 + ], + [ + 0, + -0.1455618541608950909370309823386863301163, + 0.1455618541608950909370309823386863301163, + -0.288021316802401096600792516064600319909, + 0.288021316802401096600792516064600319909, + -0.4243421202074387835736688885437880520964, + 0.4243421202074387835736688885437880520964, + -0.551618835887219807059018796724313286622, + 0.551618835887219807059018796724313286622, + -0.667138804197412319305966669990339162597, + 0.667138804197412319305966669990339162597, + -0.7684399634756779086158778513062280348209, + 0.7684399634756779086158778513062280348209, + -0.8533633645833172836472506385875676702761, + 0.8533633645833172836472506385875676702761, + -0.9200993341504008287901871337149688941591, + 0.9200993341504008287901871337149688941591, + -0.9672268385663062943166222149076951614246, + 0.9672268385663062943166222149076951614246, + -0.9937521706203895002602420359379409291933, + 0.9937521706203895002602420359379409291933 + ], + [ + -0.0697392733197222212138417961186280818222, + 0.0697392733197222212138417961186280818222, + -0.2078604266882212854788465339195457342156, + 0.2078604266882212854788465339195457342156, + -0.3419358208920842251581474204273796195591, + 0.3419358208920842251581474204273796195591, + -0.4693558379867570264063307109664063460953, + 0.4693558379867570264063307109664063460953, + -0.5876404035069115929588769276386473488776, + 0.5876404035069115929588769276386473488776, + -0.6944872631866827800506898357622567712673, + 0.6944872631866827800506898357622567712673, + -0.7878168059792081620042779554083515213881, + 0.7878168059792081620042779554083515213881, + -0.8658125777203001365364256370193787290847, + 0.8658125777203001365364256370193787290847, + -0.9269567721871740005206929392590531966353, + 0.9269567721871740005206929392590531966353, + -0.9700604978354287271239509867652687108059, + 0.9700604978354287271239509867652687108059, + -0.994294585482399292073031421161298980393, + 0.994294585482399292073031421161298980393 + ], + [ + 0, + -0.1332568242984661109317426822417661370104, + 0.1332568242984661109317426822417661370104, + -0.264135680970344930533869538283309602979, + 0.264135680970344930533869538283309602979, + -0.390301038030290831421488872880605458578, + 0.390301038030290831421488872880605458578, + -0.5095014778460075496897930478668464305448, + 0.5095014778460075496897930478668464305448, + -0.6196098757636461563850973116495956533871, + 0.6196098757636461563850973116495956533871, + -0.7186613631319501944616244837486188483299, + 0.7186613631319501944616244837486188483299, + -0.8048884016188398921511184069967785579414, + 0.8048884016188398921511184069967785579414, + -0.8767523582704416673781568859341456716389, + 0.8767523582704416673781568859341456716389, + -0.9329710868260161023491969890384229782357, + 0.9329710868260161023491969890384229782357, + -0.9725424712181152319560240768207773751816, + 0.9725424712181152319560240768207773751816, + -0.9947693349975521235239257154455743605736, + 0.9947693349975521235239257154455743605736 + ], + [ + -0.0640568928626056260850430826247450385909, + 0.0640568928626056260850430826247450385909, + -0.1911188674736163091586398207570696318404, + 0.1911188674736163091586398207570696318404, + -0.3150426796961633743867932913198102407864, + 0.3150426796961633743867932913198102407864, + -0.4337935076260451384870842319133497124524, + 0.4337935076260451384870842319133497124524, + -0.5454214713888395356583756172183723700107, + 0.5454214713888395356583756172183723700107, + -0.6480936519369755692524957869107476266696, + 0.6480936519369755692524957869107476266696, + -0.7401241915785543642438281030999784255232, + 0.7401241915785543642438281030999784255232, + -0.8200019859739029219539498726697452080761, + 0.8200019859739029219539498726697452080761, + -0.8864155270044010342131543419821967550873, + 0.8864155270044010342131543419821967550873, + -0.9382745520027327585236490017087214496548, + 0.9382745520027327585236490017087214496548, + -0.9747285559713094981983919930081690617411, + 0.9747285559713094981983919930081690617411, + -0.9951872199970213601799974097007368118745, + 0.9951872199970213601799974097007368118745 + ] +]; + +// Legendre-Gauss weights (wi values, defined by a function linked to in the Bezier primer article) +const cValues = [ + [], + [], + [1.0, 1.0], + [ + 0.8888888888888888888888888888888888888888, + 0.5555555555555555555555555555555555555555, + 0.5555555555555555555555555555555555555555 + ], + [ + 0.6521451548625461426269360507780005927646, + 0.6521451548625461426269360507780005927646, + 0.3478548451374538573730639492219994072353, + 0.3478548451374538573730639492219994072353 + ], + [ + 0.5688888888888888888888888888888888888888, + 0.4786286704993664680412915148356381929122, + 0.4786286704993664680412915148356381929122, + 0.2369268850561890875142640407199173626432, + 0.2369268850561890875142640407199173626432 + ], + [ + 0.3607615730481386075698335138377161116615, + 0.3607615730481386075698335138377161116615, + 0.4679139345726910473898703439895509948116, + 0.4679139345726910473898703439895509948116, + 0.1713244923791703450402961421727328935268, + 0.1713244923791703450402961421727328935268 + ], + [ + 0.4179591836734693877551020408163265306122, + 0.3818300505051189449503697754889751338783, + 0.3818300505051189449503697754889751338783, + 0.2797053914892766679014677714237795824869, + 0.2797053914892766679014677714237795824869, + 0.1294849661688696932706114326790820183285, + 0.1294849661688696932706114326790820183285 + ], + [ + 0.3626837833783619829651504492771956121941, + 0.3626837833783619829651504492771956121941, + 0.3137066458778872873379622019866013132603, + 0.3137066458778872873379622019866013132603, + 0.2223810344533744705443559944262408844301, + 0.2223810344533744705443559944262408844301, + 0.1012285362903762591525313543099621901153, + 0.1012285362903762591525313543099621901153 + ], + [ + 0.3302393550012597631645250692869740488788, + 0.1806481606948574040584720312429128095143, + 0.1806481606948574040584720312429128095143, + 0.0812743883615744119718921581105236506756, + 0.0812743883615744119718921581105236506756, + 0.3123470770400028400686304065844436655987, + 0.3123470770400028400686304065844436655987, + 0.2606106964029354623187428694186328497718, + 0.2606106964029354623187428694186328497718 + ], + [ + 0.295524224714752870173892994651338329421, + 0.295524224714752870173892994651338329421, + 0.2692667193099963550912269215694693528597, + 0.2692667193099963550912269215694693528597, + 0.2190863625159820439955349342281631924587, + 0.2190863625159820439955349342281631924587, + 0.1494513491505805931457763396576973324025, + 0.1494513491505805931457763396576973324025, + 0.0666713443086881375935688098933317928578, + 0.0666713443086881375935688098933317928578 + ], + [ + 0.272925086777900630714483528336342189156, + 0.2628045445102466621806888698905091953727, + 0.2628045445102466621806888698905091953727, + 0.2331937645919904799185237048431751394317, + 0.2331937645919904799185237048431751394317, + 0.1862902109277342514260976414316558916912, + 0.1862902109277342514260976414316558916912, + 0.1255803694649046246346942992239401001976, + 0.1255803694649046246346942992239401001976, + 0.0556685671161736664827537204425485787285, + 0.0556685671161736664827537204425485787285 + ], + [ + 0.2491470458134027850005624360429512108304, + 0.2491470458134027850005624360429512108304, + 0.2334925365383548087608498989248780562594, + 0.2334925365383548087608498989248780562594, + 0.2031674267230659217490644558097983765065, + 0.2031674267230659217490644558097983765065, + 0.160078328543346226334652529543359071872, + 0.160078328543346226334652529543359071872, + 0.1069393259953184309602547181939962242145, + 0.1069393259953184309602547181939962242145, + 0.047175336386511827194615961485017060317, + 0.047175336386511827194615961485017060317 + ], + [ + 0.2325515532308739101945895152688359481566, + 0.2262831802628972384120901860397766184347, + 0.2262831802628972384120901860397766184347, + 0.2078160475368885023125232193060527633865, + 0.2078160475368885023125232193060527633865, + 0.1781459807619457382800466919960979955128, + 0.1781459807619457382800466919960979955128, + 0.1388735102197872384636017768688714676218, + 0.1388735102197872384636017768688714676218, + 0.0921214998377284479144217759537971209236, + 0.0921214998377284479144217759537971209236, + 0.0404840047653158795200215922009860600419, + 0.0404840047653158795200215922009860600419 + ], + [ + 0.2152638534631577901958764433162600352749, + 0.2152638534631577901958764433162600352749, + 0.2051984637212956039659240656612180557103, + 0.2051984637212956039659240656612180557103, + 0.1855383974779378137417165901251570362489, + 0.1855383974779378137417165901251570362489, + 0.1572031671581935345696019386238421566056, + 0.1572031671581935345696019386238421566056, + 0.1215185706879031846894148090724766259566, + 0.1215185706879031846894148090724766259566, + 0.0801580871597602098056332770628543095836, + 0.0801580871597602098056332770628543095836, + 0.0351194603317518630318328761381917806197, + 0.0351194603317518630318328761381917806197 + ], + [ + 0.2025782419255612728806201999675193148386, + 0.1984314853271115764561183264438393248186, + 0.1984314853271115764561183264438393248186, + 0.1861610000155622110268005618664228245062, + 0.1861610000155622110268005618664228245062, + 0.1662692058169939335532008604812088111309, + 0.1662692058169939335532008604812088111309, + 0.1395706779261543144478047945110283225208, + 0.1395706779261543144478047945110283225208, + 0.1071592204671719350118695466858693034155, + 0.1071592204671719350118695466858693034155, + 0.0703660474881081247092674164506673384667, + 0.0703660474881081247092674164506673384667, + 0.0307532419961172683546283935772044177217, + 0.0307532419961172683546283935772044177217 + ], + [ + 0.1894506104550684962853967232082831051469, + 0.1894506104550684962853967232082831051469, + 0.1826034150449235888667636679692199393835, + 0.1826034150449235888667636679692199393835, + 0.1691565193950025381893120790303599622116, + 0.1691565193950025381893120790303599622116, + 0.1495959888165767320815017305474785489704, + 0.1495959888165767320815017305474785489704, + 0.1246289712555338720524762821920164201448, + 0.1246289712555338720524762821920164201448, + 0.0951585116824927848099251076022462263552, + 0.0951585116824927848099251076022462263552, + 0.0622535239386478928628438369943776942749, + 0.0622535239386478928628438369943776942749, + 0.0271524594117540948517805724560181035122, + 0.0271524594117540948517805724560181035122 + ], + [ + 0.1794464703562065254582656442618856214487, + 0.1765627053669926463252709901131972391509, + 0.1765627053669926463252709901131972391509, + 0.1680041021564500445099706637883231550211, + 0.1680041021564500445099706637883231550211, + 0.1540457610768102880814315948019586119404, + 0.1540457610768102880814315948019586119404, + 0.1351363684685254732863199817023501973721, + 0.1351363684685254732863199817023501973721, + 0.1118838471934039710947883856263559267358, + 0.1118838471934039710947883856263559267358, + 0.0850361483171791808835353701910620738504, + 0.0850361483171791808835353701910620738504, + 0.0554595293739872011294401653582446605128, + 0.0554595293739872011294401653582446605128, + 0.0241483028685479319601100262875653246916, + 0.0241483028685479319601100262875653246916 + ], + [ + 0.1691423829631435918406564701349866103341, + 0.1691423829631435918406564701349866103341, + 0.1642764837458327229860537764659275904123, + 0.1642764837458327229860537764659275904123, + 0.1546846751262652449254180038363747721932, + 0.1546846751262652449254180038363747721932, + 0.1406429146706506512047313037519472280955, + 0.1406429146706506512047313037519472280955, + 0.1225552067114784601845191268002015552281, + 0.1225552067114784601845191268002015552281, + 0.1009420441062871655628139849248346070628, + 0.1009420441062871655628139849248346070628, + 0.0764257302548890565291296776166365256053, + 0.0764257302548890565291296776166365256053, + 0.0497145488949697964533349462026386416808, + 0.0497145488949697964533349462026386416808, + 0.0216160135264833103133427102664524693876, + 0.0216160135264833103133427102664524693876 + ], + [ + 0.1610544498487836959791636253209167350399, + 0.1589688433939543476499564394650472016787, + 0.1589688433939543476499564394650472016787, + 0.152766042065859666778855400897662998461, + 0.152766042065859666778855400897662998461, + 0.1426067021736066117757461094419029724756, + 0.1426067021736066117757461094419029724756, + 0.1287539625393362276755157848568771170558, + 0.1287539625393362276755157848568771170558, + 0.1115666455473339947160239016817659974813, + 0.1115666455473339947160239016817659974813, + 0.0914900216224499994644620941238396526609, + 0.0914900216224499994644620941238396526609, + 0.0690445427376412265807082580060130449618, + 0.0690445427376412265807082580060130449618, + 0.0448142267656996003328381574019942119517, + 0.0448142267656996003328381574019942119517, + 0.0194617882297264770363120414644384357529, + 0.0194617882297264770363120414644384357529 + ], + [ + 0.1527533871307258506980843319550975934919, + 0.1527533871307258506980843319550975934919, + 0.1491729864726037467878287370019694366926, + 0.1491729864726037467878287370019694366926, + 0.1420961093183820513292983250671649330345, + 0.1420961093183820513292983250671649330345, + 0.1316886384491766268984944997481631349161, + 0.1316886384491766268984944997481631349161, + 0.118194531961518417312377377711382287005, + 0.118194531961518417312377377711382287005, + 0.1019301198172404350367501354803498761666, + 0.1019301198172404350367501354803498761666, + 0.0832767415767047487247581432220462061001, + 0.0832767415767047487247581432220462061001, + 0.0626720483341090635695065351870416063516, + 0.0626720483341090635695065351870416063516, + 0.040601429800386941331039952274932109879, + 0.040601429800386941331039952274932109879, + 0.0176140071391521183118619623518528163621, + 0.0176140071391521183118619623518528163621 + ], + [ + 0.1460811336496904271919851476833711882448, + 0.1445244039899700590638271665537525436099, + 0.1445244039899700590638271665537525436099, + 0.1398873947910731547221334238675831108927, + 0.1398873947910731547221334238675831108927, + 0.132268938633337461781052574496775604329, + 0.132268938633337461781052574496775604329, + 0.1218314160537285341953671771257335983563, + 0.1218314160537285341953671771257335983563, + 0.1087972991671483776634745780701056420336, + 0.1087972991671483776634745780701056420336, + 0.0934444234560338615532897411139320884835, + 0.0934444234560338615532897411139320884835, + 0.0761001136283793020170516533001831792261, + 0.0761001136283793020170516533001831792261, + 0.0571344254268572082836358264724479574912, + 0.0571344254268572082836358264724479574912, + 0.0369537897708524937999506682993296661889, + 0.0369537897708524937999506682993296661889, + 0.0160172282577743333242246168584710152658, + 0.0160172282577743333242246168584710152658 + ], + [ + 0.1392518728556319933754102483418099578739, + 0.1392518728556319933754102483418099578739, + 0.1365414983460151713525738312315173965863, + 0.1365414983460151713525738312315173965863, + 0.1311735047870623707329649925303074458757, + 0.1311735047870623707329649925303074458757, + 0.1232523768105124242855609861548144719594, + 0.1232523768105124242855609861548144719594, + 0.1129322960805392183934006074217843191142, + 0.1129322960805392183934006074217843191142, + 0.1004141444428809649320788378305362823508, + 0.1004141444428809649320788378305362823508, + 0.0859416062170677274144436813727028661891, + 0.0859416062170677274144436813727028661891, + 0.0697964684245204880949614189302176573987, + 0.0697964684245204880949614189302176573987, + 0.0522933351526832859403120512732112561121, + 0.0522933351526832859403120512732112561121, + 0.0337749015848141547933022468659129013491, + 0.0337749015848141547933022468659129013491, + 0.0146279952982722006849910980471854451902, + 0.0146279952982722006849910980471854451902 + ], + [ + 0.1336545721861061753514571105458443385831, + 0.132462039404696617371642464703316925805, + 0.132462039404696617371642464703316925805, + 0.1289057221880821499785953393997936532597, + 0.1289057221880821499785953393997936532597, + 0.1230490843067295304675784006720096548158, + 0.1230490843067295304675784006720096548158, + 0.1149966402224113649416435129339613014914, + 0.1149966402224113649416435129339613014914, + 0.1048920914645414100740861850147438548584, + 0.1048920914645414100740861850147438548584, + 0.0929157660600351474770186173697646486034, + 0.0929157660600351474770186173697646486034, + 0.0792814117767189549228925247420432269137, + 0.0792814117767189549228925247420432269137, + 0.0642324214085258521271696151589109980391, + 0.0642324214085258521271696151589109980391, + 0.0480376717310846685716410716320339965612, + 0.0480376717310846685716410716320339965612, + 0.0309880058569794443106942196418845053837, + 0.0309880058569794443106942196418845053837, + 0.0134118594871417720813094934586150649766, + 0.0134118594871417720813094934586150649766 + ], + [ + 0.1279381953467521569740561652246953718517, + 0.1279381953467521569740561652246953718517, + 0.1258374563468282961213753825111836887264, + 0.1258374563468282961213753825111836887264, + 0.121670472927803391204463153476262425607, + 0.121670472927803391204463153476262425607, + 0.1155056680537256013533444839067835598622, + 0.1155056680537256013533444839067835598622, + 0.1074442701159656347825773424466062227946, + 0.1074442701159656347825773424466062227946, + 0.0976186521041138882698806644642471544279, + 0.0976186521041138882698806644642471544279, + 0.086190161531953275917185202983742667185, + 0.086190161531953275917185202983742667185, + 0.0733464814110803057340336152531165181193, + 0.0733464814110803057340336152531165181193, + 0.0592985849154367807463677585001085845412, + 0.0592985849154367807463677585001085845412, + 0.0442774388174198061686027482113382288593, + 0.0442774388174198061686027482113382288593, + 0.0285313886289336631813078159518782864491, + 0.0285313886289336631813078159518782864491, + 0.0123412297999871995468056670700372915759, + 0.0123412297999871995468056670700372915759 + ] +]; + +// LUT for binomial coefficient arrays per curve order 'n' +const binomialCoefficients = [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1]]; + +// Look up what the binomial coefficient is for pair {n,k} +const binomials = (n: number, k: number) => binomialCoefficients[n][k]; + +/** + * Compute the curve derivative (hodograph) at t. + */ +const getDerivative = (derivative: number, t: number, vs: number[]): number => { + // the derivative of any 't'-less function is zero. + const n = vs.length - 1; + let value; + let k; + if (n === 0) { + return 0; + } + + // direct values? compute! + if (derivative === 0) { + value = 0; + for (k = 0; k <= n; k += 1) { + value += binomials(n, k) * (1 - t ** n - k) * t ** k * vs[k]; + } + return value; + } + // Still some derivative? go down one order, then try + // for the lower order curve's. + const vs1 = new Array(n); + for (k = 0; k < n; k += 1) { + vs1[k] = n * (vs[k + 1] - vs[k]); + } + return getDerivative(derivative - 1, t, vs1); +}; + +function B(xs: CtrlPoint, ys: CtrlPoint, t: number) { + const xbase = getDerivative(1, t, xs); + const ybase = getDerivative(1, t, ys); + const combined = xbase * xbase + ybase * ybase; + return Math.sqrt(combined); +} + +const getArcLength = ( + xs: CtrlPoint, + ys: CtrlPoint, + t: number = 1, + n: number = 20 +) => { + if (xs.length >= tValues.length) { + throw new Error("too high n bezier"); + } + const z = t / 2; + let sum = 0; + let i; + for (i = 0; i < n; i += 1) { + const correctedT = z * tValues[n][i] + z; + sum += cValues[n][i] * B(xs, ys, correctedT); + } + return z * sum; +}; + +const cubicBezierLength = ( + p0: Point, + p1: Point, + p2: Point, + p3: Point +): number => { + const xs: CtrlPoint = [p0.x, p1.x, p2.x, p3.x]; + const ys: CtrlPoint = [p0.y, p1.y, p2.y, p3.y]; + return getArcLength(xs, ys); +}; + +export default cubicBezierLength; diff --git a/src/Gesture.ts b/src/Gesture.ts index c31bf7fa..8d288cd7 100644 --- a/src/Gesture.ts +++ b/src/Gesture.ts @@ -6,6 +6,7 @@ import { runDecay, runSpring } from "./AnimationRunners"; const { Clock, Value, + event, add, block, cond, @@ -16,13 +17,12 @@ const { multiply, set, stopClock, - sub, + sub } = Animated; - export const preserveOffset = ( value: Animated.Adaptable, - state: Animated.Adaptable, + state: Animated.Adaptable ) => { const previous = new Value(0); const offset = new Value(0); @@ -31,16 +31,16 @@ export const preserveOffset = ( cond( eq(state, GestureState.BEGAN), [set(previous, 0)], - [set(offset, add(offset, sub(value, previous))), set(previous, value)], + [set(offset, add(offset, sub(value, previous))), set(previous, value)] ), - offset, + offset ]); }; export const decay = ( value: Animated.Adaptable, state: Animated.Adaptable, - velocity: Animated.Adaptable, + velocity: Animated.Adaptable ) => { const decayedValue = new Value(0); const offset = new Value(0); @@ -53,19 +53,19 @@ export const decay = ( [ set( decayedValue, - runDecay(clock, add(value, offset), velocity, rerunDecaying), - ), + runDecay(clock, add(value, offset), velocity, rerunDecaying) + ) ], [ stopClock(clock), cond(eq(state, GestureState.BEGAN), [ set(rerunDecaying, 0), - set(offset, sub(decayedValue, value)), + set(offset, sub(decayedValue, value)) ]), - set(decayedValue, add(value, offset)), - ], + set(decayedValue, add(value, offset)) + ] ), - decayedValue, + decayedValue ]); }; @@ -73,7 +73,7 @@ export const spring = ( translation: Animated.Value, state: Animated.Value, snapPoint: number, - defaultOffset: number = 0, + defaultOffset: number = 0 ) => { const springedValue = new Value(0); const offset = new Value(defaultOffset); @@ -87,19 +87,27 @@ export const spring = ( stiffness: 150, overshootClamping: false, restSpeedThreshold: 0.001, - restDisplacementThreshold: 0.001, + restDisplacementThreshold: 0.001 }; return block([ cond( eq(state, GestureState.END), - [set(springedValue, runSpring(clock, add(translation, offset), snapPoint, springConfig))], [ - stopClock(clock), - cond(eq(state, GestureState.BEGAN), [set(rerunSpring, 0), set(offset, sub(springedValue, translation))]), - set(springedValue, add(translation, offset)), + set( + springedValue, + runSpring(clock, add(translation, offset), snapPoint, springConfig) + ) ], + [ + stopClock(clock), + cond(eq(state, GestureState.BEGAN), [ + set(rerunSpring, 0), + set(offset, sub(springedValue, translation)) + ]), + set(springedValue, add(translation, offset)) + ] ), - springedValue, + springedValue ]); }; @@ -107,7 +115,7 @@ export const limit = ( value: Animated.Adaptable, state: Animated.Adaptable, min: number, - max: number, + max: number ) => { const offset = new Animated.Value(0); const offsetValue = add(offset, value); @@ -115,19 +123,19 @@ export const limit = ( return block([ cond(eq(state, GestureState.BEGAN), [ cond(lessThan(offsetValue, min), set(offset, sub(min, value))), - cond(greaterThan(offsetValue, max), set(offset, sub(max, value))), + cond(greaterThan(offsetValue, max), set(offset, sub(max, value))) ]), cond( lessThan(offsetValue, min), min, - cond(greaterThan(offsetValue, max), max, offsetValue), - ), + cond(greaterThan(offsetValue, max), max, offsetValue) + ) ]); }; export const preserveMultiplicativeOffset = ( value: Animated.Adaptable, - state: Animated.Adaptable, + state: Animated.Adaptable ) => { const previous = new Animated.Value(1); const offset = new Animated.Value(1); @@ -138,9 +146,21 @@ export const preserveMultiplicativeOffset = ( [set(previous, 1)], [ set(offset, multiply(offset, divide(value, previous))), - set(previous, value), - ], + set(previous, value) + ] ), - offset, + offset ]); }; + +export const onScroll = (contentOffset: { + x?: Animated.Node; + y?: Animated.Node; +}) => + event([ + { + nativeEvent: { + contentOffset + } + } + ]); diff --git a/src/Interactable.tsx b/src/Interactable.tsx index be5e0cf6..0bfc25c7 100644 --- a/src/Interactable.tsx +++ b/src/Interactable.tsx @@ -28,7 +28,7 @@ const { stopClock, sub, Clock, - Value, + Value } = Animated; const ANIMATOR_PAUSE_CONSECUTIVE_FRAMES = 10; @@ -47,19 +47,26 @@ function influenceAreaWithRadius(radius: number, anchor: any) { left: (anchor.x || 0) - radius, right: (anchor.x || 0) + radius, top: (anchor.y || 0) - radius, - bottom: (anchor.y || 0) + radius, + bottom: (anchor.y || 0) + radius }; } -function snapTo(target: any, snapPoints: any, best: any, clb: any, dragClb: any) { +function snapTo( + target: any, + snapPoints: any, + best: any, + clb: any, + dragClb: any +) { const dist = new Value(0); const snap = (pt: any) => [ set(best.tension, pt.tension || DEFAULT_SNAP_TENSION), set(best.damping, pt.damping || DEFAULT_SNAP_DAMPING), set(best.x, pt.x || 0), - set(best.y, pt.y || 0), + set(best.y, pt.y || 0) ]; - const snapDist = (pt: any) => add(sq(sub(target.x, pt.x || 0)), sq(sub(target.y, pt.y || 0))); + const snapDist = (pt: any) => + add(sq(sub(target.x, pt.x || 0)), sq(sub(target.y, pt.y || 0))); return [ set(dist, snapDist(snapPoints[0])), ...snap(snapPoints[0]), @@ -67,12 +74,12 @@ function snapTo(target: any, snapPoints: any, best: any, clb: any, dragClb: any) const newDist = snapDist(pt); return cond(lessThan(newDist, dist), [set(dist, newDist), ...snap(pt)]); }), - (clb || dragClb) - && call([best.x, best.y, target.x, target.y], ([bx, by, x, y]) => { + (clb || dragClb) && + call([best.x, best.y, target.x, target.y], ([bx, by, x, y]) => { snapPoints.forEach((pt: any, index: number) => { if ( - (pt.x === undefined || pt.x === bx) - && (pt.y === undefined || pt.y === by) + (pt.x === undefined || pt.x === bx) && + (pt.y === undefined || pt.y === by) ) { if (clb) { clb({ nativeEvent: { ...pt, index } }); @@ -80,24 +87,33 @@ function snapTo(target: any, snapPoints: any, best: any, clb: any, dragClb: any) if (dragClb) { dragClb({ nativeEvent: { - x, y, targetSnapPointId: pt.id, state: "end", - }, + x, + y, + targetSnapPointId: pt.id, + state: "end" + } }); } } }); - }), + }) ]; } -function springBehavior(dt: any, target: any, obj: any, anchor: any, tension: number = 300) { +function springBehavior( + dt: any, + target: any, + obj: any, + anchor: any, + tension: number = 300 +) { const dx = sub(target.x, anchor.x); const ax = divide(multiply(-1, tension, dx), obj.mass); const dy = sub(target.y, anchor.y); const ay = divide(multiply(-1, tension, dy), obj.mass); return { x: set(obj.vx, add(obj.vx, multiply(dt, ax))), - y: set(obj.vy, add(obj.vy, multiply(dt, ay))), + y: set(obj.vy, add(obj.vy, multiply(dt, ay))) }; } @@ -105,7 +121,7 @@ function frictionBehavior(dt: any, obj: any, damping: number = 0.7) { const friction = pow(damping, multiply(60, dt)); return { x: set(obj.vx, multiply(obj.vx, friction)), - y: set(obj.vy, multiply(obj.vy, friction)), + y: set(obj.vy, multiply(obj.vy, friction)) }; } @@ -114,7 +130,7 @@ function anchorBehavior(dt: any, target: any, obj: any, anchor: any) { const dy = sub(anchor.y, target.y); return { x: set(obj.vx, divide(dx, dt)), - y: set(obj.vy, divide(dy, dt)), + y: set(obj.vy, divide(dy, dt)) }; } @@ -124,7 +140,7 @@ function gravityBehavior( obj: any, anchor: any, strength: number = DEFAULT_GRAVITY_STRENGTH, - falloff: number = DEFAULT_GRAVITY_FALLOF, + falloff: number = DEFAULT_GRAVITY_FALLOF ) { const dx = sub(target.x, anchor.x); const dy = sub(target.y, anchor.y); @@ -132,12 +148,12 @@ function gravityBehavior( const dr = sqrt(drsq); const a = divide( multiply(-1, strength, dr, exp(divide(multiply(-0.5, drsq), sq(falloff)))), - obj.mass, + obj.mass ); const div = divide(a, dr); return { x: cond(dr, set(obj.vx, add(obj.vx, multiply(dt, dx, div)))), - y: cond(dr, set(obj.vy, add(obj.vy, multiply(dt, dy, div)))), + y: cond(dr, set(obj.vy, add(obj.vy, multiply(dt, dy, div)))) }; } @@ -151,7 +167,7 @@ function bounceBehavior(target: any, obj: any, area: any, bounce: number = 0) { } if (area.right !== undefined) { xnodes.push( - cond(and(eq(target.x, area.right), lessThan(0, obj.vx)), flipx), + cond(and(eq(target.x, area.right), lessThan(0, obj.vx)), flipx) ); } if (area.top !== undefined) { @@ -159,12 +175,12 @@ function bounceBehavior(target: any, obj: any, area: any, bounce: number = 0) { } if (area.bottom !== undefined) { xnodes.push( - cond(and(eq(target.y, area.bottom), lessThan(0, obj.vy)), flipy), + cond(and(eq(target.y, area.bottom), lessThan(0, obj.vy)), flipy) ); } return { x: xnodes, - y: ynodes, + y: ynodes }; } @@ -175,14 +191,15 @@ function withInfluence(area: any, target: any, behavior: any) { const testLeft = area.left === undefined || lessOrEq(area.left, target.x); const testRight = area.right === undefined || lessOrEq(target.x, area.right); const testTop = area.top === undefined || lessOrEq(area.top, target.y); - const testBottom = area.bottom === undefined || lessOrEq(target.y, area.bottom); + const testBottom = + area.bottom === undefined || lessOrEq(target.y, area.bottom); const testNodes: any[] = [testLeft, testRight, testTop, testBottom].filter( - t => t !== true, + t => t !== true ); const test = and(testNodes[0], testNodes[1], ...testNodes.slice(2)); return { x: cond(test, behavior.x), - y: cond(test, behavior.y), + y: cond(test, behavior.y) }; } @@ -259,19 +276,23 @@ interface InteractableProps { style?: StyleProp; dragEnabled: boolean; onSnap?: (e: { nativeEvent: SnapPoint & { index: number } }) => void; - onStop?: (e: { nativeEvent: { x: number, y: number } }) => void; - onDrag?: (e: { nativeEvent: { x: number, y: number, state: "start" | "end" } }) => void; + onStop?: (e: { nativeEvent: { x: number; y: number } }) => void; + onDrag?: (e: { + nativeEvent: { x: number; y: number; state: "start" | "end" }; + }) => void; initialPosition: Position; dragToss: number; - dragWithSpring?: {tension: number, damping: number}; + dragWithSpring?: { tension: number; damping: number }; boundaries?: Boundaries; } -export default class Interactable extends React.PureComponent { +export default class Interactable extends React.PureComponent< + InteractableProps +> { static defaultProps = { dragToss: 0.1, dragEnabled: true, - initialPosition: { x: 0, y: 0 }, + initialPosition: { x: 0, y: 0 } }; transY: any; @@ -281,26 +302,26 @@ export default class Interactable extends React.PureComponent onGestureEvent: any; position: { - x: any, - y: any + x: any; + y: any; }; dragging: { - x?: any, - y?: any + x?: any; + y?: any; }; snapAnchor: { - x: any, - y: any, - tension: any, - damping: any + x: any; + y: any; + tension: any; + damping: any; }; velocity: { - x?: any, - y?: any - } + x?: any; + y?: any; + }; constructor(props: InteractableProps) { super(props); @@ -313,19 +334,19 @@ export default class Interactable extends React.PureComponent nativeEvent: { translationX: gesture.x, translationY: gesture.y, - state, - }, - }, + state + } + } ]); const target = { x: new Value(props.initialPosition.x || 0), - y: new Value(props.initialPosition.y || 0), + y: new Value(props.initialPosition.y || 0) }; const update = { x: props.animatedValueX, - y: props.animatedValueY, + y: props.animatedValueY }; const clock = new Clock(); @@ -335,33 +356,38 @@ export default class Interactable extends React.PureComponent const obj = { vx: new Value(0), vy: new Value(0), - mass: 1, + mass: 1 }; const tossedTarget = { x: add(target.x, multiply(props.dragToss, obj.vx)), - y: add(target.y, multiply(props.dragToss, obj.vy)), + y: add(target.y, multiply(props.dragToss, obj.vy)) }; const permBuckets: [any[], any[], any[]] = [[], [], []]; - const addSpring = (anchor: any, tension: any, influence: any, buckets: any = permBuckets) => { + const addSpring = ( + anchor: any, + tension: any, + influence: any, + buckets: any = permBuckets + ) => { buckets[0].push( withInfluence( influence, target, - springBehavior(dt, target, obj, anchor, tension), - ), + springBehavior(dt, target, obj, anchor, tension) + ) ); }; - const addFriction = (damping: any, influence: any, buckets: any = permBuckets) => { + const addFriction = ( + damping: any, + influence: any, + buckets: any = permBuckets + ) => { buckets[1].push( - withInfluence( - influence, - target, - frictionBehavior(dt, obj, damping), - ), + withInfluence(influence, target, frictionBehavior(dt, obj, damping)) ); }; @@ -370,14 +396,14 @@ export default class Interactable extends React.PureComponent strength: any, falloff: any, influence: any, - buckets: any = permBuckets, + buckets: any = permBuckets ) => { buckets[0].push( withInfluence( influence, target, - gravityBehavior(dt, target, obj, anchor, strength, falloff), - ), + gravityBehavior(dt, target, obj, anchor, strength, falloff) + ) ); }; @@ -391,10 +417,13 @@ export default class Interactable extends React.PureComponent dragBuckets[0].push(anchorBehavior(dt, target, obj, dragAnchor)); } - const handleStartDrag = props.onDrag - && call( + const handleStartDrag = + props.onDrag && + call( [target.x, target.y], - ([x, y]) => props.onDrag && props.onDrag({ nativeEvent: { x, y, state: "start" } }), + ([x, y]) => + props.onDrag && + props.onDrag({ nativeEvent: { x, y, state: "start" } }) ); const snapBuckets: [any[], any[], any[]] = [[], [], []]; @@ -402,14 +431,14 @@ export default class Interactable extends React.PureComponent x: new Value(props.initialPosition.x || 0), y: new Value(props.initialPosition.y || 0), tension: new Value(DEFAULT_SNAP_TENSION), - damping: new Value(DEFAULT_SNAP_DAMPING), + damping: new Value(DEFAULT_SNAP_DAMPING) }; const updateSnapTo = snapTo( tossedTarget, props.snapPoints, snapAnchor, props.onSnap, - props.onDrag, + props.onDrag ); addSpring(snapAnchor, snapAnchor.tension, null, snapBuckets); @@ -424,11 +453,12 @@ export default class Interactable extends React.PureComponent }); } if (props.gravityPoints) { - props.gravityPoints.forEach((pt) => { + props.gravityPoints.forEach(pt => { const falloff = pt.falloff || DEFAULT_GRAVITY_FALLOF; addGravity(pt, pt.strength, falloff, pt.influenceArea); if (pt.damping) { - const influenceArea = pt.influenceArea || influenceAreaWithRadius(1.4 * falloff, pt); + const influenceArea = + pt.influenceArea || influenceAreaWithRadius(1.4 * falloff, pt); addFriction(pt.damping, influenceArea); } }); @@ -440,12 +470,7 @@ export default class Interactable extends React.PureComponent } if (props.boundaries) { snapBuckets[0].push( - bounceBehavior( - target, - obj, - props.boundaries, - props.boundaries.bounce, - ), + bounceBehavior(target, obj, props.boundaries, props.boundaries.bounce) ); } @@ -454,44 +479,54 @@ export default class Interactable extends React.PureComponent // front, so we join in reverse order and then reverse the array. const sortBuckets = (specialBuckets: any) => ({ x: specialBuckets - .map((b: any, idx: number) => [...permBuckets[idx], ...b].reverse().map(b1 => b1.x)) + .map((b: any, idx: number) => + [...permBuckets[idx], ...b].reverse().map(b1 => b1.x) + ) .reduce((acc: any, b2: any) => acc.concat(b2), []), y: specialBuckets - .map((b: any, idx: number) => [...permBuckets[idx], ...b].reverse().map(b1 => b1.y)) - .reduce((acc: any, b2: any) => acc.concat(b2), []), + .map((b: any, idx: number) => + [...permBuckets[idx], ...b].reverse().map(b1 => b1.y) + ) + .reduce((acc: any, b2: any) => acc.concat(b2), []) }); const dragBehaviors = sortBuckets(dragBuckets); const snapBehaviors = sortBuckets(snapBuckets); const noMovementFrames = { x: new Value( - props.verticalOnly ? ANIMATOR_PAUSE_CONSECUTIVE_FRAMES + 1 : 0, + props.verticalOnly ? ANIMATOR_PAUSE_CONSECUTIVE_FRAMES + 1 : 0 ), y: new Value( - props.horizontalOnly ? ANIMATOR_PAUSE_CONSECUTIVE_FRAMES + 1 : 0, - ), + props.horizontalOnly ? ANIMATOR_PAUSE_CONSECUTIVE_FRAMES + 1 : 0 + ) }; const stopWhenNeeded = cond( and( greaterOrEq(noMovementFrames.x, ANIMATOR_PAUSE_CONSECUTIVE_FRAMES), - greaterOrEq(noMovementFrames.y, ANIMATOR_PAUSE_CONSECUTIVE_FRAMES), + greaterOrEq(noMovementFrames.y, ANIMATOR_PAUSE_CONSECUTIVE_FRAMES) ), block([ props.onStop ? cond( - clockRunning(clock), - call([target.x, target.y], ([x, y]) => props.onStop && props.onStop({ nativeEvent: { x, y } })), - ) : [], - stopClock(clock), + clockRunning(clock), + call( + [target.x, target.y], + ([x, y]) => + props.onStop && props.onStop({ nativeEvent: { x, y } }) + ) + ) + : [], + stopClock(clock) ]), - startClock(clock), + startClock(clock) ); const trans = ( - axis: "x" | "y", vaxis: "vx" | "vy", + axis: "x" | "y", + vaxis: "vx" | "vy", lowerBound: "top" | "left" | "right" | "bottom", - upperBound: "top" | "left" | "right" | "bottom", + upperBound: "top" | "left" | "right" | "bottom" ) => { const dragging = new Value(0); const start = new Value(0); @@ -502,13 +537,13 @@ export default class Interactable extends React.PureComponent let advance = cond( lessThan(abs(vx), ANIMATOR_PAUSE_ZERO_VELOCITY), x, - add(x, multiply(vx, dt)), + add(x, multiply(vx, dt)) ); if (props.boundaries) { advance = withLimits( advance, props.boundaries[lowerBound], - props.boundaries[upperBound], + props.boundaries[upperBound] ); } const last = new Value(Number.MAX_SAFE_INTEGER); @@ -516,30 +551,37 @@ export default class Interactable extends React.PureComponent const testMovementFrames = cond( eq(advance, last), set(noMoveFrameCount, add(noMoveFrameCount, 1)), - [set(last, advance), set(noMoveFrameCount, 0)], + [set(last, advance), set(noMoveFrameCount, 0)] ); const step = cond( eq(state, State.ACTIVE), [ - cond(dragging, 0, block([ - handleStartDrag || [], - startClock(clock), - set(dragging, 1), - set(start, x), - ])), + cond( + dragging, + 0, + block([ + handleStartDrag || [], + startClock(clock), + set(dragging, 1), + set(start, x) + ]) + ), set(anchor, add(start, drag)), - cond(dt, dragBehaviors[axis]), + cond(dt, dragBehaviors[axis]) ], [ cond(clockRunning(clock), 0, startClock(clock)), cond(dragging, [updateSnapTo, set(dragging, 0)]), cond(dt, snapBehaviors[axis]), testMovementFrames, - stopWhenNeeded, - ], + stopWhenNeeded + ] ); const wrapStep = props.dragEnabled - ? cond(props.dragEnabled as any, step, [set(dragging, 1), stopClock(clock)]) + ? cond(props.dragEnabled as any, step, [ + set(dragging, 1), + stopClock(clock) + ]) : step; // export some values to be available for imperative commands @@ -563,7 +605,13 @@ export default class Interactable extends React.PureComponent } // imperative commands - setVelocity({ x, y }: { x: Animated.Adaptable, y: Animated.Adaptable }) { + setVelocity({ + x, + y + }: { + x: Animated.Adaptable; + y: Animated.Adaptable; + }) { if (x !== undefined) { this.dragging.x.setValue(1); this.velocity.x.setValue(x); @@ -592,7 +640,13 @@ export default class Interactable extends React.PureComponent } */ - changePosition({ x, y }: { x: Animated.Adaptable, y: Animated.Adaptable}) { + changePosition({ + x, + y + }: { + x: Animated.Adaptable; + y: Animated.Adaptable; + }) { if (x !== undefined) { this.dragging.x.setValue(1); this.position.x.setValue(x); @@ -606,7 +660,11 @@ export default class Interactable extends React.PureComponent render() { const { onGestureEvent } = this; const { - children, style, horizontalOnly, verticalOnly, dragEnabled: enabled, + children, + style, + horizontalOnly, + verticalOnly, + dragEnabled: enabled } = this.props; return ( transform: [ { translateX: verticalOnly ? 0 : this.transX, - translateY: horizontalOnly ? 0 : this.transY, - }, - ], - }, + translateY: horizontalOnly ? 0 : this.transY + } + ] + } ]} > {children} diff --git a/src/Math.ts b/src/Math.ts index 38da6321..18a9327c 100644 --- a/src/Math.ts +++ b/src/Math.ts @@ -11,33 +11,62 @@ const { min: min2, max: max2, greaterOrEq, + pow } = Animated; -// ## Math -export const toRad = (deg: Animated.Adaptable) => multiply(deg, Math.PI / 180); -export const toDeg = (rad: Animated.Adaptable) => multiply(rad, 180 / Math.PI); +export const min = (...args: Animated.Adaptable[]) => + args.reduce((acc, arg) => min2(acc, arg)); + +export const max = (...args: Animated.Adaptable[]) => + args.reduce((acc, arg) => max2(acc, arg)); -export const min = (...args: Animated.Adaptable[]) => args.reduce((acc, arg) => min2(acc, arg)); -export const max = (...args: Animated.Adaptable[]) => args.reduce((acc, arg) => max2(acc, arg)); export const clamp = ( value: Animated.Node, lowerBound: number, - upperBound: number, -) => min2(max2(lowerBound, value), upperBound); + upperBound: number +): Animated.Node => min2(max2(lowerBound, value), upperBound); + +export const toRad = (deg: Animated.Adaptable): Animated.Node => + multiply(deg, Math.PI / 180); + +export const toDeg = (rad: Animated.Adaptable): Animated.Node => + multiply(rad, 180 / Math.PI); -export const atan = (rad: Animated.Adaptable) => sub( - multiply(Math.PI / 4, rad), - multiply(multiply(rad, sub(abs(rad), 1)), add(0.2447, multiply(0.0663, abs(rad)))), -); +export const atan = (rad: Animated.Adaptable): Animated.Node => + sub( + multiply(Math.PI / 4, rad), + multiply( + multiply(rad, sub(abs(rad), 1)), + add(0.2447, multiply(0.0663, abs(rad))) + ) + ); -export const atan2 = (y: Animated.Adaptable, x: Animated.Adaptable) => { +export const atan2 = ( + y: Animated.Adaptable, + x: Animated.Adaptable +): Animated.Node => { const coeff1 = Math.PI / 4; const coeff2 = 3 * coeff1; const absY = abs(y); - const angle = cond(greaterOrEq(x, 0), [ - sub(coeff1, multiply(coeff1, divide(sub(x, absY), add(x, absY)))), - ], [ - sub(coeff2, multiply(coeff1, divide(add(x, absY), sub(absY, x)))), - ]); + const angle = cond( + greaterOrEq(x, 0), + [sub(coeff1, multiply(coeff1, divide(sub(x, absY), add(x, absY))))], + [sub(coeff2, multiply(coeff1, divide(add(x, absY), sub(absY, x))))] + ); return cond(lessThan(y, 0), multiply(angle, -1), angle); }; + +export const cubicBezier = ( + t: Animated.Node, + p0: Animated.Node, + p1: Animated.Node, + p2: Animated.Node, + p3: Animated.Node +): Animated.Node => { + const term = sub(1, t); + const a = multiply(1, pow(term, 3), pow(t, 0), p0); + const b = multiply(3, pow(term, 2), pow(t, 1), p1); + const c = multiply(3, pow(term, 1), pow(t, 2), p2); + const d = multiply(1, pow(term, 0), pow(t, 3), p3); + return add(a, b, c, d); +}; diff --git a/src/SVG.ts b/src/SVG.ts index 5b15cf20..8e156c80 100644 --- a/src/SVG.ts +++ b/src/SVG.ts @@ -1,84 +1,175 @@ -import * as path from "svg-path-properties"; import Animated from "react-native-reanimated"; - +import parseSVG from "parse-svg-path"; +import absSVG from "abs-svg-path"; +import normalizeSVG from "normalize-svg-path"; import { find } from "./Arrays"; +import { cubicBezier } from "./Math"; +import { bInterpolate } from "./Animations"; +import cubicBezierLength from "./CubicBezierLength"; + const { Value, - cond, - and, - greaterOrEq, lessOrEq, + greaterOrEq, + and, + cond, interpolate, - eq, + concat: reConcat } = Animated; -interface Parts { - search: { startX: number; endX: number; startY: number; endY: number; }[]; - startX: number[]; - startY: number[]; - endX: number[]; - endY: number[]; +// const COMMAND = 0; +const MX = 1; +const MY = 2; +const CX1 = 1; +const CY1 = 2; +const CX2 = 3; +const CY2 = 4; +const CX = 5; +const CY = 6; + +type SVGMoveCommand = ["M", number, number]; +type SVGCurveCommand = ["C", number, number, number, number, number, number]; +type SVGNormalizedCommands = [SVGMoveCommand, ...SVGCurveCommand[]]; + +const concat = ( + ...args: Array | Animated.Adaptable> +): Animated.Node => + reConcat(args[0] as any, args[1] as any, ...(args.slice(2) as any[])); + +interface Point { + x: number; + y: number; +} + +interface BezierCubicCurve { + length: number; + p0: Point; + p1: Point; + p2: Point; + p3: Point; } -export const getParts = (d: string): Parts => { - const properties = path.svgPathProperties(d); - const parts = properties.getParts(); - const search: ({ startX: number; endX: number; startY: number; endY: number; })[] = []; - const startX: number[] = []; - const startY: number[] = []; - const endX: number[] = []; - const endY: number[] = []; - parts.forEach((part: any) => { - search.push({ - startX: part.start.x, endX: part.end.x, startY: part.start.y, endY: part.end.y, - }); - startX.push(part.start.x); - startY.push(part.start.y); - endX.push(part.end.x); - endY.push(part.end.y); +export interface ReanimatedPath { + totalLength: number; + segments: { start: number; end: number }[]; + length: number[]; + start: number[]; + end: number[]; + p0x: number[]; + p0y: number[]; + p1x: number[]; + p1y: number[]; + p2x: number[]; + p2y: number[]; + p3x: number[]; + p3y: number[]; +} +export const parsePath = (d: string): ReanimatedPath => { + const [move, ...curves]: SVGNormalizedCommands = normalizeSVG( + absSVG(parseSVG(d)) + ); + const parts: BezierCubicCurve[] = curves.map((curve, index) => { + const prevCurve = curves[index - 1]; + const p0 = + index === 0 + ? { x: move[MX], y: move[MY] } + : { x: prevCurve[CX], y: prevCurve[CY] }; + const p1 = { x: curve[CX1], y: curve[CY1] }; + const p2 = { x: curve[CX2], y: curve[CY2] }; + const p3 = { x: curve[CX], y: curve[CY] }; + const length = cubicBezierLength(p0, p1, p2, p3); + return { + p0, + p1, + p2, + p3, + length + }; + }); + const segments = parts.map((part, index) => { + const start = parts.slice(0, index).reduce((acc, p) => acc + p.length, 0); + const end = start + part.length; + return { + start, + end + }; }); return { - search, - startX, - startY, - endX, - endY, + segments, + totalLength: parts.reduce((acc, part) => acc + part.length, 0), + length: parts.map(part => part.length), + start: segments.map(segment => segment.start), + end: segments.map(segment => segment.end), + p0x: parts.map(part => part.p0.x), + p0y: parts.map(part => part.p0.y), + p1x: parts.map(part => part.p1.x), + p1y: parts.map(part => part.p1.y), + p2x: parts.map(part => part.p2.x), + p2y: parts.map(part => part.p2.y), + p3x: parts.map(part => part.p3.x), + p3y: parts.map(part => part.p3.y) }; }; -export const getY = (d: string | Parts, x: Animated.Node): Animated.Node => { - const parts = typeof d === "string" ? getParts(d) : d; +export const getPointAtLength = ( + path: ReanimatedPath, + length: Animated.Node +): { x: Animated.Node; y: Animated.Node } => { const notFound: Animated.Node = new Value(-1); - const index = parts.search.reduce( - (acc, p, i) => cond(and(greaterOrEq(x, p.startX), lessOrEq(x, p.endX)), i, acc), - notFound, + const index = path.segments.reduce( + (acc, p, i) => + cond(and(greaterOrEq(length, p.start), lessOrEq(length, p.end)), i, acc), + notFound ); - const startX = find(parts.startX, index); - const startY = find(parts.startY, index); - const endX = find(parts.endX, index); - const endY = find(parts.endY, index); - const y = interpolate(x, { - inputRange: [startX, endX], - outputRange: [startY, endY], + const start = find(path.start, index); + const end = find(path.end, index); + + const p0x = find(path.p0x, index); + const p1x = find(path.p1x, index); + const p2x = find(path.p2x, index); + const p3x = find(path.p3x, index); + + const p0y = find(path.p0y, index); + const p1y = find(path.p1y, index); + const p2y = find(path.p2y, index); + const p3y = find(path.p3y, index); + const t = interpolate(length, { + inputRange: [start, end], + outputRange: [0, 1] }); - return cond(eq(index, notFound), notFound, y); + return { + x: cubicBezier(t, p0x, p1x, p2x, p3x), + y: cubicBezier(t, p0y, p1y, p2y, p3y) + }; }; -export const getX = (d: string | Parts, y: Animated.Node): Animated.Node => { - const parts = typeof d === "string" ? getParts(d) : d; - const notFound: Animated.Node = new Value(-1); - const index = parts.search.reduce( - (acc, p, i) => cond(and(greaterOrEq(y, p.startY), lessOrEq(y, p.endY)), i, acc), - notFound, - ); - const startX = find(parts.startX, index); - const startY = find(parts.startY, index); - const endX = find(parts.endX, index); - const endY = find(parts.endY, index); - const x = interpolate(y, { - inputRange: [startY, endY], - outputRange: [startX, endX], +export const interpolatePath = ( + path1: ReanimatedPath, + path2: ReanimatedPath, + progress: Animated.Value +): Animated.Node => { + const commands = path1.segments.map((_, index) => { + const command: Animated.Node[] = []; + if (index === 0) { + const mx = bInterpolate(progress, path1.p0x[index], path2.p0x[index]); + const my = bInterpolate(progress, path1.p0y[index], path2.p0y[index]); + command.push(concat("M", mx, ",", my, " ")); + } + + const p1x = bInterpolate(progress, path1.p1x[index], path2.p1x[index]); + const p1y = bInterpolate(progress, path1.p1y[index], path2.p1y[index]); + + const p2x = bInterpolate(progress, path1.p2x[index], path2.p2x[index]); + const p2y = bInterpolate(progress, path1.p2y[index], path2.p2y[index]); + + const p3x = bInterpolate(progress, path1.p3x[index], path2.p3x[index]); + const p3y = bInterpolate(progress, path1.p3y[index], path2.p3y[index]); + + command.push( + concat("C", p1x, ",", p1y, " ", p2x, ",", p2y, " ", p3x, ",", p3y, " ") + ); + return concat(...command); }); - return cond(eq(index, notFound), notFound, x); + return concat(...commands); }; diff --git a/src/index.d.ts b/src/index.d.ts index f24fb50c..6af3c7ae 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1 +1,3 @@ -declare module "svg-path-properties"; +declare module "parse-svg-path"; +declare module "abs-svg-path"; +declare module "normalize-svg-path"; diff --git a/src/index.ts b/src/index.ts index 6f501bcd..68ce7a2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,3 @@ -import Animated from "react-native-reanimated"; -import { min } from "./Math"; - export { default as ReText } from "./ReText"; export { default as Interactable } from "./Interactable"; export * from "./Math"; @@ -9,54 +6,4 @@ export * from "./AnimationRunners"; export * from "./Gesture"; export * from "./Arrays"; export * from "./SVG"; -const { - event, - cond, - Value, - add, - multiply, - abs, - interpolate, - divide, - sub, - eq, -} = Animated; - -// ## Animations -export const snapPoint = ( - value: Animated.Adaptable, - velocity: Animated.Adaptable, - points: number[], -) => { - const point = add(value, multiply(0.2, velocity)); - const diffPoint = (p: Animated.Adaptable) => abs(sub(point, p)); - const deltas = points.map(p => diffPoint(p)); - const minDelta = min(...deltas); - return points.reduce((acc: Animated.Node, p: number) => cond(eq(diffPoint(p), minDelta), p, acc), new Value()); -}; - -export const binaryInterpolation = ( - value: Animated.Adaptable, - origin: Animated.Adaptable, - destination: Animated.Adaptable, -) => interpolate(value, { - inputRange: [0, 1], - outputRange: [origin, destination], -}); - -// ## Transformations -export const translateZ = (perspective: Animated.Adaptable, z: Animated.Adaptable) => ( - { scale: divide(perspective, sub(perspective, z)) } -); - -// ## Gestures -export const onScroll = (contentOffset: { x?: Animated.Node, y?: Animated.Node }) => event( - [ - { - nativeEvent: { - contentOffset, - }, - }, - ], - { useNativeDriver: true }, -); +export * from "./Animations"; diff --git a/yarn.lock b/yarn.lock index a6a457e7..af4b47b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -818,6 +818,10 @@ abbrev@1, abbrev@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" +abs-svg-path@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/abs-svg-path/-/abs-svg-path-0.1.1.tgz#df601c8e8d2ba10d4a76d625e236a9a39c2723bf" + absolute-path@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" @@ -2655,17 +2659,26 @@ eslint-config-airbnb@^17.1.0: object.assign "^4.1.0" object.entries "^1.0.4" -eslint-config-react-native-wcandillon@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/eslint-config-react-native-wcandillon/-/eslint-config-react-native-wcandillon-1.0.6.tgz#867c78f8b659239a31ca749825c01e7afefabc17" +eslint-config-prettier@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-4.3.0.tgz#c55c1fcac8ce4518aeb77906984e134d9eb5a4f0" + dependencies: + get-stdin "^6.0.0" + +eslint-config-react-native-wcandillon@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/eslint-config-react-native-wcandillon/-/eslint-config-react-native-wcandillon-1.1.3.tgz#6591d58e4a4dd9fcf7057ec26766e06155da0a27" dependencies: "@typescript-eslint/eslint-plugin" "1.3.0" "@typescript-eslint/parser" "1.3.0" eslint-config-airbnb "^17.1.0" + eslint-config-prettier "^4.3.0" eslint-plugin-import "^2.14.0" eslint-plugin-jsx-a11y "^6.1.2" + eslint-plugin-prettier "^3.1.0" eslint-plugin-react "^7.12.4" eslint-plugin-react-native "^3.6.0" + prettier "^1.17.1" typescript "^3.2.4" eslint-import-resolver-node@^0.3.2: @@ -2711,6 +2724,12 @@ eslint-plugin-jsx-a11y@^6.1.2: has "^1.0.3" jsx-ast-utils "^2.0.1" +eslint-plugin-prettier@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz#8695188f95daa93b0dc54b249347ca3b79c4686d" + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-plugin-react-native-globals@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz#ee1348bc2ceb912303ce6bdbd22e2f045ea86ea2" @@ -2994,6 +3013,10 @@ fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + fast-glob@^2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.6.tgz#a5d5b697ec8deda468d85a74035290a025a95295" @@ -3325,6 +3348,10 @@ get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -5342,6 +5369,12 @@ normalize-path@^2.0.1, normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" +normalize-svg-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/normalize-svg-path/-/normalize-svg-path-1.0.1.tgz#6f729ad6b70bb4ca4eff2fe4b107489efe1d56fe" + dependencies: + svg-arc-to-cubic-bezier "^3.0.0" + normalize-url@^4.0.0: version "4.3.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.3.0.tgz#9c49e10fc1876aeb76dba88bf1b2b5d9fa57b2ee" @@ -5904,6 +5937,10 @@ parse-node-version@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" +parse-svg-path@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/parse-svg-path/-/parse-svg-path-0.1.2.tgz#7a7ec0d1eb06fa5325c7d3e009b859a09b5d49eb" + parseurl@~1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -6033,6 +6070,16 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + dependencies: + fast-diff "^1.1.2" + +prettier@^1.17.1: + version "1.17.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.1.tgz#ed64b4e93e370cb8a25b9ef7fef3e4fd1c0995db" + pretty-format@24.0.0-alpha.6: version "24.0.0-alpha.6" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.0.0-alpha.6.tgz#25ad2fa46b342d6278bf241c5d2114d4376fbac1" @@ -7307,9 +7354,9 @@ supports-hyperlinks@^1.0.1: has-flag "^2.0.0" supports-color "^5.0.0" -svg-path-properties@0.4.8: - version "0.4.8" - resolved "https://registry.yarnpkg.com/svg-path-properties/-/svg-path-properties-0.4.8.tgz#376a833e4bfdc414045532f4d2ee3c79ea0b998d" +svg-arc-to-cubic-bezier@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.1.3.tgz#7b5f76445310ef03238964c39ae7dbc761fda879" table@^5.2.3: version "5.2.3"