mirror of
https://github.com/zhigang1992/react-native-reanimated.git
synced 2026-01-12 17:42:44 +08:00
Interpolate method, input validation, linting, refactoring. (#17)
Interpolate validations are taken from `Animated`.
This commit is contained in:
committed by
Krzysztof Magiera
parent
401227f8f1
commit
ad8b0cf534
17
.eslintrc.js
Normal file
17
.eslintrc.js
Normal file
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
parser: 'babel-eslint',
|
||||
extends: [
|
||||
'standard',
|
||||
'prettier',
|
||||
'prettier/flowtype',
|
||||
'prettier/react',
|
||||
'prettier/standard',
|
||||
],
|
||||
plugins: ['react', 'react-native', 'import'],
|
||||
env: {
|
||||
'react-native/react-native': true,
|
||||
},
|
||||
rules: {
|
||||
'import/no-unresolved': 2,
|
||||
},
|
||||
};
|
||||
@@ -12,7 +12,6 @@ const {
|
||||
eq,
|
||||
and,
|
||||
add,
|
||||
call,
|
||||
multiply,
|
||||
lessThan,
|
||||
startClock,
|
||||
@@ -20,13 +19,12 @@ const {
|
||||
clockRunning,
|
||||
block,
|
||||
timing,
|
||||
debug,
|
||||
spring,
|
||||
Value,
|
||||
Clock,
|
||||
event,
|
||||
interpolate,
|
||||
defined,
|
||||
Extrapolate,
|
||||
} = Animated;
|
||||
|
||||
function runSpring(clock, value, velocity, dest) {
|
||||
@@ -60,41 +58,6 @@ function runSpring(clock, value, velocity, dest) {
|
||||
];
|
||||
}
|
||||
|
||||
function runTiming(clock, value, dest) {
|
||||
const state = {
|
||||
finished: new Value(1),
|
||||
position: new Value(value),
|
||||
time: new Value(0),
|
||||
frameTime: new Value(0),
|
||||
};
|
||||
|
||||
const config = {
|
||||
duration: 500,
|
||||
toValue: new Value(0),
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
};
|
||||
|
||||
const reset = [
|
||||
set(state.finished, 0),
|
||||
set(state.time, 0),
|
||||
set(state.frameTime, 0),
|
||||
];
|
||||
|
||||
return block([
|
||||
cond(and(state.finished, eq(state.position, value)), [
|
||||
...reset,
|
||||
set(config.toValue, dest),
|
||||
]),
|
||||
cond(and(state.finished, eq(state.position, dest)), [
|
||||
...reset,
|
||||
set(config.toValue, value),
|
||||
]),
|
||||
cond(clockRunning(clock), 0, startClock(clock)),
|
||||
timing(clock, state, config),
|
||||
state.position,
|
||||
]);
|
||||
}
|
||||
|
||||
const getAnimation = (min, max) => {
|
||||
const clock = new Clock();
|
||||
const state = {
|
||||
@@ -173,7 +136,7 @@ export default class AnimatedBounds extends Component {
|
||||
this._transX = interpolate(this._transX, {
|
||||
inputRange: [-100, 100],
|
||||
outputRange: [-100, 100],
|
||||
extrapolate: 'clamp',
|
||||
extrapolate: Extrapolate.CLAMP,
|
||||
});
|
||||
|
||||
const min = getAnimation(-100, -50);
|
||||
@@ -181,7 +144,7 @@ export default class AnimatedBounds extends Component {
|
||||
this._transXA = interpolate(this._transX, {
|
||||
inputRange: [-100, 100],
|
||||
outputRange: [min, max],
|
||||
extrapolate: 'clamp',
|
||||
extrapolate: Extrapolate.CLAMP,
|
||||
});
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import Animated, { Easing } from 'react-native-reanimated';
|
||||
import Box from '../Box';
|
||||
import Row from '../Row';
|
||||
@@ -9,20 +8,12 @@ const {
|
||||
cond,
|
||||
eq,
|
||||
and,
|
||||
add,
|
||||
call,
|
||||
multiply,
|
||||
lessThan,
|
||||
startClock,
|
||||
stopClock,
|
||||
clockRunning,
|
||||
block,
|
||||
timing,
|
||||
debug,
|
||||
spring,
|
||||
Value,
|
||||
Clock,
|
||||
event,
|
||||
interpolate,
|
||||
} = Animated;
|
||||
|
||||
|
||||
@@ -10,18 +10,12 @@ const {
|
||||
cond,
|
||||
sub,
|
||||
eq,
|
||||
and,
|
||||
add,
|
||||
call,
|
||||
multiply,
|
||||
lessThan,
|
||||
startClock,
|
||||
stopClock,
|
||||
clockRunning,
|
||||
block,
|
||||
timing,
|
||||
debug,
|
||||
spring,
|
||||
Value,
|
||||
Clock,
|
||||
event,
|
||||
@@ -60,41 +54,6 @@ function runSpring(clock, value, velocity, dest) {
|
||||
];
|
||||
}
|
||||
|
||||
function runTiming(clock, value, dest) {
|
||||
const state = {
|
||||
finished: new Value(1),
|
||||
position: new Value(value),
|
||||
time: new Value(0),
|
||||
frameTime: new Value(0),
|
||||
};
|
||||
|
||||
const config = {
|
||||
duration: 500,
|
||||
toValue: new Value(0),
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
};
|
||||
|
||||
const reset = [
|
||||
set(state.finished, 0),
|
||||
set(state.time, 0),
|
||||
set(state.frameTime, 0),
|
||||
];
|
||||
|
||||
return block([
|
||||
cond(and(state.finished, eq(state.position, value)), [
|
||||
...reset,
|
||||
set(config.toValue, dest),
|
||||
]),
|
||||
cond(and(state.finished, eq(state.position, dest)), [
|
||||
...reset,
|
||||
set(config.toValue, value),
|
||||
]),
|
||||
cond(clockRunning(clock), 0, startClock(clock)),
|
||||
timing(clock, state, config),
|
||||
state.position,
|
||||
]);
|
||||
}
|
||||
|
||||
export default class WithDrag extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
17
package.json
17
package.json
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "react-native-reanimated",
|
||||
"version": "1.0.0-alpha.3",
|
||||
"description":
|
||||
"More powerfull alternative to Animated library for React Native",
|
||||
"description": "More powerfull alternative to Animated library for React Native",
|
||||
"scripts": {
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"test": "jest",
|
||||
"format": "prettier --write './src/**'",
|
||||
"lint": "eslint --fix './src/**'",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"main": "src/Animated.js",
|
||||
@@ -40,8 +41,18 @@
|
||||
"preset": "jest-react-native"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^8.2.3",
|
||||
"babel-jest": "16.0.0",
|
||||
"babel-preset-react-native": "1.9.0",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-config-standard": "^11.0.0",
|
||||
"eslint-plugin-import": "^2.12.0",
|
||||
"eslint-plugin-node": "^6.0.1",
|
||||
"eslint-plugin-promise": "^3.8.0",
|
||||
"eslint-plugin-react": "^7.8.2",
|
||||
"eslint-plugin-react-native": "^3.2.1",
|
||||
"eslint-plugin-standard": "^3.1.0",
|
||||
"husky": "^0.14.3",
|
||||
"jest": "16.0.2",
|
||||
"jest-react-native": "16.0.0",
|
||||
@@ -52,7 +63,7 @@
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"prettier --write --print-width 80 --tab-width 2 --single-quote --jsx-bracket-same-line=true --trailing-comma=es5",
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
|
||||
7
prettier.config.js
Normal file
7
prettier.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
printWidth: 80,
|
||||
singleQuote: true,
|
||||
trailingComma: 'es5',
|
||||
jsxBracketSameLine: true,
|
||||
tabWidth: 2,
|
||||
};
|
||||
@@ -15,7 +15,6 @@ let NATIVE_PROPS_WHITELIST = {
|
||||
borderColor: true,
|
||||
borderEndColor: true,
|
||||
borderLeftColor: true,
|
||||
backgroundColor: true,
|
||||
borderStartColor: true,
|
||||
borderTopColor: true,
|
||||
/* ios styles */
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
greaterThan,
|
||||
cond,
|
||||
greaterOrEq,
|
||||
lessThan,
|
||||
multiply,
|
||||
pow,
|
||||
@@ -9,7 +7,6 @@ import {
|
||||
sqrt,
|
||||
sub,
|
||||
add,
|
||||
debug,
|
||||
divide,
|
||||
} from './base';
|
||||
import AnimatedBezier from './core/AnimatedBezier';
|
||||
|
||||
@@ -33,9 +33,9 @@ function getSlope(aT, aA1, aA2) {
|
||||
}
|
||||
|
||||
function binarySubdivide(aX, aA, aB, mX1, mX2) {
|
||||
var currentX,
|
||||
currentT,
|
||||
i = 0;
|
||||
var currentX = 0;
|
||||
var currentT = 0;
|
||||
var i = 0;
|
||||
do {
|
||||
currentT = aA + (aB - aA) / 2.0;
|
||||
currentX = calcBezier(currentT, mX1, mX2) - aX;
|
||||
@@ -64,8 +64,7 @@ function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
|
||||
}
|
||||
|
||||
function bezier(mX1, mY1, mX2, mY2) {
|
||||
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
|
||||
// eslint-disable-line yoda
|
||||
if (!(mX1 >= 0 && mX1 <= 1 && mX2 >= 0 && mX2 <= 1)) {
|
||||
throw new Error('bezier x values must be in [0, 1] range');
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ export default class AnimatedNode {
|
||||
}
|
||||
|
||||
__onEvaluate() {
|
||||
throw new Excaption('Missing implementation of onEvaluate');
|
||||
throw new Error('Missing implementation of onEvaluate');
|
||||
}
|
||||
|
||||
__getProps() {
|
||||
|
||||
@@ -40,10 +40,12 @@ const OPERATIONS = {
|
||||
|
||||
// comparing
|
||||
lessThan: infix((a, b) => a < b),
|
||||
/* eslint-disable-next-line eqeqeq */
|
||||
eq: infix((a, b) => a == b),
|
||||
greaterThan: infix((a, b) => a > b),
|
||||
lessOrEq: infix((a, b) => a <= b),
|
||||
greaterOrEq: infix((a, b) => a >= b),
|
||||
/* eslint-disable-next-line eqeqeq */
|
||||
neq: infix((a, b) => a != b),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import AnimatedNode from './AnimatedNode';
|
||||
import { val } from '../utils';
|
||||
import interpolate from '../derived/interpolate';
|
||||
|
||||
function sanitizeValue(value) {
|
||||
return value === null || value === undefined ? value : Number(value);
|
||||
@@ -27,4 +28,8 @@ export default class AnimatedValue extends AnimatedNode {
|
||||
this._value = value;
|
||||
this.__forceUpdateCache(value);
|
||||
}
|
||||
|
||||
interpolate(config) {
|
||||
return interpolate(this, config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export default function createAnimatedComponent(Component) {
|
||||
|
||||
_eventDetachers = [];
|
||||
|
||||
/* eslint-disable-next-line camelcase */
|
||||
static __skipSetNativeProps_FOR_TESTS_ONLY = false;
|
||||
|
||||
constructor(props) {
|
||||
|
||||
5
src/derived/abs.js
Normal file
5
src/derived/abs.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { cond, lessThan, multiply } from '../base';
|
||||
|
||||
export default function abs(a) {
|
||||
return cond(lessThan(a, 0), multiply(-1, a), a);
|
||||
}
|
||||
7
src/derived/acc.js
Normal file
7
src/derived/acc.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { set, add } from '../base';
|
||||
import AnimatedValue from '../core/AnimatedValue';
|
||||
|
||||
export default function acc(v) {
|
||||
const acc = new AnimatedValue(0);
|
||||
return set(acc, add(acc, v));
|
||||
}
|
||||
26
src/derived/color.js
Normal file
26
src/derived/color.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { cond, lessThan, multiply, round, add, sub } from '../base';
|
||||
import { Platform } from 'react-native';
|
||||
import AnimatedValue from '../core/AnimatedValue';
|
||||
|
||||
export default function color(r, g, b, a = 1) {
|
||||
if (a instanceof AnimatedValue) {
|
||||
a = round(multiply(a, 255));
|
||||
} else {
|
||||
a = Math.round(a * 255);
|
||||
}
|
||||
const color = add(
|
||||
multiply(a, 1 << 24),
|
||||
multiply(r, 1 << 16),
|
||||
multiply(g, 1 << 8),
|
||||
b
|
||||
);
|
||||
if (Platform.OS === 'android') {
|
||||
// on Android color is represented as signed 32 bit int
|
||||
return cond(
|
||||
lessThan(color, (1 << 31) >>> 0),
|
||||
color,
|
||||
sub(color, Math.pow(2, 32))
|
||||
);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
12
src/derived/diff.js
Normal file
12
src/derived/diff.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { cond, block, defined, sub, set } from '../base';
|
||||
import AnimatedValue from '../core/AnimatedValue';
|
||||
|
||||
export default function diff(v) {
|
||||
const stash = new AnimatedValue(0);
|
||||
const prev = new AnimatedValue();
|
||||
return block([
|
||||
set(stash, cond(defined(prev), sub(v, prev), 0)),
|
||||
set(prev, v),
|
||||
stash,
|
||||
]);
|
||||
}
|
||||
13
src/derived/diffClamp.js
Normal file
13
src/derived/diffClamp.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { cond, defined, set, add } from '../base';
|
||||
import AnimatedValue from '../core/AnimatedValue';
|
||||
import { min } from './min';
|
||||
import { max } from './max';
|
||||
import { diff } from './diff';
|
||||
|
||||
export default function diffClamp(a, minVal, maxVal) {
|
||||
const value = new AnimatedValue();
|
||||
return set(
|
||||
value,
|
||||
min(max(add(cond(defined(value), value, a), diff(a)), minVal), maxVal)
|
||||
);
|
||||
}
|
||||
8
src/derived/index.js
Normal file
8
src/derived/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export { default as abs } from './abs';
|
||||
export { default as acc } from './acc';
|
||||
export { default as color } from './color';
|
||||
export { default as diff } from './diff';
|
||||
export { default as diffClamp } from './diffClamp';
|
||||
export { default as interpolate, Extrapolate } from './interpolate';
|
||||
export { default as max } from './max';
|
||||
export { default as min } from './min';
|
||||
@@ -1,102 +1,25 @@
|
||||
import { Platform } from 'react-native';
|
||||
import {
|
||||
cond,
|
||||
lessThan,
|
||||
greaterThan,
|
||||
multiply,
|
||||
block,
|
||||
defined,
|
||||
sub,
|
||||
set,
|
||||
add,
|
||||
divide,
|
||||
round,
|
||||
} from './base';
|
||||
import AnimatedValue from './core/AnimatedValue';
|
||||
import { adapt } from './utils';
|
||||
greaterThan,
|
||||
} from '../base';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import AnimatedNode from '../core/AnimatedNode';
|
||||
|
||||
export const abs = function(a) {
|
||||
return cond(lessThan(a, 0), multiply(-1, a), a);
|
||||
};
|
||||
|
||||
export const min = function(a, b) {
|
||||
a = adapt(a);
|
||||
b = adapt(b);
|
||||
return cond(lessThan(a, b), a, b);
|
||||
};
|
||||
|
||||
export const max = function(a, b) {
|
||||
a = adapt(a);
|
||||
b = adapt(b);
|
||||
return cond(lessThan(a, b), b, a);
|
||||
};
|
||||
|
||||
export const diff = function(v) {
|
||||
const stash = new AnimatedValue(0);
|
||||
const prev = new AnimatedValue();
|
||||
return block([
|
||||
set(stash, cond(defined(prev), sub(v, prev), 0)),
|
||||
set(prev, v),
|
||||
stash,
|
||||
]);
|
||||
};
|
||||
|
||||
export const color = function(r, g, b, a = 1) {
|
||||
if (a instanceof AnimatedValue) {
|
||||
a = round(multiply(a, 255));
|
||||
} else {
|
||||
a = Math.round(a * 255);
|
||||
}
|
||||
const color = add(
|
||||
multiply(a, 1 << 24),
|
||||
multiply(r, 1 << 16),
|
||||
multiply(g, 1 << 8),
|
||||
b
|
||||
);
|
||||
if (Platform.OS === 'android') {
|
||||
// on Android color is represented as signed 32 bit int
|
||||
return cond(
|
||||
lessThan(color, (1 << 31) >>> 0),
|
||||
color,
|
||||
sub(color, Math.pow(2, 32))
|
||||
);
|
||||
}
|
||||
return color;
|
||||
};
|
||||
|
||||
export const acc = function(v) {
|
||||
const acc = new AnimatedValue(0);
|
||||
return set(acc, add(acc, v));
|
||||
};
|
||||
|
||||
export const diffClamp = function(a, minVal, maxVal) {
|
||||
const value = new AnimatedValue();
|
||||
return set(
|
||||
value,
|
||||
min(max(add(cond(defined(value), value, a), diff(a)), minVal), maxVal)
|
||||
);
|
||||
};
|
||||
|
||||
const interpolateInternalSingle = function(
|
||||
value,
|
||||
inputRange,
|
||||
outputRange,
|
||||
offset
|
||||
) {
|
||||
function interpolateInternalSingle(value, inputRange, outputRange, offset) {
|
||||
const inS = inputRange[offset];
|
||||
const inE = inputRange[offset + 1];
|
||||
const outS = outputRange[offset];
|
||||
const outE = outputRange[offset + 1];
|
||||
const progress = divide(sub(value, inS), sub(inE, inS));
|
||||
return add(outS, multiply(progress, sub(outE, outS)));
|
||||
};
|
||||
}
|
||||
|
||||
const interpolateInternal = function(
|
||||
value,
|
||||
inputRange,
|
||||
outputRange,
|
||||
offset = 0
|
||||
) {
|
||||
function interpolateInternal(value, inputRange, outputRange, offset = 0) {
|
||||
if (inputRange.length - offset === 2) {
|
||||
return interpolateInternalSingle(value, inputRange, outputRange, offset);
|
||||
}
|
||||
@@ -105,7 +28,7 @@ const interpolateInternal = function(
|
||||
interpolateInternalSingle(value, inputRange, outputRange, offset),
|
||||
interpolateInternal(value, inputRange, outputRange, offset + 1)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const Extrapolate = {
|
||||
EXTEND: 'extend',
|
||||
@@ -113,7 +36,44 @@ export const Extrapolate = {
|
||||
IDENTITY: 'identity',
|
||||
};
|
||||
|
||||
export const interpolate = function(value, config) {
|
||||
function checkNonDecreasing(name, arr) {
|
||||
for (let i = 1; i < arr.length; ++i) {
|
||||
// We can't validate animated nodes in JS.
|
||||
if (arr[i] instanceof AnimatedNode || arr[i - 1] instanceof AnimatedNode)
|
||||
continue;
|
||||
invariant(
|
||||
arr[i] >= arr[i - 1],
|
||||
'%s must be monotonically non-decreasing. (%s)',
|
||||
name,
|
||||
arr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function checkMinElements(name, arr) {
|
||||
invariant(
|
||||
arr.length >= 2,
|
||||
'%s must have at least 2 elements. (%s)',
|
||||
name,
|
||||
arr
|
||||
);
|
||||
}
|
||||
|
||||
function checkValidNumbers(name, arr) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
// We can't validate animated nodes in JS.
|
||||
if (arr[i] instanceof AnimatedNode) continue;
|
||||
invariant(
|
||||
Number.isFinite(arr[i]),
|
||||
'%s cannot include %s. (%s)',
|
||||
name,
|
||||
arr[i],
|
||||
arr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function interpolate(value, config) {
|
||||
const {
|
||||
inputRange,
|
||||
outputRange,
|
||||
@@ -121,6 +81,16 @@ export const interpolate = function(value, config) {
|
||||
extrapolateLeft,
|
||||
extrapolateRight,
|
||||
} = config;
|
||||
checkMinElements('inputRange', inputRange);
|
||||
checkValidNumbers('inputRange', inputRange);
|
||||
checkMinElements('outputRange', outputRange);
|
||||
checkValidNumbers('outputRange', outputRange);
|
||||
checkNonDecreasing('inputRange', inputRange);
|
||||
invariant(
|
||||
inputRange.length === outputRange.length,
|
||||
'inputRange and outputRange must be the same length.'
|
||||
);
|
||||
|
||||
const left = extrapolateLeft || extrapolate;
|
||||
const right = extrapolateRight || extrapolate;
|
||||
let output = interpolateInternal(value, inputRange, outputRange);
|
||||
@@ -148,4 +118,4 @@ export const interpolate = function(value, config) {
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
}
|
||||
8
src/derived/max.js
Normal file
8
src/derived/max.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { cond, lessThan } from '../base';
|
||||
import { adapt } from '../utils';
|
||||
|
||||
export default function max(a, b) {
|
||||
a = adapt(a);
|
||||
b = adapt(b);
|
||||
return cond(lessThan(a, b), b, a);
|
||||
}
|
||||
8
src/derived/min.js
Normal file
8
src/derived/min.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { cond, lessThan } from '../base';
|
||||
import { adapt } from '../utils';
|
||||
|
||||
export default function min(a, b) {
|
||||
a = adapt(a);
|
||||
b = adapt(b);
|
||||
return cond(lessThan(a, b), a, b);
|
||||
}
|
||||
Reference in New Issue
Block a user