diff --git a/examples/webpack.config.js b/examples/webpack.config.js index a4da0a30..fd223f49 100644 --- a/examples/webpack.config.js +++ b/examples/webpack.config.js @@ -28,6 +28,11 @@ module.exports = { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development') }), new webpack.optimize.DedupePlugin(), + // https://github.com/animatedjs/animated/issues/40 + new webpack.NormalModuleReplacementPlugin( + /es6-set/, + path.join(__dirname, '../src/modules/polyfills/Set.js') + ), new webpack.optimize.OccurenceOrderPlugin() ], resolve: { diff --git a/package.json b/package.json index 995e874d..db94b401 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "test:watch": "npm run test -- --no-single-run" }, "dependencies": { + "animated": "^0.1.3", "babel-runtime": "^6.9.2", "fbjs": "^0.8.1", "inline-style-prefix-all": "^2.0.2", diff --git a/src/apis/Animated/AnimatedImplementation.js b/src/apis/Animated/AnimatedImplementation.js deleted file mode 100644 index eb753abb..00000000 --- a/src/apis/Animated/AnimatedImplementation.js +++ /dev/null @@ -1,1654 +0,0 @@ -/* eslint-disable */ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule AnimatedImplementation - * @flow - */ -'use strict'; - -/* @edit start */ -import Easing from '../Easing'; -import flattenStyle from '../StyleSheet/flattenStyle'; -import InteractionManager from '../InteractionManager'; -import Interpolation from './Interpolation'; -import invariant from 'fbjs/lib/invariant'; -import React from 'react'; -import Set from './polyfills/Set'; -import SpringConfig from './SpringConfig'; -import ViewStylePropTypes from '../../components/View/ViewStylePropTypes'; - -import type { InterpolationConfigType } from './Interpolation'; -/* @edit end */ - -type EndResult = {finished: bool}; -type EndCallback = (result: EndResult) => void; - -// Note(vjeux): this would be better as an interface but flow doesn't -// support them yet -class Animated { - __attach(): void {} - __detach(): void {} - __getValue(): any {} - __getAnimatedValue(): any { return this.__getValue(); } - __addChild(child: Animated) {} - __removeChild(child: Animated) {} - __getChildren(): Array { return []; } -} - -type AnimationConfig = { - isInteraction?: bool; -}; - -// Important note: start() and stop() will only be called at most once. -// Once an animation has been stopped or finished its course, it will -// not be reused. -class Animation { - __active: bool; - __isInteraction: bool; - __onEnd: ?EndCallback; - start( - fromValue: number, - onUpdate: (value: number) => void, - onEnd: ?EndCallback, - previousAnimation: ?Animation, - ): void {} - stop(): void {} - // Helper function for subclasses to make sure onEnd is only called once. - __debouncedOnEnd(result: EndResult) { - var onEnd = this.__onEnd; - this.__onEnd = null; - onEnd && onEnd(result); - } -} - -class AnimatedWithChildren extends Animated { - _children: Array; - - constructor() { - super(); - this._children = []; - } - - __addChild(child: Animated): void { - if (this._children.length === 0) { - this.__attach(); - } - this._children.push(child); - } - - __removeChild(child: Animated): void { - var index = this._children.indexOf(child); - if (index === -1) { - console.warn('Trying to remove a child that doesn\'t exist'); - return; - } - this._children.splice(index, 1); - if (this._children.length === 0) { - this.__detach(); - } - } - - __getChildren(): Array { - return this._children; - } -} - -/** - * Animated works by building a directed acyclic graph of dependencies - * transparently when you render your Animated components. - * - * new Animated.Value(0) - * .interpolate() .interpolate() new Animated.Value(1) - * opacity translateY scale - * style transform - * View#234 style - * View#123 - * - * A) Top Down phase - * When an Animated.Value is updated, we recursively go down through this - * graph in order to find leaf nodes: the views that we flag as needing - * an update. - * - * B) Bottom Up phase - * When a view is flagged as needing an update, we recursively go back up - * in order to build the new value that it needs. The reason why we need - * this two-phases process is to deal with composite props such as - * transform which can receive values from multiple parents. - */ -function _flush(rootNode: AnimatedValue): void { - var animatedStyles = new Set(); - function findAnimatedStyles(node) { - if (typeof node.update === 'function') { - animatedStyles.add(node); - } else { - node.__getChildren().forEach(findAnimatedStyles); - } - } - findAnimatedStyles(rootNode); - animatedStyles.forEach(animatedStyle => animatedStyle.update()); -} - -type TimingAnimationConfig = AnimationConfig & { - toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY; - easing?: (value: number) => number; - duration?: number; - delay?: number; -}; - -type TimingAnimationConfigSingle = AnimationConfig & { - toValue: number | AnimatedValue; - easing?: (value: number) => number; - duration?: number; - delay?: number; -}; - -var easeInOut = Easing.inOut(Easing.ease); - -class TimingAnimation extends Animation { - _startTime: number; - _fromValue: number; - _toValue: any; - _duration: number; - _delay: number; - _easing: (value: number) => number; - _onUpdate: (value: number) => void; - _animationFrame: any; - _timeout: any; - - constructor( - config: TimingAnimationConfigSingle, - ) { - super(); - this._toValue = config.toValue; - this._easing = config.easing !== undefined ? config.easing : easeInOut; - this._duration = config.duration !== undefined ? config.duration : 500; - this._delay = config.delay !== undefined ? config.delay : 0; - this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; - } - - start( - fromValue: number, - onUpdate: (value: number) => void, - onEnd: ?EndCallback, - ): void { - this.__active = true; - this._fromValue = fromValue; - this._onUpdate = onUpdate; - this.__onEnd = onEnd; - - var start = () => { - if (this._duration === 0) { - this._onUpdate(this._toValue); - this.__debouncedOnEnd({finished: true}); - } else { - this._startTime = Date.now(); - this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this)); - } - }; - if (this._delay) { - this._timeout = setTimeout(start, this._delay); - } else { - start(); - } - } - - onUpdate(): void { - var now = Date.now(); - if (now >= this._startTime + this._duration) { - if (this._duration === 0) { - this._onUpdate(this._toValue); - } else { - this._onUpdate( - this._fromValue + this._easing(1) * (this._toValue - this._fromValue) - ); - } - this.__debouncedOnEnd({finished: true}); - return; - } - - this._onUpdate( - this._fromValue + - this._easing((now - this._startTime) / this._duration) * - (this._toValue - this._fromValue) - ); - if (this.__active) { - this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this)); - } - } - - stop(): void { - this.__active = false; - clearTimeout(this._timeout); - window.cancelAnimationFrame(this._animationFrame); - this.__debouncedOnEnd({finished: false}); - } -} - -type DecayAnimationConfig = AnimationConfig & { - velocity: number | {x: number, y: number}; - deceleration?: number; -}; - -type DecayAnimationConfigSingle = AnimationConfig & { - velocity: number; - deceleration?: number; -}; - -class DecayAnimation extends Animation { - _startTime: number; - _lastValue: number; - _fromValue: number; - _deceleration: number; - _velocity: number; - _onUpdate: (value: number) => void; - _animationFrame: any; - - constructor( - config: DecayAnimationConfigSingle, - ) { - super(); - this._deceleration = config.deceleration !== undefined ? config.deceleration : 0.998; - this._velocity = config.velocity; - this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; - } - - start( - fromValue: number, - onUpdate: (value: number) => void, - onEnd: ?EndCallback, - ): void { - this.__active = true; - this._lastValue = fromValue; - this._fromValue = fromValue; - this._onUpdate = onUpdate; - this.__onEnd = onEnd; - this._startTime = Date.now(); - this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this)); - } - - onUpdate(): void { - var now = Date.now(); - - var value = this._fromValue + - (this._velocity / (1 - this._deceleration)) * - (1 - Math.exp(-(1 - this._deceleration) * (now - this._startTime))); - - this._onUpdate(value); - - if (Math.abs(this._lastValue - value) < 0.1) { - this.__debouncedOnEnd({finished: true}); - return; - } - - this._lastValue = value; - if (this.__active) { - this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this)); - } - } - - stop(): void { - this.__active = false; - window.cancelAnimationFrame(this._animationFrame); - this.__debouncedOnEnd({finished: false}); - } -} - -type SpringAnimationConfig = AnimationConfig & { - toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY; - overshootClamping?: bool; - restDisplacementThreshold?: number; - restSpeedThreshold?: number; - velocity?: number | {x: number, y: number}; - bounciness?: number; - speed?: number; - tension?: number; - friction?: number; -}; - -type SpringAnimationConfigSingle = AnimationConfig & { - toValue: number | AnimatedValue; - overshootClamping?: bool; - restDisplacementThreshold?: number; - restSpeedThreshold?: number; - velocity?: number; - bounciness?: number; - speed?: number; - tension?: number; - friction?: number; -}; - -function withDefault(value: ?T, defaultValue: T): T { - if (value === undefined || value === null) { - return defaultValue; - } - return value; -} - -class SpringAnimation extends Animation { - _overshootClamping: bool; - _restDisplacementThreshold: number; - _restSpeedThreshold: number; - _initialVelocity: ?number; - _lastVelocity: number; - _startPosition: number; - _lastPosition: number; - _fromValue: number; - _toValue: any; - _tension: number; - _friction: number; - _lastTime: number; - _onUpdate: (value: number) => void; - _animationFrame: any; - - constructor( - config: SpringAnimationConfigSingle, - ) { - super(); - - this._overshootClamping = withDefault(config.overshootClamping, false); - this._restDisplacementThreshold = withDefault(config.restDisplacementThreshold, 0.001); - this._restSpeedThreshold = withDefault(config.restSpeedThreshold, 0.001); - this._initialVelocity = config.velocity; - this._lastVelocity = withDefault(config.velocity, 0); - this._toValue = config.toValue; - this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; - - var springConfig; - if (config.bounciness !== undefined || config.speed !== undefined) { - invariant( - config.tension === undefined && config.friction === undefined, - 'You can only define bounciness/speed or tension/friction but not both', - ); - springConfig = SpringConfig.fromBouncinessAndSpeed( - withDefault(config.bounciness, 8), - withDefault(config.speed, 12), - ); - } else { - springConfig = SpringConfig.fromOrigamiTensionAndFriction( - withDefault(config.tension, 40), - withDefault(config.friction, 7), - ); - } - this._tension = springConfig.tension; - this._friction = springConfig.friction; - } - - start( - fromValue: number, - onUpdate: (value: number) => void, - onEnd: ?EndCallback, - previousAnimation: ?Animation, - ): void { - this.__active = true; - this._startPosition = fromValue; - this._lastPosition = this._startPosition; - - this._onUpdate = onUpdate; - this.__onEnd = onEnd; - this._lastTime = Date.now(); - - if (previousAnimation instanceof SpringAnimation) { - var internalState = previousAnimation.getInternalState(); - this._lastPosition = internalState.lastPosition; - this._lastVelocity = internalState.lastVelocity; - this._lastTime = internalState.lastTime; - } - if (this._initialVelocity !== undefined && - this._initialVelocity !== null) { - this._lastVelocity = this._initialVelocity; - } - this.onUpdate(); - } - - getInternalState(): Object { - return { - lastPosition: this._lastPosition, - lastVelocity: this._lastVelocity, - lastTime: this._lastTime, - }; - } - - onUpdate(): void { - var position = this._lastPosition; - var velocity = this._lastVelocity; - - var tempPosition = this._lastPosition; - var tempVelocity = this._lastVelocity; - - // If for some reason we lost a lot of frames (e.g. process large payload or - // stopped in the debugger), we only advance by 4 frames worth of - // computation and will continue on the next frame. It's better to have it - // running at faster speed than jumping to the end. - var MAX_STEPS = 64; - var now = Date.now(); - if (now > this._lastTime + MAX_STEPS) { - now = this._lastTime + MAX_STEPS; - } - - // We are using a fixed time step and a maximum number of iterations. - // The following post provides a lot of thoughts into how to build this - // loop: http://gafferongames.com/game-physics/fix-your-timestep/ - var TIMESTEP_MSEC = 1; - var numSteps = Math.floor((now - this._lastTime) / TIMESTEP_MSEC); - - for (var i = 0; i < numSteps; ++i) { - // Velocity is based on seconds instead of milliseconds - var step = TIMESTEP_MSEC / 1000; - - // This is using RK4. A good blog post to understand how it works: - // http://gafferongames.com/game-physics/integration-basics/ - var aVelocity = velocity; - var aAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity; - var tempPosition = position + aVelocity * step / 2; - var tempVelocity = velocity + aAcceleration * step / 2; - - var bVelocity = tempVelocity; - var bAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity; - tempPosition = position + bVelocity * step / 2; - tempVelocity = velocity + bAcceleration * step / 2; - - var cVelocity = tempVelocity; - var cAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity; - tempPosition = position + cVelocity * step / 2; - tempVelocity = velocity + cAcceleration * step / 2; - - var dVelocity = tempVelocity; - var dAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity; - tempPosition = position + cVelocity * step / 2; - tempVelocity = velocity + cAcceleration * step / 2; - - var dxdt = (aVelocity + 2 * (bVelocity + cVelocity) + dVelocity) / 6; - var dvdt = (aAcceleration + 2 * (bAcceleration + cAcceleration) + dAcceleration) / 6; - - position += dxdt * step; - velocity += dvdt * step; - } - - this._lastTime = now; - this._lastPosition = position; - this._lastVelocity = velocity; - - this._onUpdate(position); - if (!this.__active) { // a listener might have stopped us in _onUpdate - return; - } - - // Conditions for stopping the spring animation - var isOvershooting = false; - if (this._overshootClamping && this._tension !== 0) { - if (this._startPosition < this._toValue) { - isOvershooting = position > this._toValue; - } else { - isOvershooting = position < this._toValue; - } - } - var isVelocity = Math.abs(velocity) <= this._restSpeedThreshold; - var isDisplacement = true; - if (this._tension !== 0) { - isDisplacement = Math.abs(this._toValue - position) <= this._restDisplacementThreshold; - } - - if (isOvershooting || (isVelocity && isDisplacement)) { - if (this._tension !== 0) { - // Ensure that we end up with a round value - this._onUpdate(this._toValue); - } - - this.__debouncedOnEnd({finished: true}); - return; - } - this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this)); - } - - stop(): void { - this.__active = false; - window.cancelAnimationFrame(this._animationFrame); - this.__debouncedOnEnd({finished: false}); - } -} - -type ValueListenerCallback = (state: {value: number}) => void; - -var _uniqueId = 1; - -/** - * Standard value for driving animations. One `Animated.Value` can drive - * multiple properties in a synchronized fashion, but can only be driven by one - * mechanism at a time. Using a new mechanism (e.g. starting a new animation, - * or calling `setValue`) will stop any previous ones. - */ -class AnimatedValue extends AnimatedWithChildren { - _value: number; - _offset: number; - _animation: ?Animation; - _tracking: ?Animated; - _listeners: {[key: string]: ValueListenerCallback}; - - constructor(value: number) { - super(); - this._value = value; - this._offset = 0; - this._animation = null; - this._listeners = {}; - } - - __detach() { - this.stopAnimation(); - } - - __getValue(): number { - return this._value + this._offset; - } - - /** - * Directly set the value. This will stop any animations running on the value - * and update all the bound properties. - */ - setValue(value: number): void { - if (this._animation) { - this._animation.stop(); - this._animation = null; - } - this._updateValue(value); - } - - /** - * Sets an offset that is applied on top of whatever value is set, whether via - * `setValue`, an animation, or `Animated.event`. Useful for compensating - * things like the start of a pan gesture. - */ - setOffset(offset: number): void { - this._offset = offset; - } - - /** - * Merges the offset value into the base value and resets the offset to zero. - * The final output of the value is unchanged. - */ - flattenOffset(): void { - this._value += this._offset; - this._offset = 0; - } - - /** - * Adds an asynchronous listener to the value so you can observe updates from - * animations. This is useful because there is no way to - * synchronously read the value because it might be driven natively. - */ - addListener(callback: ValueListenerCallback): string { - var id = String(_uniqueId++); - this._listeners[id] = callback; - return id; - } - - removeListener(id: string): void { - delete this._listeners[id]; - } - - removeAllListeners(): void { - this._listeners = {}; - } - - /** - * Stops any running animation or tracking. `callback` is invoked with the - * final value after stopping the animation, which is useful for updating - * state to match the animation position with layout. - */ - stopAnimation(callback?: ?(value: number) => void): void { - this.stopTracking(); - this._animation && this._animation.stop(); - this._animation = null; - callback && callback(this.__getValue()); - } - - /** - * Interpolates the value before updating the property, e.g. mapping 0-1 to - * 0-10. - */ - interpolate(config: InterpolationConfigType): AnimatedInterpolation { - return new AnimatedInterpolation(this, Interpolation.create(config)); - } - - /** - * Typically only used internally, but could be used by a custom Animation - * class. - */ - animate(animation: Animation, callback: ?EndCallback): void { - var handle = null; - if (animation.__isInteraction) { - handle = InteractionManager.createInteractionHandle(); - } - var previousAnimation = this._animation; - this._animation && this._animation.stop(); - this._animation = animation; - animation.start( - this._value, - (value) => { - this._updateValue(value); - }, - (result) => { - this._animation = null; - if (handle !== null) { - InteractionManager.clearInteractionHandle(handle); - } - callback && callback(result); - }, - previousAnimation, - ); - } - - /** - * Typically only used internally. - */ - stopTracking(): void { - this._tracking && this._tracking.__detach(); - this._tracking = null; - } - - /** - * Typically only used internally. - */ - track(tracking: Animated): void { - this.stopTracking(); - this._tracking = tracking; - } - - _updateValue(value: number): void { - this._value = value; - _flush(this); - for (var key in this._listeners) { - this._listeners[key]({value: this.__getValue()}); - } - } -} - -type ValueXYListenerCallback = (value: {x: number; y: number}) => void; - -/** - * 2D Value for driving 2D animations, such as pan gestures. Almost identical - * API to normal `Animated.Value`, but multiplexed. Contains two regular - * `Animated.Value`s under the hood. Example: - * - *```javascript - * class DraggableView extends React.Component { - * constructor(props) { - * super(props); - * this.state = { - * pan: new Animated.ValueXY(), // inits to zero - * }; - * this.state.panResponder = PanResponder.create({ - * onStartShouldSetPanResponder: () => true, - * onPanResponderMove: Animated.event([null, { - * dx: this.state.pan.x, // x,y are Animated.Value - * dy: this.state.pan.y, - * }]), - * onPanResponderRelease: () => { - * Animated.spring( - * this.state.pan, // Auto-multiplexed - * {toValue: {x: 0, y: 0}} // Back to zero - * ).start(); - * }, - * }); - * } - * render() { - * return ( - * - * {this.props.children} - * - * ); - * } - * } - *``` - */ -class AnimatedValueXY extends AnimatedWithChildren { - x: AnimatedValue; - y: AnimatedValue; - _listeners: {[key: string]: {x: string; y: string}}; - - constructor(valueIn?: ?{x: number | AnimatedValue; y: number | AnimatedValue}) { - super(); - var value: any = valueIn || {x: 0, y: 0}; // @flowfixme: shouldn't need `: any` - if (typeof value.x === 'number' && typeof value.y === 'number') { - this.x = new AnimatedValue(value.x); - this.y = new AnimatedValue(value.y); - } else { - invariant( - value.x instanceof AnimatedValue && - value.y instanceof AnimatedValue, - 'AnimatedValueXY must be initalized with an object of numbers or ' + - 'AnimatedValues.' - ); - this.x = value.x; - this.y = value.y; - } - this._listeners = {}; - } - - setValue(value: {x: number; y: number}) { - this.x.setValue(value.x); - this.y.setValue(value.y); - } - - setOffset(offset: {x: number; y: number}) { - this.x.setOffset(offset.x); - this.y.setOffset(offset.y); - } - - flattenOffset(): void { - this.x.flattenOffset(); - this.y.flattenOffset(); - } - - __getValue(): {x: number; y: number} { - return { - x: this.x.__getValue(), - y: this.y.__getValue(), - }; - } - - stopAnimation(callback?: ?() => number): void { - this.x.stopAnimation(); - this.y.stopAnimation(); - callback && callback(this.__getValue()); - } - - addListener(callback: ValueXYListenerCallback): string { - var id = String(_uniqueId++); - var jointCallback = ({value: number}) => { - callback(this.__getValue()); - }; - this._listeners[id] = { - x: this.x.addListener(jointCallback), - y: this.y.addListener(jointCallback), - }; - return id; - } - - removeListener(id: string): void { - this.x.removeListener(this._listeners[id].x); - this.y.removeListener(this._listeners[id].y); - delete this._listeners[id]; - } - - /** - * Converts `{x, y}` into `{left, top}` for use in style, e.g. - * - *```javascript - * style={this.state.anim.getLayout()} - *``` - */ - getLayout(): {[key: string]: AnimatedValue} { - return { - left: this.x, - top: this.y, - }; - } - - /** - * Converts `{x, y}` into a useable translation transform, e.g. - * - *```javascript - * style={{ - * transform: this.state.anim.getTranslateTransform() - * }} - *``` - */ - getTranslateTransform(): Array<{[key: string]: AnimatedValue}> { - return [ - {translateX: this.x}, - {translateY: this.y} - ]; - } -} - -class AnimatedInterpolation extends AnimatedWithChildren { - _parent: Animated; - _interpolation: (input: number) => number | string; - - constructor(parent: Animated, interpolation: (input: number) => number | string) { - super(); - this._parent = parent; - this._interpolation = interpolation; - } - - __getValue(): number | string { - var parentValue: number = this._parent.__getValue(); - invariant( - typeof parentValue === 'number', - 'Cannot interpolate an input which is not a number.' - ); - return this._interpolation(parentValue); - } - - interpolate(config: InterpolationConfigType): AnimatedInterpolation { - return new AnimatedInterpolation(this, Interpolation.create(config)); - } - - __attach(): void { - this._parent.__addChild(this); - } - - __detach(): void { - this._parent.__removeChild(this); - } -} - -class AnimatedAddition extends AnimatedWithChildren { - _a: Animated; - _b: Animated; - - constructor(a: Animated, b: Animated) { - super(); - this._a = a; - this._b = b; - } - - __getValue(): number { - return this._a.__getValue() + this._b.__getValue(); - } - - interpolate(config: InterpolationConfigType): AnimatedInterpolation { - return new AnimatedInterpolation(this, Interpolation.create(config)); - } - - __attach(): void { - this._a.__addChild(this); - this._b.__addChild(this); - } - - __detach(): void { - this._a.__removeChild(this); - this._b.__removeChild(this); - } -} - -class AnimatedMultiplication extends AnimatedWithChildren { - _a: Animated; - _b: Animated; - - constructor(a: Animated, b: Animated) { - super(); - this._a = a; - this._b = b; - } - - __getValue(): number { - return this._a.__getValue() * this._b.__getValue(); - } - - interpolate(config: InterpolationConfigType): AnimatedInterpolation { - return new AnimatedInterpolation(this, Interpolation.create(config)); - } - - __attach(): void { - this._a.__addChild(this); - this._b.__addChild(this); - } - - __detach(): void { - this._a.__removeChild(this); - this._b.__removeChild(this); - } -} - -class AnimatedTransform extends AnimatedWithChildren { - _transforms: Array; - - constructor(transforms: Array) { - super(); - this._transforms = transforms; - } - - __getValue(): Array { - return this._transforms.map(transform => { - var result = {}; - for (var key in transform) { - var value = transform[key]; - if (value instanceof Animated) { - result[key] = value.__getValue(); - } else { - result[key] = value; - } - } - return result; - }); - } - - __getAnimatedValue(): Array { - return this._transforms.map(transform => { - var result = {}; - for (var key in transform) { - var value = transform[key]; - if (value instanceof Animated) { - result[key] = value.__getAnimatedValue(); - } else { - // All transform components needed to recompose matrix - result[key] = value; - } - } - return result; - }); - } - - __attach(): void { - this._transforms.forEach(transform => { - for (var key in transform) { - var value = transform[key]; - if (value instanceof Animated) { - value.__addChild(this); - } - } - }); - } - - __detach(): void { - this._transforms.forEach(transform => { - for (var key in transform) { - var value = transform[key]; - if (value instanceof Animated) { - value.__removeChild(this); - } - } - }); - } -} - -class AnimatedStyle extends AnimatedWithChildren { - _style: Object; - - constructor(style: any) { - super(); - style = flattenStyle(style) || {}; - if (style.transform) { - style = { - ...style, - transform: new AnimatedTransform(style.transform), - }; - } - this._style = style; - } - - __getValue(): Object { - var style = {}; - for (var key in this._style) { - var value = this._style[key]; - if (value instanceof Animated) { - style[key] = value.__getValue(); - } else { - style[key] = value; - } - } - return style; - } - - __getAnimatedValue(): Object { - var style = {}; - for (var key in this._style) { - var value = this._style[key]; - if (value instanceof Animated) { - style[key] = value.__getAnimatedValue(); - } - } - return style; - } - - __attach(): void { - for (var key in this._style) { - var value = this._style[key]; - if (value instanceof Animated) { - value.__addChild(this); - } - } - } - - __detach(): void { - for (var key in this._style) { - var value = this._style[key]; - if (value instanceof Animated) { - value.__removeChild(this); - } - } - } -} - -class AnimatedProps extends Animated { - _props: Object; - _callback: () => void; - - constructor( - props: Object, - callback: () => void, - ) { - super(); - if (props.style) { - props = { - ...props, - style: new AnimatedStyle(props.style), - }; - } - this._props = props; - this._callback = callback; - this.__attach(); - } - - __getValue(): Object { - var props = {}; - for (var key in this._props) { - var value = this._props[key]; - if (value instanceof Animated) { - props[key] = value.__getValue(); - } else { - props[key] = value; - } - } - return props; - } - - __getAnimatedValue(): Object { - var props = {}; - for (var key in this._props) { - var value = this._props[key]; - if (value instanceof Animated) { - props[key] = value.__getAnimatedValue(); - } - } - return props; - } - - __attach(): void { - for (var key in this._props) { - var value = this._props[key]; - if (value instanceof Animated) { - value.__addChild(this); - } - } - } - - __detach(): void { - for (var key in this._props) { - var value = this._props[key]; - if (value instanceof Animated) { - value.__removeChild(this); - } - } - } - - update(): void { - this._callback(); - } -} - -function createAnimatedComponent(Component: any): any { - var refName = 'node'; - - class AnimatedComponent extends React.Component { - _propsAnimated: AnimatedProps; - - componentWillUnmount() { - this._propsAnimated && this._propsAnimated.__detach(); - } - - setNativeProps(props) { - this.refs[refName].setNativeProps(props); - } - - componentWillMount() { - this.attachProps(this.props); - } - - attachProps(nextProps) { - var oldPropsAnimated = this._propsAnimated; - - // The system is best designed when setNativeProps is implemented. It is - // able to avoid re-rendering and directly set the attributes that - // changed. However, setNativeProps can only be implemented on leaf - // native components. If you want to animate a composite component, you - // need to re-render it. In this case, we have a fallback that uses - // forceUpdate. - var callback = () => { - if (this.refs[refName].setNativeProps) { - var value = this._propsAnimated.__getAnimatedValue(); - this.refs[refName].setNativeProps(value); - } else { - this.forceUpdate(); - } - }; - - this._propsAnimated = new AnimatedProps( - nextProps, - callback, - ); - - // When you call detach, it removes the element from the parent list - // of children. If it goes to 0, then the parent also detaches itself - // and so on. - // An optimization is to attach the new elements and THEN detach the old - // ones instead of detaching and THEN attaching. - // This way the intermediate state isn't to go to 0 and trigger - // this expensive recursive detaching to then re-attach everything on - // the very next operation. - oldPropsAnimated && oldPropsAnimated.__detach(); - } - - componentWillReceiveProps(nextProps) { - this.attachProps(nextProps); - } - - render() { - return ( - - ); - } - } - AnimatedComponent.propTypes = { - style: function(props, propName, componentName) { - if (!Component.propTypes) { - return; - } - - for (var key in ViewStylePropTypes) { - if (!Component.propTypes[key] && props[key] !== undefined) { - console.error( - 'You are setting the style `{ ' + key + ': ... }` as a prop. You ' + - 'should nest it in a style object. ' + - 'E.g. `{ style: { ' + key + ': ... } }`' - ); - } - } - } - }; - - return AnimatedComponent; -} - -class AnimatedTracking extends Animated { - _value: AnimatedValue; - _parent: Animated; - _callback: ?EndCallback; - _animationConfig: Object; - _animationClass: any; - - constructor( - value: AnimatedValue, - parent: Animated, - animationClass: any, - animationConfig: Object, - callback?: ?EndCallback, - ) { - super(); - this._value = value; - this._parent = parent; - this._animationClass = animationClass; - this._animationConfig = animationConfig; - this._callback = callback; - this.__attach(); - } - - __getValue(): Object { - return this._parent.__getValue(); - } - - __attach(): void { - this._parent.__addChild(this); - } - - __detach(): void { - this._parent.__removeChild(this); - } - - update(): void { - this._value.animate(new this._animationClass({ - ...this._animationConfig, - toValue: (this._animationConfig.toValue: any).__getValue(), - }), this._callback); - } -} - -type CompositeAnimation = { - start: (callback?: ?EndCallback) => void; - stop: () => void; -}; - -var add = function( - a: Animated, - b: Animated -): AnimatedAddition { - return new AnimatedAddition(a, b); -}; - -var multiply = function( - a: Animated, - b: Animated -): AnimatedMultiplication { - return new AnimatedMultiplication(a, b); -}; - -var maybeVectorAnim = function( - value: AnimatedValue | AnimatedValueXY, - config: Object, - anim: (value: AnimatedValue, config: Object) => CompositeAnimation -): ?CompositeAnimation { - if (value instanceof AnimatedValueXY) { - var configX = {...config}; - var configY = {...config}; - for (var key in config) { - var {x, y} = config[key]; - if (x !== undefined && y !== undefined) { - configX[key] = x; - configY[key] = y; - } - } - var aX = anim((value: AnimatedValueXY).x, configX); - var aY = anim((value: AnimatedValueXY).y, configY); - // We use `stopTogether: false` here because otherwise tracking will break - // because the second animation will get stopped before it can update. - return parallel([aX, aY], {stopTogether: false}); - } - return null; -}; - -var spring = function( - value: AnimatedValue | AnimatedValueXY, - config: SpringAnimationConfig, -): CompositeAnimation { - return maybeVectorAnim(value, config, spring) || { - start: function(callback?: ?EndCallback): void { - var singleValue: any = value; - var singleConfig: any = config; - singleValue.stopTracking(); - if (config.toValue instanceof Animated) { - singleValue.track(new AnimatedTracking( - singleValue, - config.toValue, - SpringAnimation, - singleConfig, - callback - )); - } else { - singleValue.animate(new SpringAnimation(singleConfig), callback); - } - }, - - stop: function(): void { - value.stopAnimation(); - }, - }; -}; - -var timing = function( - value: AnimatedValue | AnimatedValueXY, - config: TimingAnimationConfig, -): CompositeAnimation { - return maybeVectorAnim(value, config, timing) || { - start: function(callback?: ?EndCallback): void { - var singleValue: any = value; - var singleConfig: any = config; - singleValue.stopTracking(); - if (config.toValue instanceof Animated) { - singleValue.track(new AnimatedTracking( - singleValue, - config.toValue, - TimingAnimation, - singleConfig, - callback - )); - } else { - singleValue.animate(new TimingAnimation(singleConfig), callback); - } - }, - - stop: function(): void { - value.stopAnimation(); - }, - }; -}; - -var decay = function( - value: AnimatedValue | AnimatedValueXY, - config: DecayAnimationConfig, -): CompositeAnimation { - return maybeVectorAnim(value, config, decay) || { - start: function(callback?: ?EndCallback): void { - var singleValue: any = value; - var singleConfig: any = config; - singleValue.stopTracking(); - singleValue.animate(new DecayAnimation(singleConfig), callback); - }, - - stop: function(): void { - value.stopAnimation(); - }, - }; -}; - -var sequence = function( - animations: Array, -): CompositeAnimation { - var current = 0; - return { - start: function(callback?: ?EndCallback) { - var onComplete = function(result) { - if (!result.finished) { - callback && callback(result); - return; - } - - current++; - - if (current === animations.length) { - callback && callback(result); - return; - } - - animations[current].start(onComplete); - }; - - if (animations.length === 0) { - callback && callback({finished: true}); - } else { - animations[current].start(onComplete); - } - }, - - stop: function() { - if (current < animations.length) { - animations[current].stop(); - } - } - }; -}; - -type ParallelConfig = { - stopTogether?: bool; // If one is stopped, stop all. default: true -} -var parallel = function( - animations: Array, - config?: ?ParallelConfig, -): CompositeAnimation { - var doneCount = 0; - // Make sure we only call stop() at most once for each animation - var hasEnded = {}; - var stopTogether = !(config && config.stopTogether === false); - - var result = { - start: function(callback?: ?EndCallback) { - if (doneCount === animations.length) { - callback && callback({finished: true}); - return; - } - - animations.forEach((animation, idx) => { - var cb = function(endResult) { - hasEnded[idx] = true; - doneCount++; - if (doneCount === animations.length) { - doneCount = 0; - callback && callback(endResult); - return; - } - - if (!endResult.finished && stopTogether) { - result.stop(); - } - }; - - if (!animation) { - cb({finished: true}); - } else { - animation.start(cb); - } - }); - }, - - stop: function(): void { - animations.forEach((animation, idx) => { - !hasEnded[idx] && animation.stop(); - hasEnded[idx] = true; - }); - } - }; - - return result; -}; - -var delay = function(time: number): CompositeAnimation { - // Would be nice to make a specialized implementation - return timing(new AnimatedValue(0), {toValue: 0, delay: time, duration: 0}); -}; - -var stagger = function( - time: number, - animations: Array, -): CompositeAnimation { - return parallel(animations.map((animation, i) => { - return sequence([ - delay(time * i), - animation, - ]); - })); -}; - -type Mapping = {[key: string]: Mapping} | AnimatedValue; - -type EventConfig = {listener?: ?Function}; -var event = function( - argMapping: Array, - config?: ?EventConfig, -): () => void { - return function(...args): void { - var traverse = function(recMapping, recEvt, key) { - if (typeof recEvt === 'number') { - invariant( - recMapping instanceof AnimatedValue, - 'Bad mapping of type ' + typeof recMapping + ' for key ' + key + - ', event value must map to AnimatedValue' - ); - recMapping.setValue(recEvt); - return; - } - invariant( - typeof recMapping === 'object', - 'Bad mapping of type ' + typeof recMapping + ' for key ' + key - ); - invariant( - typeof recEvt === 'object', - 'Bad event of type ' + typeof recEvt + ' for key ' + key - ); - for (var key in recMapping) { - traverse(recMapping[key], recEvt[key], key); - } - }; - argMapping.forEach((mapping, idx) => { - traverse(mapping, args[idx], 'arg' + idx); - }); - if (config && config.listener) { - config.listener.apply(null, args); - } - }; -}; - -/** - * Animations are an important part of modern UX, and the `Animated` - * library is designed to make them fluid, powerful, and easy to build and - * maintain. - * - * The simplest workflow is to create an `Animated.Value`, hook it up to one or - * more style attributes of an animated component, and then drive updates either - * via animations, such as `Animated.timing`, or by hooking into gestures like - * panning or scrolling via `Animated.event`. `Animated.Value` can also bind to - * props other than style, and can be interpolated as well. Here is a basic - * example of a container view that will fade in when it's mounted: - * - *```javascript - * class FadeInView extends React.Component { - * constructor(props) { - * super(props); - * this.state = { - * fadeAnim: new Animated.Value(0), // init opacity 0 - * }; - * } - * componentDidMount() { - * Animated.timing( // Uses easing functions - * this.state.fadeAnim, // The value to drive - * {toValue: 1}, // Configuration - * ).start(); // Don't forget start! - * } - * render() { - * return ( - * // Binds - * {this.props.children} - * - * ); - * } - * } - *``` - * - * Note that only animatable components can be animated. `View`, `Text`, and - * `Image` are already provided, and you can create custom ones with - * `createAnimatedComponent`. These special components do the magic of binding - * the animated values to the properties, and do targeted native updates to - * avoid the cost of the react render and reconciliation process on every frame. - * They also handle cleanup on unmount so they are safe by default. - * - * Animations are heavily configurable. Custom and pre-defined easing - * functions, delays, durations, decay factors, spring constants, and more can - * all be tweaked depending on the type of animation. - * - * A single `Animated.Value` can drive any number of properties, and each - * property can be run through an interpolation first. An interpolation maps - * input ranges to output ranges, typically using a linear interpolation but - * also supports easing functions. By default, it will extrapolate the curve - * beyond the ranges given, but you can also have it clamp the output value. - * - * For example, you may want to think about your `Animated.Value` as going from - * 0 to 1, but animate the position from 150px to 0px and the opacity from 0 to - * 1. This can easily be done by modifying `style` in the example above like so: - * - *```javascript - * style={{ - * opacity: this.state.fadeAnim, // Binds directly - * transform: [{ - * translateY: this.state.fadeAnim.interpolate({ - * inputRange: [0, 1], - * outputRange: [150, 0] // 0 : 150, 0.5 : 75, 1 : 0 - * }), - * }], - * }}> - *``` - * - * Animations can also be combined in complex ways using composition functions - * such as `sequence` and `parallel`, and can also be chained together simply - * by setting the `toValue` of one animation to be another `Animated.Value`. - * - * `Animated.ValueXY` is handy for 2D animations, like panning, and there are - * other helpful additions like `setOffset` and `getLayout` to aid with typical - * interaction patterns, like drag-and-drop. - * - * You can see more example usage in `AnimationExample.js`, the Gratuitous - * Animation App, and [Animations documentation guide](docs/animations.html). - * - * Note that `Animated` is designed to be fully serializable so that animations - * can be run in a high performance way, independent of the normal JavaScript - * event loop. This does influence the API, so keep that in mind when it seems a - * little trickier to do something compared to a fully synchronous system. - * Checkout `Animated.Value.addListener` as a way to work around some of these - * limitations, but use it sparingly since it might have performance - * implications in the future. - */ -module.exports = { - /** - * Standard value class for driving animations. Typically initialized with - * `new Animated.Value(0);` - */ - Value: AnimatedValue, - /** - * 2D value class for driving 2D animations, such as pan gestures. - */ - ValueXY: AnimatedValueXY, - - /** - * Animates a value from an initial velocity to zero based on a decay - * coefficient. - */ - decay, - /** - * Animates a value along a timed easing curve. The `Easing` module has tons - * of pre-defined curves, or you can use your own function. - */ - timing, - /** - * Spring animation based on Rebound and Origami. Tracks velocity state to - * create fluid motions as the `toValue` updates, and can be chained together. - */ - spring, - - /** - * Creates a new Animated value composed from two Animated values added - * together. - */ - add, - /** - * Creates a new Animated value composed from two Animated values multiplied - * together. - */ - multiply, - - /** - * Starts an animation after the given delay. - */ - delay, - /** - * Starts an array of animations in order, waiting for each to complete - * before starting the next. If the current running animation is stopped, no - * following animations will be started. - */ - sequence, - /** - * Starts an array of animations all at the same time. By default, if one - * of the animations is stopped, they will all be stopped. You can override - * this with the `stopTogether` flag. - */ - parallel, - /** - * Array of animations may run in parallel (overlap), but are started in - * sequence with successive delays. Nice for doing trailing effects. - */ - stagger, - - /** - * Takes an array of mappings and extracts values from each arg accordingly, - * then calls `setValue` on the mapped outputs. e.g. - * - *```javascript - * onScroll={Animated.event( - * [{nativeEvent: {contentOffset: {x: this._scrollX}}}] - * {listener}, // Optional async listener - * ) - * ... - * onPanResponderMove: Animated.event([ - * null, // raw event arg ignored - * {dx: this._panX}, // gestureState arg - * ]), - *``` - */ - event, - - /** - * Make any React component Animatable. Used to create `Animated.View`, etc. - */ - createAnimatedComponent, - - __PropsOnlyForTests: AnimatedProps, -}; diff --git a/src/apis/Animated/Interpolation.js b/src/apis/Animated/Interpolation.js deleted file mode 100644 index 74c18aee..00000000 --- a/src/apis/Animated/Interpolation.js +++ /dev/null @@ -1,288 +0,0 @@ -/* eslint-disable */ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule Interpolation - * @flow - */ -/* eslint no-bitwise: 0 */ -'use strict'; - -/* @edit start */ -var normalizeColor = require('../StyleSheet/normalizeColor'); -var invariant = require('fbjs/lib/invariant'); -/* @edit end */ - -type ExtrapolateType = 'extend' | 'identity' | 'clamp'; - -export type InterpolationConfigType = { - inputRange: Array; - outputRange: (Array | Array); - easing?: ((input: number) => number); - extrapolate?: ExtrapolateType; - extrapolateLeft?: ExtrapolateType; - extrapolateRight?: ExtrapolateType; -}; - -var linear = (t) => t; - -/** - * Very handy helper to map input ranges to output ranges with an easing - * function and custom behavior outside of the ranges. - */ -class Interpolation { - static create(config: InterpolationConfigType): (input: number) => number | string { - - if (config.outputRange && typeof config.outputRange[0] === 'string') { - return createInterpolationFromStringOutputRange(config); - } - - var outputRange: Array = (config.outputRange: any); - checkInfiniteRange('outputRange', outputRange); - - var inputRange = config.inputRange; - checkInfiniteRange('inputRange', inputRange); - checkValidInputRange(inputRange); - - invariant( - inputRange.length === outputRange.length, - 'inputRange (' + inputRange.length + ') and outputRange (' + - outputRange.length + ') must have the same length' - ); - - var easing = config.easing || linear; - - var extrapolateLeft: ExtrapolateType = 'extend'; - if (config.extrapolateLeft !== undefined) { - extrapolateLeft = config.extrapolateLeft; - } else if (config.extrapolate !== undefined) { - extrapolateLeft = config.extrapolate; - } - - var extrapolateRight: ExtrapolateType = 'extend'; - if (config.extrapolateRight !== undefined) { - extrapolateRight = config.extrapolateRight; - } else if (config.extrapolate !== undefined) { - extrapolateRight = config.extrapolate; - } - - return (input) => { - invariant( - typeof input === 'number', - 'Cannot interpolation an input which is not a number' - ); - - var range = findRange(input, inputRange); - return interpolate( - input, - inputRange[range], - inputRange[range + 1], - outputRange[range], - outputRange[range + 1], - easing, - extrapolateLeft, - extrapolateRight, - ); - }; - } -} - -function interpolate( - input: number, - inputMin: number, - inputMax: number, - outputMin: number, - outputMax: number, - easing: ((input: number) => number), - extrapolateLeft: ExtrapolateType, - extrapolateRight: ExtrapolateType, -) { - var result = input; - - // Extrapolate - if (result < inputMin) { - if (extrapolateLeft === 'identity') { - return result; - } else if (extrapolateLeft === 'clamp') { - result = inputMin; - } else if (extrapolateLeft === 'extend') { - // noop - } - } - - if (result > inputMax) { - if (extrapolateRight === 'identity') { - return result; - } else if (extrapolateRight === 'clamp') { - result = inputMax; - } else if (extrapolateRight === 'extend') { - // noop - } - } - - if (outputMin === outputMax) { - return outputMin; - } - - if (inputMin === inputMax) { - if (input <= inputMin) { - return outputMin; - } - return outputMax; - } - - // Input Range - if (inputMin === -Infinity) { - result = -result; - } else if (inputMax === Infinity) { - result = result - inputMin; - } else { - result = (result - inputMin) / (inputMax - inputMin); - } - - // Easing - result = easing(result); - - // Output Range - if (outputMin === -Infinity) { - result = -result; - } else if (outputMax === Infinity) { - result = result + outputMin; - } else { - result = result * (outputMax - outputMin) + outputMin; - } - - return result; -} - -function colorToRgba(input: string): string { - var int32Color = normalizeColor(input); - if (int32Color === null) { - return input; - } - - int32Color = int32Color || 0; // $FlowIssue - - var r = (int32Color & 0xff000000) >>> 24; - var g = (int32Color & 0x00ff0000) >>> 16; - var b = (int32Color & 0x0000ff00) >>> 8; - var a = (int32Color & 0x000000ff) / 255; - - return `rgba(${r}, ${g}, ${b}, ${a})`; -} - -var stringShapeRegex = /[0-9\.-]+/g; - -/** - * Supports string shapes by extracting numbers so new values can be computed, - * and recombines those values into new strings of the same shape. Supports - * things like: - * - * rgba(123, 42, 99, 0.36) // colors - * -45deg // values with units - */ -function createInterpolationFromStringOutputRange( - config: InterpolationConfigType, -): (input: number) => string { - var outputRange: Array = (config.outputRange: any); - invariant(outputRange.length >= 2, 'Bad output range'); - outputRange = outputRange.map(colorToRgba); - checkPattern(outputRange); - - // ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.5)'] - // -> - // [ - // [0, 50], - // [100, 150], - // [200, 250], - // [0, 0.5], - // ] - /* $FlowFixMe(>=0.18.0): `outputRange[0].match()` can return `null`. Need to - * guard against this possibility. - */ - var outputRanges = outputRange[0].match(stringShapeRegex).map(() => []); - outputRange.forEach(value => { - /* $FlowFixMe(>=0.18.0): `value.match()` can return `null`. Need to guard - * against this possibility. - */ - value.match(stringShapeRegex).forEach((number, i) => { - outputRanges[i].push(+number); - }); - }); - - /* $FlowFixMe(>=0.18.0): `outputRange[0].match()` can return `null`. Need to - * guard against this possibility. - */ - var interpolations = outputRange[0].match(stringShapeRegex).map((value, i) => { - return Interpolation.create({ - ...config, - outputRange: outputRanges[i], - }); - }); - - return (input) => { - var i = 0; - // 'rgba(0, 100, 200, 0)' - // -> - // 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...' - return outputRange[0].replace(stringShapeRegex, () => { - return String(interpolations[i++](input)); - }); - }; -} - -function checkPattern(arr: Array) { - var pattern = arr[0].replace(stringShapeRegex, ''); - for (var i = 1; i < arr.length; ++i) { - invariant( - pattern === arr[i].replace(stringShapeRegex, ''), - 'invalid pattern ' + arr[0] + ' and ' + arr[i], - ); - } -} - -function findRange(input: number, inputRange: Array) { - for (var i = 1; i < inputRange.length - 1; ++i) { - if (inputRange[i] >= input) { - break; - } - } - return i - 1; -} - -function checkValidInputRange(arr: Array) { - invariant(arr.length >= 2, 'inputRange must have at least 2 elements'); - for (var i = 1; i < arr.length; ++i) { - invariant( - arr[i] >= arr[i - 1], - /* $FlowFixMe(>=0.13.0) - In the addition expression below this comment, - * one or both of the operands may be something that doesn't cleanly - * convert to a string, like undefined, null, and object, etc. If you really - * mean this implicit string conversion, you can do something like - * String(myThing) - */ - 'inputRange must be monotonically increasing ' + arr - ); - } -} - -function checkInfiniteRange(name: string, arr: Array) { - invariant(arr.length >= 2, name + ' must have at least 2 elements'); - invariant( - arr.length !== 2 || arr[0] !== -Infinity || arr[1] !== Infinity, - /* $FlowFixMe(>=0.13.0) - In the addition expression below this comment, - * one or both of the operands may be something that doesn't cleanly convert - * to a string, like undefined, null, and object, etc. If you really mean - * this implicit string conversion, you can do something like - * String(myThing) - */ - name + 'cannot be ]-infinity;+infinity[ ' + arr - ); -} - -module.exports = Interpolation; diff --git a/src/apis/Animated/SpringConfig.js b/src/apis/Animated/SpringConfig.js deleted file mode 100644 index bedcbd8c..00000000 --- a/src/apis/Animated/SpringConfig.js +++ /dev/null @@ -1,103 +0,0 @@ -/* eslint-disable */ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule SpringConfig - * @flow - */ - -'use strict'; - -type SpringConfigType = { - tension: number, - friction: number, -}; - -function tensionFromOrigamiValue(oValue) { - return (oValue - 30) * 3.62 + 194; -} - -function frictionFromOrigamiValue(oValue) { - return (oValue - 8) * 3 + 25; -} - -function fromOrigamiTensionAndFriction( - tension: number, - friction: number, -): SpringConfigType { - return { - tension: tensionFromOrigamiValue(tension), - friction: frictionFromOrigamiValue(friction) - }; -} - -function fromBouncinessAndSpeed( - bounciness: number, - speed: number, -): SpringConfigType { - function normalize(value, startValue, endValue) { - return (value - startValue) / (endValue - startValue); - } - - function projectNormal(n, start, end) { - return start + (n * (end - start)); - } - - function linearInterpolation(t, start, end) { - return t * end + (1 - t) * start; - } - - function quadraticOutInterpolation(t, start, end) { - return linearInterpolation(2 * t - t * t, start, end); - } - - function b3Friction1(x) { - return (0.0007 * Math.pow(x, 3)) - - (0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28; - } - - function b3Friction2(x) { - return (0.000044 * Math.pow(x, 3)) - - (0.006 * Math.pow(x, 2)) + 0.36 * x + 2; - } - - function b3Friction3(x) { - return (0.00000045 * Math.pow(x, 3)) - - (0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84; - } - - function b3Nobounce(tension) { - if (tension <= 18) { - return b3Friction1(tension); - } else if (tension > 18 && tension <= 44) { - return b3Friction2(tension); - } else { - return b3Friction3(tension); - } - } - - var b = normalize(bounciness / 1.7, 0, 20); - b = projectNormal(b, 0, 0.8); - var s = normalize(speed / 1.7, 0, 20); - var bouncyTension = projectNormal(s, 0.5, 200); - var bouncyFriction = quadraticOutInterpolation( - b, - b3Nobounce(bouncyTension), - 0.01 - ); - - return { - tension: tensionFromOrigamiValue(bouncyTension), - friction: frictionFromOrigamiValue(bouncyFriction) - }; -} - -module.exports = { - fromOrigamiTensionAndFriction, - fromBouncinessAndSpeed, -}; diff --git a/src/apis/Animated/index.js b/src/apis/Animated/index.js deleted file mode 100644 index d8405007..00000000 --- a/src/apis/Animated/index.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2016-present, Nicolas Gallagher. - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * @flow - */ - -import AnimatedImplementation from './AnimatedImplementation' -import Image from '../../components/Image' -import Text from '../../components/Text' -import View from '../../components/View' - -module.exports = { - ...AnimatedImplementation, - View: AnimatedImplementation.createAnimatedComponent(View), - Text: AnimatedImplementation.createAnimatedComponent(Text), - Image: AnimatedImplementation.createAnimatedComponent(Image) -} diff --git a/src/apis/Animated/polyfills/Set.js b/src/apis/Animated/polyfills/Set.js deleted file mode 100644 index 56d52f93..00000000 --- a/src/apis/Animated/polyfills/Set.js +++ /dev/null @@ -1,15 +0,0 @@ -function SetPolyfill() { - this._cache = [] -} - -SetPolyfill.prototype.add = function (e) { - if (this._cache.indexOf(e) === -1) { - this._cache.push(e) - } -} - -SetPolyfill.prototype.forEach = function (cb) { - this._cache.forEach(cb) -} - -export default SetPolyfill diff --git a/src/apis/Easing/bezier.js b/src/apis/Easing/bezier.js deleted file mode 100644 index bbaa0dd3..00000000 --- a/src/apis/Easing/bezier.js +++ /dev/null @@ -1,83 +0,0 @@ -/* eslint-disable */ -/** - * https://github.com/arian/cubic-bezier - * - * MIT License - * - * Copyright (c) 2013 Arian Stolwijk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule bezier - * @nolint - */ - -module.exports = function(x1, y1, x2, y2, epsilon){ - - var curveX = function(t){ - var v = 1 - t; - return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t; - }; - - var curveY = function(t){ - var v = 1 - t; - return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t; - }; - - var derivativeCurveX = function(t){ - var v = 1 - t; - return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (-t * t * t + 2 * v * t) * x2; - }; - - return function(t){ - - var x = t, t0, t1, t2, x2, d2, i; - - // First try a few iterations of Newton's method -- normally very fast. - for (t2 = x, i = 0; i < 8; i++){ - x2 = curveX(t2) - x; - if (Math.abs(x2) < epsilon) { return curveY(t2); } - d2 = derivativeCurveX(t2); - if (Math.abs(d2) < 1e-6) { break; } - t2 = t2 - x2 / d2; - } - - t0 = 0; - t1 = 1; - t2 = x; - - if (t2 < t0) { return curveY(t0); } - if (t2 > t1) { return curveY(t1); } - - // Fallback to the bisection method for reliability. - while (t0 < t1){ - x2 = curveX(t2); - if (Math.abs(x2 - x) < epsilon) { return curveY(t2); } - if (x > x2) { t0 = t2; } - else { t1 = t2; } - t2 = (t1 - t0) * 0.5 + t0; - } - - // Failure - return curveY(t2); - - }; - -}; diff --git a/src/apis/Easing/index.js b/src/apis/Easing/index.js deleted file mode 100644 index 89f45d48..00000000 --- a/src/apis/Easing/index.js +++ /dev/null @@ -1,155 +0,0 @@ -/* eslint-disable */ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule Easing - * @flow - */ -'use strict'; - -var _bezier = require('./bezier'); - -/** - * This class implements common easing functions. The math is pretty obscure, - * but this cool website has nice visual illustrations of what they represent: - * http://xaedes.de/dev/transitions/ - */ -class Easing { - static step0(n) { - return n > 0 ? 1 : 0; - } - - static step1(n) { - return n >= 1 ? 1 : 0; - } - - static linear(t) { - return t; - } - - static ease(t: number): number { - return ease(t); - } - - static quad(t) { - return t * t; - } - - static cubic(t) { - return t * t * t; - } - - static poly(n) { - return (t) => Math.pow(t, n); - } - - static sin(t) { - return 1 - Math.cos(t * Math.PI / 2); - } - - static circle(t) { - return 1 - Math.sqrt(1 - t * t); - } - - static exp(t) { - return Math.pow(2, 10 * (t - 1)); - } - - /** - * A simple elastic interaction, similar to a spring. Default bounciness - * is 1, which overshoots a little bit once. 0 bounciness doesn't overshoot - * at all, and bounciness of N > 1 will overshoot about N times. - * - * Wolfram Plots: - * - * http://tiny.cc/elastic_b_1 (default bounciness = 1) - * http://tiny.cc/elastic_b_3 (bounciness = 3) - */ - static elastic(bounciness: number = 1): (t: number) => number { - var p = bounciness * Math.PI; - return (t) => 1 - Math.pow(Math.cos(t * Math.PI / 2), 3) * Math.cos(t * p); - } - - static back(s: number): (t: number) => number { - if (s === undefined) { - s = 1.70158; - } - return (t) => t * t * ((s + 1) * t - s); - } - - static bounce(t: number): number { - if (t < 1 / 2.75) { - return 7.5625 * t * t; - } - - if (t < 2 / 2.75) { - t -= 1.5 / 2.75; - return 7.5625 * t * t + 0.75; - } - - if (t < 2.5 / 2.75) { - t -= 2.25 / 2.75; - return 7.5625 * t * t + 0.9375; - } - - t -= 2.625 / 2.75; - return 7.5625 * t * t + 0.984375; - } - - static bezier( - x1: number, - y1: number, - x2: number, - y2: number, - epsilon?: ?number, - ): (t: number) => number { - if (epsilon === undefined) { - // epsilon determines the precision of the solved values - // a good approximation is: - var duration = 500; // duration of animation in milliseconds. - epsilon = (1000 / 60 / duration) / 4; - } - - return _bezier(x1, y1, x2, y2, epsilon); - } - - static in( - easing: (t: number) => number, - ): (t: number) => number { - return easing; - } - - /** - * Runs an easing function backwards. - */ - static out( - easing: (t: number) => number, - ): (t: number) => number { - return (t) => 1 - easing(1 - t); - } - - /** - * Makes any easing function symmetrical. - */ - static inOut( - easing: (t: number) => number, - ): (t: number) => number { - return (t) => { - if (t < 0.5) { - return easing(t * 2) / 2; - } - return 1 - easing((1 - t) * 2) / 2; - }; - } -} - -var ease = Easing.bezier(0.42, 0, 1, 1); - - - -module.exports = Easing; diff --git a/src/components/Touchable/TouchableBounce.js b/src/components/Touchable/TouchableBounce.js index 7aec0b39..1c39ae99 100644 --- a/src/components/Touchable/TouchableBounce.js +++ b/src/components/Touchable/TouchableBounce.js @@ -12,7 +12,7 @@ */ 'use strict'; -var Animated = require('../../apis/Animated'); +var Animated = require('animated'); var EdgeInsetsPropType = require('../../apis/StyleSheet/EdgeInsetsPropType'); var NativeMethodsMixin = require('../../modules/NativeMethodsMixin'); var React = require('react'); diff --git a/src/components/Touchable/TouchableOpacity.js b/src/components/Touchable/TouchableOpacity.js index 2f6c6d91..e33af48a 100644 --- a/src/components/Touchable/TouchableOpacity.js +++ b/src/components/Touchable/TouchableOpacity.js @@ -14,7 +14,7 @@ // Note (avik): add @flow when Flow supports spread properties in propTypes -var Animated = require('../../apis/Animated'); +var Animated = require('animated'); var NativeMethodsMixin = require('../../modules/NativeMethodsMixin'); var React = require('react'); var StyleSheet = require('../../apis/StyleSheet'); diff --git a/src/index.js b/src/index.js index 36313f85..03165cc1 100644 --- a/src/index.js +++ b/src/index.js @@ -4,12 +4,12 @@ import findNodeHandle from './modules/findNodeHandle' import ReactDOM from 'react-dom' // apis -import Animated from './apis/Animated' +import Animated from 'animated' import AppRegistry from './apis/AppRegistry' import AppState from './apis/AppState' import AsyncStorage from './apis/AsyncStorage' import Dimensions from './apis/Dimensions' -import Easing from './apis/Easing' +import Easing from 'animated/lib/Easing' import InteractionManager from './apis/InteractionManager' import NetInfo from './apis/NetInfo' import PanResponder from './apis/PanResponder' @@ -42,13 +42,20 @@ import ColorPropType from './apis/StyleSheet/ColorPropType' import EdgeInsetsPropType from './apis/StyleSheet/EdgeInsetsPropType' import PointPropType from './apis/StyleSheet/PointPropType' +Animated.inject.FlattenStyle(StyleSheet.flatten) + const ReactNative = { findNodeHandle, render: ReactDOM.render, unmountComponentAtNode: ReactDOM.unmountComponentAtNode, // apis - Animated, + Animated: { + ...Animated, + Image: Animated.createAnimatedComponent(Image), + Text: Animated.createAnimatedComponent(Text), + View: Animated.createAnimatedComponent(View) + }, AppRegistry, AppState, AsyncStorage, diff --git a/src/modules/polyfills/Set.js b/src/modules/polyfills/Set.js new file mode 100644 index 00000000..718f5c10 --- /dev/null +++ b/src/modules/polyfills/Set.js @@ -0,0 +1,8 @@ +function SetPolyfill() { this._cache = [] } +SetPolyfill.prototype.add = function (e) { + if (this._cache.indexOf(e) === -1) { this._cache.push(e) } +} +SetPolyfill.prototype.forEach = function (cb) { + this._cache.forEach(cb) +} +module.exports = SetPolyfill diff --git a/webpack.config.js b/webpack.config.js index cfa48253..af708713 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,5 @@ -var webpack = require('webpack') +const path = require('path') +const webpack = require('webpack') const DIST_DIRECTORY = './dist' @@ -8,13 +9,18 @@ module.exports = { }, output: { filename: 'ReactNative.js', - library: 'React', + library: 'ReactNative', libraryTarget: 'umd', path: DIST_DIRECTORY }, plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }), new webpack.optimize.DedupePlugin(), + // https://github.com/animatedjs/animated/issues/40 + new webpack.NormalModuleReplacementPlugin( + /es6-set/, + path.join(__dirname, 'src/modules/polyfills/Set.js') + ), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin({ compress: {