@react-spring/animated
Advanced tools
Comparing version 9.0.0-beta.24 to 9.0.0-beta.25
517
index.cjs.js
@@ -11,9 +11,13 @@ 'use strict'; | ||
var G = require('@react-spring/shared/globals'); | ||
var _objectWithoutPropertiesLoose = _interopDefault(require('@babel/runtime/helpers/objectWithoutPropertiesLoose')); | ||
var _assertThisInitialized = _interopDefault(require('@babel/runtime/helpers/assertThisInitialized')); | ||
var invariant = _interopDefault(require('tiny-invariant')); | ||
var React = require('react'); | ||
var React__default = _interopDefault(React); | ||
function isAnimated(val) { | ||
return val instanceof Animated; | ||
} | ||
var _a; | ||
var animatedTag = Symbol.for('isAnimated'); | ||
var isAnimated = function isAnimated(val) { | ||
return !!(val && val[animatedTag]); | ||
}; | ||
var Animated = | ||
@@ -23,19 +27,16 @@ /*#__PURE__*/ | ||
function Animated() { | ||
this.children = []; | ||
this[_a] = true; | ||
this.children = new Set(); | ||
} | ||
/** Returns the set of `AnimatedValue` nodes contained by this node. */ | ||
var _proto = Animated.prototype; | ||
_proto.getAnimatedValue = function getAnimatedValue() { | ||
return this.getValue(); | ||
}; | ||
_proto.getPayload = function getPayload() { | ||
return this.payload || this; | ||
}; | ||
return this.payload; | ||
} | ||
/** Returns the set of animated nodes that depend on this node. */ | ||
; | ||
_proto.attach = function attach() {}; | ||
_proto.detach = function detach() {}; | ||
_proto.getChildren = function getChildren() { | ||
@@ -46,10 +47,9 @@ return this.children; | ||
_proto.addChild = function addChild(child) { | ||
if (this.children.length === 0) this.attach(); | ||
this.children.push(child); | ||
this.children.size || this._attach(); | ||
this.children.add(child); | ||
}; | ||
_proto.removeChild = function removeChild(child) { | ||
var index = this.children.indexOf(child); | ||
this.children.splice(index, 1); | ||
if (this.children.length === 0) this.detach(); | ||
this.children.delete(child); | ||
this.children.size || this._detach(); | ||
}; | ||
@@ -59,136 +59,165 @@ | ||
}(); | ||
var AnimatedArray = | ||
_a = animatedTag; | ||
var AnimatedObject = | ||
/*#__PURE__*/ | ||
function (_Animated) { | ||
_inheritsLoose(AnimatedArray, _Animated); | ||
_inheritsLoose(AnimatedObject, _Animated); | ||
function AnimatedArray() { | ||
return _Animated.apply(this, arguments) || this; | ||
function AnimatedObject(source) { | ||
var _this; | ||
_this = _Animated.call(this) || this; | ||
_this.source = source; | ||
_this.payload = toPayload(source); | ||
return _this; | ||
} | ||
var _proto2 = AnimatedArray.prototype; | ||
var _proto = AnimatedObject.prototype; | ||
_proto2.attach = function attach() { | ||
var _this = this; | ||
this.payload.forEach(function (p) { | ||
return isAnimated(p) && p.addChild(_this); | ||
_proto.getValue = function getValue(animated) { | ||
var obj = {}; | ||
shared.each(this.source, function (val, key) { | ||
if (isAnimated(val)) { | ||
obj[key] = val.getValue(animated); | ||
} else if (!animated) { | ||
obj[key] = val; | ||
} | ||
}); | ||
return obj; | ||
}; | ||
_proto2.detach = function detach() { | ||
var _this2 = this; | ||
_proto.updatePayload = function updatePayload(prev, next) { | ||
var source = _extends({}, this.source); | ||
this.payload.forEach(function (p) { | ||
return isAnimated(p) && p.removeChild(_this2); | ||
shared.each(source, function (val, key) { | ||
if (val === prev) source[key] = next; | ||
}); | ||
this.source = source; | ||
this.payload = toPayload(source); | ||
}; | ||
return AnimatedArray; | ||
_proto._attach = function _attach() { | ||
shared.each(this.source, addChild, this); | ||
}; | ||
_proto._detach = function _detach() { | ||
shared.each(this.source, removeChild, this); | ||
}; | ||
return AnimatedObject; | ||
}(Animated); | ||
var AnimatedObject = | ||
/** Convert an array or object to a flat payload */ | ||
function toPayload(source) { | ||
var payload = new Set(); | ||
shared.each(source, function (val) { | ||
if (isAnimated(val)) { | ||
shared.each(val.getPayload(), function (node) { | ||
return payload.add(node); | ||
}); | ||
} | ||
}); | ||
return payload; | ||
} | ||
function addChild(parent) { | ||
if (isAnimated(parent)) parent.addChild(this); | ||
} | ||
function removeChild(parent) { | ||
if (isAnimated(parent)) parent.removeChild(this); | ||
} | ||
var AnimatedStyle = | ||
/*#__PURE__*/ | ||
function (_Animated2) { | ||
_inheritsLoose(AnimatedObject, _Animated2); | ||
function (_AnimatedObject) { | ||
_inheritsLoose(AnimatedStyle, _AnimatedObject); | ||
function AnimatedObject(payload) { | ||
var _this3; | ||
function AnimatedStyle(style) { | ||
if (style === void 0) { | ||
style = {}; | ||
} | ||
_this3 = _Animated2.call(this) || this; | ||
_this3.payload = payload; | ||
return _this3; | ||
return _AnimatedObject.call(this, style.transform && G.createAnimatedTransform ? _extends({}, style, { | ||
transform: G.createAnimatedTransform(style.transform) | ||
}) : style) || this; | ||
} | ||
var _proto3 = AnimatedObject.prototype; | ||
return AnimatedStyle; | ||
}(AnimatedObject); | ||
_proto3.getValue = function getValue(animated) { | ||
if (animated === void 0) { | ||
animated = false; | ||
} | ||
var AnimatedInterpolation = | ||
/*#__PURE__*/ | ||
function (_Animated) { | ||
_inheritsLoose(AnimatedInterpolation, _Animated); | ||
var payload = {}; | ||
function AnimatedInterpolation(source, args) { | ||
var _this; | ||
for (var key in this.payload) { | ||
var value = this.payload[key]; | ||
if (animated && !isAnimated(value)) continue; | ||
payload[key] = isAnimated(value) ? value[animated ? 'getAnimatedValue' : 'getValue']() : value; | ||
_this = _Animated.call(this) || this; | ||
_this.source = source; | ||
_this.calc = shared.createInterpolator.apply(void 0, args); | ||
return _this; | ||
} | ||
var _proto = AnimatedInterpolation.prototype; | ||
_proto.getValue = function getValue(animated) { | ||
var args = shared.is.arr(this.source) ? this.source.map(function (node) { | ||
return node.getValue(animated); | ||
}) : shared.toArray(this.source.getValue(animated)); | ||
return this.calc.apply(this, args); | ||
}; | ||
_proto.interpolate = function interpolate() { | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return payload; | ||
return G.createAnimatedInterpolation.apply(void 0, [this].concat(args)); | ||
}; | ||
_proto3.getAnimatedValue = function getAnimatedValue() { | ||
return this.getValue(true); | ||
_proto.getPayload = function getPayload() { | ||
return shared.is.arr(this.source) ? this.payload || (this.payload = toPayload(this.source)) : this.source.getPayload(); | ||
}; | ||
_proto3.attach = function attach() { | ||
var _this4 = this; | ||
_proto.updatePayload = function updatePayload(prev, next) { | ||
this.payload = void 0; | ||
Object.values(this.payload).forEach(function (s) { | ||
return isAnimated(s) && s.addChild(_this4); | ||
}); | ||
if (shared.is.arr(this.source)) { | ||
var source = [].concat(this.source); | ||
shared.each(source, function (val, index) { | ||
if (val === prev) source[index] = next; | ||
}); | ||
this.source = source; | ||
} else { | ||
this.source = next; | ||
} | ||
}; | ||
_proto3.detach = function detach() { | ||
var _this5 = this; | ||
_proto._attach = function _attach() { | ||
shared.each(shared.toArray(this.source), addChild, this); | ||
}; | ||
Object.values(this.payload).forEach(function (s) { | ||
return isAnimated(s) && s.removeChild(_this5); | ||
}); | ||
_proto._detach = function _detach() { | ||
shared.each(shared.toArray(this.source), removeChild, this); | ||
}; | ||
return AnimatedObject; | ||
return AnimatedInterpolation; | ||
}(Animated); | ||
var AnimatedStyle = | ||
/*#__PURE__*/ | ||
function (_AnimatedObject) { | ||
_inheritsLoose(AnimatedStyle, _AnimatedObject); | ||
function AnimatedStyle(style) { | ||
if (style === void 0) { | ||
style = {}; | ||
shared.Globals.assign({ | ||
createAnimatedStyle: function createAnimatedStyle(style) { | ||
return new AnimatedStyle(style); | ||
}, | ||
createAnimatedInterpolation: function createAnimatedInterpolation(parents) { | ||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
args[_key - 1] = arguments[_key]; | ||
} | ||
return _AnimatedObject.call(this, style.transform && G.createAnimatedTransform ? _extends({}, style, { | ||
transform: G.createAnimatedTransform(style.transform) | ||
}) : style) || this; | ||
return new AnimatedInterpolation(parents, args); | ||
} | ||
}); | ||
return AnimatedStyle; | ||
}(AnimatedObject); | ||
/** An animated number or a native attribute value */ | ||
/** | ||
* 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 AnimatedValue 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 addAnimatedStyles(node, styles) { | ||
if ('update' in node) { | ||
styles.add(node); | ||
} else { | ||
node.getChildren().forEach(function (child) { | ||
return addAnimatedStyles(child, styles); | ||
}); | ||
} | ||
} | ||
var AnimatedValue = | ||
@@ -203,15 +232,6 @@ /*#__PURE__*/ | ||
_this = _Animated.call(this) || this; | ||
_this.animatedStyles = new Set(); | ||
_this.views = new Set(); | ||
_this.done = false; | ||
_this.setValue = function (value, flush) { | ||
if (flush === void 0) { | ||
flush = true; | ||
} | ||
_this.value = value; | ||
if (flush) _this.flush(); | ||
}; | ||
_this.value = value; | ||
_this.payload = new Set([_assertThisInitialized(_this)]); | ||
@@ -226,6 +246,2 @@ if (shared.is.num(value)) { | ||
AnimatedValue.from = function from(value) { | ||
return isAnimated(value) ? value : new AnimatedValue(value); | ||
}; | ||
var _proto = AnimatedValue.prototype; | ||
@@ -237,2 +253,16 @@ | ||
_proto.setValue = function setValue(value, flush) { | ||
this.value = value; | ||
if (flush !== false) { | ||
if (!this.views.size) { | ||
collectViews(this, this.views); | ||
} | ||
shared.each(this.views, function (view) { | ||
return view.update(); | ||
}); | ||
} | ||
}; | ||
_proto.interpolate = function interpolate() { | ||
@@ -256,40 +286,59 @@ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
this.done = false; | ||
this.animatedStyles.clear(); | ||
}; | ||
this.views.clear(); | ||
} // Do nothing for either of these. | ||
; | ||
_proto.clearStyles = function clearStyles() { | ||
this.animatedStyles.clear(); | ||
}; | ||
_proto._attach = function _attach() {}; | ||
_proto.flush = function flush() { | ||
if (this.animatedStyles.size === 0) { | ||
addAnimatedStyles(this, this.animatedStyles); | ||
} | ||
_proto._detach = function _detach() {}; | ||
this.animatedStyles.forEach(function (animatedStyle) { | ||
return animatedStyle.update(); | ||
}); | ||
}; | ||
return AnimatedValue; | ||
}(Animated); | ||
/** | ||
* This library works by building a directed acyclic graph of dependencies | ||
* transparently whenever 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 AnimatedValue is updated, we recursively go down through this | ||
* graph in order to find leaf nodes: the components that depend on our value. | ||
* | ||
* B) Bottom Up phase | ||
* When a view is flagged as needing an update, we recursively go back up | ||
* in order to build the new props that it needs. This two-phase process is | ||
* necessary because some props (eg: "transform") can have multiple parents. | ||
*/ | ||
var AnimatedValueArray = | ||
function collectViews(node, views) { | ||
if ('update' in node) { | ||
views.add(node); | ||
} else { | ||
shared.each(node.getChildren(), function (child) { | ||
return collectViews(child, views); | ||
}); | ||
} | ||
} | ||
/** An array of animated nodes */ | ||
var AnimatedArray = | ||
/*#__PURE__*/ | ||
function (_AnimatedArray) { | ||
_inheritsLoose(AnimatedValueArray, _AnimatedArray); | ||
function (_AnimatedObject) { | ||
_inheritsLoose(AnimatedArray, _AnimatedObject); | ||
function AnimatedValueArray(values) { | ||
var _this; | ||
_this = _AnimatedArray.call(this) || this; | ||
_this.payload = values; | ||
return _this; | ||
function AnimatedArray(source) { | ||
return _AnimatedObject.call(this, source) || this; | ||
} | ||
var _proto = AnimatedValueArray.prototype; | ||
var _proto = AnimatedArray.prototype; | ||
_proto.getValue = function getValue() { | ||
return this.payload.map(function (v) { | ||
return v.getValue(); | ||
_proto.getValue = function getValue(animated) { | ||
return this.source.map(function (node) { | ||
return node.getValue(animated); | ||
}); | ||
@@ -299,17 +348,13 @@ }; | ||
_proto.setValue = function setValue(value, flush) { | ||
var _this2 = this; | ||
var nodes = this.payload; | ||
if (flush === void 0) { | ||
flush = true; | ||
} | ||
if (shared.is.arr(value)) { | ||
if (value.length === this.payload.length) { | ||
value.forEach(function (v, i) { | ||
return _this2.payload[i].setValue(v, flush); | ||
}); | ||
} | ||
invariant(value.length == nodes.size); | ||
var i = 0; | ||
shared.each(nodes, function (node) { | ||
return node.setValue(value[i++], flush); | ||
}); | ||
} else { | ||
this.payload.forEach(function (p) { | ||
return p.setValue(value, flush); | ||
shared.each(nodes, function (node) { | ||
return node.setValue(value, flush); | ||
}); | ||
@@ -327,56 +372,14 @@ } | ||
return AnimatedValueArray; | ||
}(AnimatedArray); | ||
var AnimatedInterpolation = | ||
/*#__PURE__*/ | ||
function (_AnimatedArray) { | ||
_inheritsLoose(AnimatedInterpolation, _AnimatedArray); | ||
function AnimatedInterpolation(parents, args) { | ||
var _this; | ||
_this = _AnimatedArray.call(this) || this; | ||
_this.calc = shared.createInterpolator.apply(void 0, args); | ||
_this.payload = Array.isArray(parents) ? parents.map(AnimatedValue.from) : parents instanceof AnimatedValueArray ? parents.getPayload() : [parents]; | ||
return _this; | ||
} | ||
var _proto = AnimatedInterpolation.prototype; | ||
_proto.getValue = function getValue() { | ||
var args = this.payload.map(function (value) { | ||
return value.getValue(); | ||
_proto.updatePayload = function updatePayload(prev, next) { | ||
var source = [].concat(this.source); | ||
shared.each(source, function (val, index) { | ||
if (val === prev) source[index] = next; | ||
}); | ||
return this.calc.apply(this, args); | ||
this.source = source; | ||
this.payload = toPayload(source); | ||
}; | ||
_proto.interpolate = function interpolate() { | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return AnimatedArray; | ||
}(AnimatedObject); | ||
return G.createAnimatedInterpolation.apply(void 0, [this].concat(args)); | ||
}; | ||
return AnimatedInterpolation; | ||
}(AnimatedArray); | ||
shared.Globals.assign({ | ||
createAnimatedStyle: function createAnimatedStyle(style) { | ||
return new AnimatedStyle(style); | ||
}, | ||
createAnimatedInterpolation: function createAnimatedInterpolation(parents) { | ||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
args[_key - 1] = arguments[_key]; | ||
} | ||
return new AnimatedInterpolation(parents, args); | ||
} | ||
}); | ||
/** | ||
* Wraps the `style` property with `AnimatedStyle`. | ||
*/ | ||
var AnimatedProps = | ||
@@ -387,3 +390,3 @@ /*#__PURE__*/ | ||
function AnimatedProps(props, callback) { | ||
function AnimatedProps(props, update) { | ||
var _this; | ||
@@ -394,6 +397,3 @@ | ||
}) : props) || this; | ||
_this.update = callback; | ||
_this.attach(); | ||
_this.update = update; | ||
return _this; | ||
@@ -406,36 +406,36 @@ } | ||
var createAnimatedComponent = function createAnimatedComponent(Component) { | ||
return React.forwardRef(function (props, ref) { | ||
var propsAnimated = React.useRef(null); | ||
return React.forwardRef(function (rawProps, ref) { | ||
var node = React.useRef(null); | ||
var props = React.useRef(null); | ||
var forceUpdate = shared.useForceUpdate(); | ||
var node = React.useRef(null); | ||
var attachProps = React.useCallback(function (props) { | ||
var oldPropsAnimated = propsAnimated.current; | ||
var nextProps = new AnimatedProps(rawProps, function () { | ||
var didUpdate = !!node.current && G.applyAnimatedValues(node.current, nextProps.getValue(true)); // Re-render the component when native updates fail. | ||
var callback = function callback() { | ||
var didUpdate = !!node.current && G.applyAnimatedValues(node.current, propsAnimated.current.getAnimatedValue()); // If no referenced node has been found, or the update target didn't have a | ||
// native-responder, then forceUpdate the animation ... | ||
if (didUpdate === false) { | ||
forceUpdate(); | ||
} | ||
}); | ||
React.useEffect(function () { | ||
var prevProps = props.current; | ||
props.current = nextProps; // To avoid causing a cascade of detachment, we must detach | ||
// the old props only *after* the new props are attached. | ||
if (didUpdate === false) forceUpdate(); | ||
}; | ||
nextProps._attach(); | ||
propsAnimated.current = new AnimatedProps(props, callback); | ||
oldPropsAnimated && oldPropsAnimated.detach(); | ||
return propsAnimated.current.getValue(); | ||
}, []); | ||
if (prevProps) { | ||
prevProps._detach(); | ||
} | ||
}); // Ensure the latest props are detached on unmount. | ||
shared.useOnce(function () { | ||
return function () { | ||
propsAnimated.current && propsAnimated.current.detach(); | ||
props.current._detach(); | ||
}; | ||
}); // TODO: Avoid special case for scrollTop/scrollLeft | ||
}); // Functions cannot have refs (see #569) | ||
var _attachProps = attachProps(props), | ||
scrollTop = _attachProps.scrollTop, | ||
scrollLeft = _attachProps.scrollLeft, | ||
animatedProps = _objectWithoutPropertiesLoose(_attachProps, ["scrollTop", "scrollLeft"]); // Functions cannot have refs (see #569) | ||
var refFn = !shared.is.fun(Component) || Component.prototype.isReactComponent ? function (value) { | ||
return node.current = updateRef(ref, value); | ||
} : void 0; | ||
return React__default.createElement(Component, Object.assign({}, animatedProps, { | ||
rawProps = G.getComponentProps(nextProps.getValue()); | ||
return React__default.createElement(Component, Object.assign({}, rawProps, { | ||
ref: refFn | ||
@@ -464,16 +464,4 @@ })); | ||
var extend = function extend(arg, overrideKey) { | ||
// Arrays avoid passing their index to `extend` | ||
if (shared.is.arr(arg)) { | ||
return arg.forEach(function (arg) { | ||
return extend(arg); | ||
}); | ||
} // Object keys are always used over value inspection | ||
if (shared.is.obj(arg)) { | ||
for (var _key in arg) { | ||
extend(arg[_key], _key); | ||
} | ||
return; | ||
if (shared.is.arr(arg) || shared.is.obj(arg)) { | ||
return shared.each(arg, extend); | ||
} // Use the `overrideKey` or inspect the value for a key | ||
@@ -495,7 +483,7 @@ | ||
self.extend = function () { | ||
for (var _len = arguments.length, args = new Array(_len), _key2 = 0; _key2 < _len; _key2++) { | ||
args[_key2] = arguments[_key2]; | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
args.forEach(function (arg) { | ||
shared.each(args, function (arg) { | ||
return extend(arg); | ||
@@ -522,6 +510,9 @@ }); | ||
exports.AnimatedValue = AnimatedValue; | ||
exports.AnimatedValueArray = AnimatedValueArray; | ||
exports.addChild = addChild; | ||
exports.animatedTag = animatedTag; | ||
exports.createAnimatedComponent = createAnimatedComponent; | ||
exports.isAnimated = isAnimated; | ||
exports.removeChild = removeChild; | ||
exports.toPayload = toPayload; | ||
exports.withExtend = withExtend; | ||
//# sourceMappingURL=index.cjs.js.map |
139
index.d.ts
@@ -1,39 +0,7 @@ | ||
import { SpringValue, Animatable, InterpolatorArgs, OneOrMore, Interpolatable, Interpolator, ElementType } from '@react-spring/shared'; | ||
import { SpringValue, Animatable, InterpolatorArgs, Indexable, Interpolatable, OneOrMore, Interpolator, ElementType } from '@react-spring/shared'; | ||
export { createAnimatedInterpolation as interpolate } from '@react-spring/shared/globals'; | ||
declare function isAnimated(val: unknown): val is Animated; | ||
declare abstract class Animated<Payload = unknown> { | ||
abstract getValue(): any; | ||
getAnimatedValue(): any; | ||
protected payload?: Payload; | ||
getPayload(): Payload | this; | ||
attach(): void; | ||
detach(): void; | ||
private children; | ||
getChildren(): Animated<unknown>[]; | ||
addChild(child: Animated): void; | ||
removeChild(child: Animated): void; | ||
} | ||
declare abstract class AnimatedArray<Payload extends ReadonlyArray<any> = ReadonlyArray<unknown>> extends Animated<Payload> { | ||
protected payload: Payload; | ||
attach(): void; | ||
detach(): void; | ||
} | ||
declare class AnimatedObject<Payload extends { | ||
[key: string]: unknown; | ||
}> extends Animated<Payload> { | ||
protected payload: Payload; | ||
constructor(payload: Payload); | ||
getValue(animated?: boolean): { | ||
[key: string]: any; | ||
}; | ||
getAnimatedValue(): { | ||
[key: string]: any; | ||
}; | ||
attach(): void; | ||
detach(): void; | ||
} | ||
declare class AnimatedValue<T = number> extends Animated implements SpringValue<T> { | ||
private animatedStyles; | ||
/** An animated number or a native attribute value */ | ||
declare class AnimatedValue<T = unknown> extends Animated implements SpringValue<T> { | ||
private views; | ||
value: T; | ||
@@ -46,50 +14,83 @@ startPosition: number; | ||
done: boolean; | ||
static from(value: any): Animated<unknown>; | ||
constructor(value: T); | ||
getValue(): T; | ||
setValue: (value: T, flush?: boolean) => void; | ||
setValue(value: T, flush?: boolean): void; | ||
interpolate<Out extends Animatable>(...args: InterpolatorArgs<T, Out>): SpringValue<Out>; | ||
reset(isActive: boolean): void; | ||
clearStyles(): void; | ||
private flush; | ||
updatePayload: any; | ||
_attach(): void; | ||
_detach(): void; | ||
} | ||
declare class AnimatedValueArray<T extends AnimatedValue[] = AnimatedValue[]> extends AnimatedArray<T> implements SpringValue<{ | ||
[P in keyof T]: number; | ||
}> { | ||
constructor(values: T); | ||
getValue(): { | ||
[P in keyof T]: number; | ||
}; | ||
setValue(value: OneOrMore<number>, flush?: boolean): void; | ||
interpolate<Out extends Animatable>(...args: InterpolatorArgs<{ | ||
[P in keyof T]: number; | ||
}, Out>): SpringValue<Out>; | ||
declare const animatedTag: unique symbol; | ||
declare const isAnimated: (val: any) => val is Animated; | ||
declare abstract class Animated { | ||
protected [animatedTag]: boolean; | ||
protected children: Set<Animated>; | ||
protected payload?: Set<AnimatedValue>; | ||
/** Returns all values contained by this node. Pass true for only the animated values. */ | ||
abstract getValue(animated?: boolean): any; | ||
/** Returns the set of `AnimatedValue` nodes contained by this node. */ | ||
getPayload(): ReadonlySet<AnimatedValue>; | ||
/** Replace an `AnimatedValue` node in the payload. */ | ||
abstract updatePayload(prev: Animated, next: Animated): void; | ||
/** Returns the set of animated nodes that depend on this node. */ | ||
getChildren(): ReadonlySet<Animated>; | ||
addChild(child: Animated): void; | ||
removeChild(child: Animated): void; | ||
/** Called when this node goes from 0 children to 1+ children. */ | ||
abstract _attach(): void; | ||
/** Called when this node goes from 1+ children to 0 children. */ | ||
abstract _detach(): void; | ||
} | ||
/** Wrap each element type of `T` with the `Animated` type */ | ||
declare type AnimatedInputs<T extends Interpolatable> = { | ||
[P in keyof T]: Animated<T[P]>; | ||
}; | ||
declare class AnimatedInterpolation<In extends Interpolatable = Interpolatable, Out extends Animatable = Animatable> extends AnimatedArray<AnimatedInputs<In>> implements SpringValue<Out> { | ||
declare class AnimatedObject extends Animated { | ||
protected source: Indexable; | ||
protected payload: Set<AnimatedValue>; | ||
constructor(source: Indexable); | ||
getValue(animated?: boolean): any; | ||
updatePayload(prev: Animated, next: Animated): void; | ||
_attach(): void; | ||
_detach(): void; | ||
} | ||
/** Convert an array or object to a flat payload */ | ||
declare function toPayload(source: Indexable): Set<AnimatedValue<unknown>>; | ||
declare function addChild(this: Animated, parent: any): void; | ||
declare function removeChild(this: Animated, parent: any): void; | ||
/** An array of animated nodes */ | ||
declare class AnimatedArray extends AnimatedObject implements SpringValue<any[]> { | ||
protected source: Animated[]; | ||
constructor(source: Animated[]); | ||
getValue(animated?: boolean): any[]; | ||
setValue(value: any, flush?: boolean): void; | ||
interpolate<Out extends Animatable>(...args: InterpolatorArgs<any[], Out>): SpringValue<Out>; | ||
updatePayload(prev: Animated, next: Animated): void; | ||
} | ||
declare class AnimatedInterpolation<In extends Interpolatable = Interpolatable, Out extends Animatable = Animatable> extends Animated implements SpringValue<Out> { | ||
source: OneOrMore<Animated>; | ||
calc: Interpolator<In>; | ||
constructor(parents: SpringValue | ReadonlyArray<SpringValue>, args: InterpolatorArgs<In, Out>); | ||
getValue(): Out; | ||
constructor(source: OneOrMore<Animated>, args: InterpolatorArgs<In, Out>); | ||
getValue(animated?: boolean): Out; | ||
interpolate<T extends Animatable>(...args: InterpolatorArgs<Out, T>): SpringValue<T>; | ||
getPayload(): ReadonlySet<AnimatedValue>; | ||
updatePayload(prev: Animated, next: Animated): void; | ||
_attach(): void; | ||
_detach(): void; | ||
} | ||
/** | ||
* Wraps the `style` property with `AnimatedStyle`. | ||
*/ | ||
declare class AnimatedProps<Props extends object & { | ||
declare type Props = object & { | ||
style?: any; | ||
} = {}> extends AnimatedObject<Props> { | ||
}; | ||
declare class AnimatedProps extends AnimatedObject { | ||
update: () => void; | ||
constructor(props: Props, callback: () => void); | ||
constructor(props: Props, update: () => void); | ||
} | ||
declare class AnimatedStyle<Payload extends object & { | ||
transform?: Animated; | ||
} = {}> extends AnimatedObject<Payload> { | ||
constructor(style?: Payload); | ||
declare type Style = object & { | ||
transform?: any; | ||
}; | ||
declare class AnimatedStyle extends AnimatedObject { | ||
constructor(style?: Style); | ||
} | ||
@@ -111,2 +112,2 @@ | ||
export { Animated, AnimatedArray, AnimatedInterpolation, AnimatedObject, AnimatedProps, AnimatedStyle, AnimatedValue, AnimatedValueArray, WithExtend, createAnimatedComponent, isAnimated, withExtend }; | ||
export { Animated, AnimatedArray, AnimatedInterpolation, AnimatedObject, AnimatedProps, AnimatedStyle, AnimatedValue, WithExtend, addChild, animatedTag, createAnimatedComponent, isAnimated, removeChild, toPayload, withExtend }; |
400
index.js
@@ -1,28 +0,26 @@ | ||
import { is, createInterpolator, Globals, useForceUpdate, useOnce } from '@react-spring/shared'; | ||
import { each, createInterpolator, is, toArray, Globals, useForceUpdate, useOnce } from '@react-spring/shared'; | ||
import _extends from '@babel/runtime/helpers/esm/extends'; | ||
import { createAnimatedTransform, createAnimatedInterpolation, now, createAnimatedStyle, applyAnimatedValues } from '@react-spring/shared/globals'; | ||
import { createAnimatedTransform, createAnimatedInterpolation, now, createAnimatedStyle, applyAnimatedValues, getComponentProps } from '@react-spring/shared/globals'; | ||
export { createAnimatedInterpolation as interpolate } from '@react-spring/shared/globals'; | ||
import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/esm/objectWithoutPropertiesLoose'; | ||
import React, { forwardRef, useRef, useCallback } from 'react'; | ||
import invariant from 'tiny-invariant'; | ||
import React, { forwardRef, useRef, useEffect } from 'react'; | ||
function isAnimated(val) { | ||
return val instanceof Animated; | ||
} | ||
var _a; | ||
const animatedTag = Symbol.for('isAnimated'); | ||
const isAnimated = val => !!(val && val[animatedTag]); | ||
class Animated { | ||
constructor() { | ||
this.children = []; | ||
this[_a] = true; | ||
this.children = new Set(); | ||
} | ||
/** Returns the set of `AnimatedValue` nodes contained by this node. */ | ||
getAnimatedValue() { | ||
return this.getValue(); | ||
} | ||
getPayload() { | ||
return this.payload || this; | ||
return this.payload; | ||
} | ||
/** Returns the set of animated nodes that depend on this node. */ | ||
attach() {} | ||
detach() {} | ||
getChildren() { | ||
@@ -33,58 +31,69 @@ return this.children; | ||
addChild(child) { | ||
if (this.children.length === 0) this.attach(); | ||
this.children.push(child); | ||
this.children.size || this._attach(); | ||
this.children.add(child); | ||
} | ||
removeChild(child) { | ||
const index = this.children.indexOf(child); | ||
this.children.splice(index, 1); | ||
if (this.children.length === 0) this.detach(); | ||
this.children.delete(child); | ||
this.children.size || this._detach(); | ||
} | ||
} | ||
class AnimatedArray extends Animated { | ||
attach() { | ||
this.payload.forEach(p => isAnimated(p) && p.addChild(this)); | ||
} | ||
_a = animatedTag; | ||
detach() { | ||
this.payload.forEach(p => isAnimated(p) && p.removeChild(this)); | ||
} | ||
} | ||
class AnimatedObject extends Animated { | ||
constructor(payload) { | ||
constructor(source) { | ||
super(); | ||
this.payload = payload; | ||
this.source = source; | ||
this.payload = toPayload(source); | ||
} | ||
getValue(animated) { | ||
if (animated === void 0) { | ||
animated = false; | ||
} | ||
const obj = {}; | ||
each(this.source, (val, key) => { | ||
if (isAnimated(val)) { | ||
obj[key] = val.getValue(animated); | ||
} else if (!animated) { | ||
obj[key] = val; | ||
} | ||
}); | ||
return obj; | ||
} | ||
const payload = {}; | ||
updatePayload(prev, next) { | ||
const source = _extends({}, this.source); | ||
for (const key in this.payload) { | ||
const value = this.payload[key]; | ||
if (animated && !isAnimated(value)) continue; | ||
payload[key] = isAnimated(value) ? value[animated ? 'getAnimatedValue' : 'getValue']() : value; | ||
} | ||
return payload; | ||
each(source, (val, key) => { | ||
if (val === prev) source[key] = next; | ||
}); | ||
this.source = source; | ||
this.payload = toPayload(source); | ||
} | ||
getAnimatedValue() { | ||
return this.getValue(true); | ||
_attach() { | ||
each(this.source, addChild, this); | ||
} | ||
attach() { | ||
Object.values(this.payload).forEach(s => isAnimated(s) && s.addChild(this)); | ||
_detach() { | ||
each(this.source, removeChild, this); | ||
} | ||
detach() { | ||
Object.values(this.payload).forEach(s => isAnimated(s) && s.removeChild(this)); | ||
} | ||
} | ||
/** Convert an array or object to a flat payload */ | ||
function toPayload(source) { | ||
const payload = new Set(); | ||
each(source, val => { | ||
if (isAnimated(val)) { | ||
each(val.getPayload(), node => payload.add(node)); | ||
} | ||
}); | ||
return payload; | ||
} | ||
function addChild(parent) { | ||
if (isAnimated(parent)) parent.addChild(this); | ||
} | ||
function removeChild(parent) { | ||
if (isAnimated(parent)) parent.removeChild(this); | ||
} | ||
@@ -104,52 +113,70 @@ class AnimatedStyle extends AnimatedObject { | ||
/** | ||
* 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 AnimatedValue 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. | ||
*/ | ||
class AnimatedInterpolation extends Animated { | ||
constructor(source, args) { | ||
super(); | ||
this.source = source; | ||
this.calc = createInterpolator(...args); | ||
} | ||
function addAnimatedStyles(node, styles) { | ||
if ('update' in node) { | ||
styles.add(node); | ||
} else { | ||
node.getChildren().forEach(child => addAnimatedStyles(child, styles)); | ||
getValue(animated) { | ||
const args = is.arr(this.source) ? this.source.map(node => node.getValue(animated)) : toArray(this.source.getValue(animated)); | ||
return this.calc(...args); | ||
} | ||
interpolate() { | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return createAnimatedInterpolation(this, ...args); | ||
} | ||
getPayload() { | ||
return is.arr(this.source) ? this.payload || (this.payload = toPayload(this.source)) : this.source.getPayload(); | ||
} | ||
updatePayload(prev, next) { | ||
this.payload = void 0; | ||
if (is.arr(this.source)) { | ||
const source = [...this.source]; | ||
each(source, (val, index) => { | ||
if (val === prev) source[index] = next; | ||
}); | ||
this.source = source; | ||
} else { | ||
this.source = next; | ||
} | ||
} | ||
_attach() { | ||
each(toArray(this.source), addChild, this); | ||
} | ||
_detach() { | ||
each(toArray(this.source), removeChild, this); | ||
} | ||
} | ||
Globals.assign({ | ||
createAnimatedStyle: style => new AnimatedStyle(style), | ||
createAnimatedInterpolation: function createAnimatedInterpolation(parents) { | ||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
args[_key - 1] = arguments[_key]; | ||
} | ||
return new AnimatedInterpolation(parents, args); | ||
} | ||
}); | ||
/** An animated number or a native attribute value */ | ||
class AnimatedValue extends Animated { | ||
constructor(value) { | ||
var _this; | ||
super(); | ||
_this = this; | ||
this.animatedStyles = new Set(); | ||
this.views = new Set(); | ||
this.done = false; | ||
this.setValue = function (value, flush) { | ||
if (flush === void 0) { | ||
flush = true; | ||
} | ||
_this.value = value; | ||
if (flush) _this.flush(); | ||
}; | ||
this.value = value; | ||
this.payload = new Set([this]); | ||
@@ -162,6 +189,2 @@ if (is.num(value)) { | ||
static from(value) { | ||
return isAnimated(value) ? value : new AnimatedValue(value); | ||
} | ||
getValue() { | ||
@@ -171,2 +194,14 @@ return this.value; | ||
setValue(value, flush) { | ||
this.value = value; | ||
if (flush !== false) { | ||
if (!this.views.size) { | ||
collectViews(this, this.views); | ||
} | ||
each(this.views, view => view.update()); | ||
} | ||
} | ||
interpolate() { | ||
@@ -190,40 +225,60 @@ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
this.done = false; | ||
this.animatedStyles.clear(); | ||
} | ||
this.views.clear(); | ||
} // Do nothing for either of these. | ||
clearStyles() { | ||
this.animatedStyles.clear(); | ||
} | ||
flush() { | ||
if (this.animatedStyles.size === 0) { | ||
addAnimatedStyles(this, this.animatedStyles); | ||
} | ||
_attach() {} | ||
this.animatedStyles.forEach(animatedStyle => animatedStyle.update()); | ||
} | ||
_detach() {} | ||
} | ||
/** | ||
* This library works by building a directed acyclic graph of dependencies | ||
* transparently whenever 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 AnimatedValue is updated, we recursively go down through this | ||
* graph in order to find leaf nodes: the components that depend on our value. | ||
* | ||
* B) Bottom Up phase | ||
* When a view is flagged as needing an update, we recursively go back up | ||
* in order to build the new props that it needs. This two-phase process is | ||
* necessary because some props (eg: "transform") can have multiple parents. | ||
*/ | ||
class AnimatedValueArray extends AnimatedArray { | ||
constructor(values) { | ||
super(); | ||
this.payload = values; | ||
function collectViews(node, views) { | ||
if ('update' in node) { | ||
views.add(node); | ||
} else { | ||
each(node.getChildren(), child => collectViews(child, views)); | ||
} | ||
} | ||
getValue() { | ||
return this.payload.map(v => v.getValue()); | ||
/** An array of animated nodes */ | ||
class AnimatedArray extends AnimatedObject { | ||
constructor(source) { | ||
super(source); | ||
} | ||
getValue(animated) { | ||
return this.source.map(node => node.getValue(animated)); | ||
} | ||
setValue(value, flush) { | ||
if (flush === void 0) { | ||
flush = true; | ||
} | ||
const nodes = this.payload; | ||
if (is.arr(value)) { | ||
if (value.length === this.payload.length) { | ||
value.forEach((v, i) => this.payload[i].setValue(v, flush)); | ||
} | ||
invariant(value.length == nodes.size); | ||
let i = 0; | ||
each(nodes, node => node.setValue(value[i++], flush)); | ||
} else { | ||
this.payload.forEach(p => p.setValue(value, flush)); | ||
each(nodes, node => node.setValue(value, flush)); | ||
} | ||
@@ -240,48 +295,19 @@ } | ||
} | ||
class AnimatedInterpolation extends AnimatedArray { | ||
constructor(parents, args) { | ||
super(); | ||
this.calc = createInterpolator(...args); | ||
this.payload = Array.isArray(parents) ? parents.map(AnimatedValue.from) : parents instanceof AnimatedValueArray ? parents.getPayload() : [parents]; | ||
updatePayload(prev, next) { | ||
const source = [...this.source]; | ||
each(source, (val, index) => { | ||
if (val === prev) source[index] = next; | ||
}); | ||
this.source = source; | ||
this.payload = toPayload(source); | ||
} | ||
getValue() { | ||
const args = this.payload.map(value => value.getValue()); | ||
return this.calc(...args); | ||
} | ||
interpolate() { | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return createAnimatedInterpolation(this, ...args); | ||
} | ||
} | ||
Globals.assign({ | ||
createAnimatedStyle: style => new AnimatedStyle(style), | ||
createAnimatedInterpolation: function createAnimatedInterpolation(parents) { | ||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
args[_key - 1] = arguments[_key]; | ||
} | ||
return new AnimatedInterpolation(parents, args); | ||
} | ||
}); | ||
/** | ||
* Wraps the `style` property with `AnimatedStyle`. | ||
*/ | ||
class AnimatedProps extends AnimatedObject { | ||
constructor(props, callback) { | ||
constructor(props, update) { | ||
super(props.style && createAnimatedStyle ? _extends({}, props, { | ||
style: createAnimatedStyle(props.style) | ||
}) : props); | ||
this.update = callback; | ||
this.attach(); | ||
this.update = update; | ||
} | ||
@@ -291,32 +317,32 @@ | ||
const createAnimatedComponent = Component => forwardRef((props, ref) => { | ||
const propsAnimated = useRef(null); | ||
const createAnimatedComponent = Component => forwardRef((rawProps, ref) => { | ||
const node = useRef(null); | ||
const props = useRef(null); | ||
const forceUpdate = useForceUpdate(); | ||
const node = useRef(null); | ||
const attachProps = useCallback(props => { | ||
const oldPropsAnimated = propsAnimated.current; | ||
const nextProps = new AnimatedProps(rawProps, () => { | ||
const didUpdate = !!node.current && applyAnimatedValues(node.current, nextProps.getValue(true)); // Re-render the component when native updates fail. | ||
const callback = () => { | ||
const didUpdate = !!node.current && applyAnimatedValues(node.current, propsAnimated.current.getAnimatedValue()); // If no referenced node has been found, or the update target didn't have a | ||
// native-responder, then forceUpdate the animation ... | ||
if (didUpdate === false) { | ||
forceUpdate(); | ||
} | ||
}); | ||
useEffect(() => { | ||
const prevProps = props.current; | ||
props.current = nextProps; // To avoid causing a cascade of detachment, we must detach | ||
// the old props only *after* the new props are attached. | ||
if (didUpdate === false) forceUpdate(); | ||
}; | ||
nextProps._attach(); | ||
propsAnimated.current = new AnimatedProps(props, callback); | ||
oldPropsAnimated && oldPropsAnimated.detach(); | ||
return propsAnimated.current.getValue(); | ||
}, []); | ||
if (prevProps) { | ||
prevProps._detach(); | ||
} | ||
}); // Ensure the latest props are detached on unmount. | ||
useOnce(() => () => { | ||
propsAnimated.current && propsAnimated.current.detach(); | ||
}); // TODO: Avoid special case for scrollTop/scrollLeft | ||
props.current._detach(); | ||
}); // Functions cannot have refs (see #569) | ||
const _attachProps = attachProps(props), | ||
scrollTop = _attachProps.scrollTop, | ||
scrollLeft = _attachProps.scrollLeft, | ||
animatedProps = _objectWithoutPropertiesLoose(_attachProps, ["scrollTop", "scrollLeft"]); // Functions cannot have refs (see #569) | ||
const refFn = !is.fun(Component) || Component.prototype.isReactComponent ? value => node.current = updateRef(ref, value) : void 0; | ||
return React.createElement(Component, Object.assign({}, animatedProps, { | ||
rawProps = getComponentProps(nextProps.getValue()); | ||
return React.createElement(Component, Object.assign({}, rawProps, { | ||
ref: refFn | ||
@@ -344,12 +370,4 @@ })); | ||
const extend = (arg, overrideKey) => { | ||
// Arrays avoid passing their index to `extend` | ||
if (is.arr(arg)) { | ||
return arg.forEach(arg => extend(arg)); | ||
} // Object keys are always used over value inspection | ||
if (is.obj(arg)) { | ||
for (const key in arg) extend(arg[key], key); | ||
return; | ||
if (is.arr(arg) || is.obj(arg)) { | ||
return each(arg, extend); | ||
} // Use the `overrideKey` or inspect the value for a key | ||
@@ -375,3 +393,3 @@ | ||
args.forEach(arg => extend(arg)); | ||
each(args, arg => extend(arg)); | ||
return self; | ||
@@ -383,3 +401,3 @@ }; | ||
export { Animated, AnimatedArray, AnimatedInterpolation, AnimatedObject, AnimatedProps, AnimatedStyle, AnimatedValue, AnimatedValueArray, createAnimatedComponent, isAnimated, withExtend }; | ||
export { Animated, AnimatedArray, AnimatedInterpolation, AnimatedObject, AnimatedProps, AnimatedStyle, AnimatedValue, addChild, animatedTag, createAnimatedComponent, isAnimated, removeChild, toPayload, withExtend }; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@react-spring/animated", | ||
"version": "9.0.0-beta.24", | ||
"version": "9.0.0-beta.25", | ||
"description": "Animated component props for React", | ||
@@ -31,3 +31,3 @@ "keywords": [ | ||
"@babel/runtime": "^7.3.1", | ||
"@react-spring/shared": "^9.0.0-beta.24" | ||
"@react-spring/shared": "^9.0.0-beta.25" | ||
}, | ||
@@ -34,0 +34,0 @@ "devDependencies": { |
@@ -1,84 +0,44 @@ | ||
export function isAnimated(val: unknown): val is Animated { | ||
return val instanceof Animated | ||
} | ||
import { AnimatedValue } from './AnimatedValue' | ||
export abstract class Animated<Payload = unknown> { | ||
public abstract getValue(): any | ||
public getAnimatedValue() { | ||
return this.getValue() | ||
} | ||
export const animatedTag = Symbol.for('isAnimated') | ||
protected payload?: Payload | ||
public getPayload() { | ||
return this.payload || this | ||
} | ||
export const isAnimated = (val: any): val is Animated => | ||
!!(val && val[animatedTag]) | ||
public attach(): void {} | ||
export abstract class Animated { | ||
protected [animatedTag] = true | ||
protected children = new Set<Animated>() | ||
protected payload?: Set<AnimatedValue> | ||
public detach(): void {} | ||
/** Returns all values contained by this node. Pass true for only the animated values. */ | ||
abstract getValue(animated?: boolean): any | ||
private children: Animated[] = [] | ||
public getChildren() { | ||
return this.children | ||
/** Returns the set of `AnimatedValue` nodes contained by this node. */ | ||
getPayload(): ReadonlySet<AnimatedValue> { | ||
return this.payload! | ||
} | ||
public addChild(child: Animated) { | ||
if (this.children.length === 0) this.attach() | ||
this.children.push(child) | ||
} | ||
/** Replace an `AnimatedValue` node in the payload. */ | ||
abstract updatePayload(prev: Animated, next: Animated): void | ||
public removeChild(child: Animated) { | ||
const index = this.children.indexOf(child) | ||
this.children.splice(index, 1) | ||
if (this.children.length === 0) this.detach() | ||
/** Returns the set of animated nodes that depend on this node. */ | ||
getChildren(): ReadonlySet<Animated> { | ||
return this.children | ||
} | ||
} | ||
export abstract class AnimatedArray< | ||
Payload extends ReadonlyArray<any> = ReadonlyArray<unknown> | ||
> extends Animated<Payload> { | ||
protected payload!: Payload | ||
public attach() { | ||
this.payload.forEach(p => isAnimated(p) && p.addChild(this)) | ||
addChild(child: Animated) { | ||
this.children.size || this._attach() | ||
this.children.add(child) | ||
} | ||
public detach() { | ||
this.payload.forEach(p => isAnimated(p) && p.removeChild(this)) | ||
removeChild(child: Animated) { | ||
this.children.delete(child) | ||
this.children.size || this._detach() | ||
} | ||
} | ||
export class AnimatedObject< | ||
Payload extends { [key: string]: unknown } | ||
> extends Animated<Payload> { | ||
constructor(protected payload: Payload) { | ||
super() | ||
} | ||
/** Called when this node goes from 0 children to 1+ children. */ | ||
abstract _attach(): void | ||
public getValue(animated = false) { | ||
const payload: { [key: string]: any } = {} | ||
for (const key in this.payload) { | ||
const value = this.payload[key] | ||
if (animated && !isAnimated(value)) continue | ||
payload[key] = isAnimated(value) | ||
? value[animated ? 'getAnimatedValue' : 'getValue']() | ||
: value | ||
} | ||
return payload | ||
} | ||
public getAnimatedValue() { | ||
return this.getValue(true) | ||
} | ||
public attach() { | ||
Object.values(this.payload).forEach(s => isAnimated(s) && s.addChild(this)) | ||
} | ||
public detach() { | ||
Object.values(this.payload).forEach( | ||
s => isAnimated(s) && s.removeChild(this) | ||
) | ||
} | ||
/** Called when this node goes from 1+ children to 0 children. */ | ||
abstract _detach(): void | ||
} |
@@ -8,21 +8,18 @@ import { | ||
Interpolator, | ||
is, | ||
OneOrMore, | ||
toArray, | ||
each, | ||
} from '@react-spring/shared' | ||
import { Animated } from './Animated' | ||
import { interpolate } from './interpolate' | ||
import { Animated, AnimatedArray } from './Animated' | ||
import { AnimatedValue } from './AnimatedValue' | ||
import { AnimatedValueArray } from './AnimatedValueArray' | ||
import { toPayload, addChild, removeChild } from './AnimatedObject' | ||
/** Wrap each element type of `T` with the `Animated` type */ | ||
type AnimatedInputs<T extends Interpolatable> = { | ||
[P in keyof T]: Animated<T[P]> | ||
} | ||
export class AnimatedInterpolation< | ||
In extends Interpolatable = Interpolatable, | ||
Out extends Animatable = Animatable | ||
> extends AnimatedArray<AnimatedInputs<In>> implements SpringValue<Out> { | ||
public calc: Interpolator<In> | ||
> extends Animated implements SpringValue<Out> { | ||
calc: Interpolator<In> | ||
constructor( | ||
parents: SpringValue | ReadonlyArray<SpringValue>, | ||
public source: OneOrMore<Animated>, | ||
args: InterpolatorArgs<In, Out> | ||
@@ -32,19 +29,43 @@ ) { | ||
this.calc = createInterpolator(...(args as [any])) as any | ||
this.payload = Array.isArray(parents) | ||
? parents.map(AnimatedValue.from) | ||
: parents instanceof AnimatedValueArray | ||
? parents.getPayload() | ||
: [parents] | ||
} | ||
public getValue(): Out { | ||
const args = this.payload.map(value => value.getValue()) | ||
return this.calc(...(args as [any])) as any | ||
getValue(animated?: boolean): Out { | ||
const args = is.arr(this.source) | ||
? this.source.map(node => node.getValue(animated)) | ||
: toArray(this.source.getValue(animated)) | ||
return (this.calc as any)(...args) | ||
} | ||
public interpolate<T extends Animatable>( | ||
interpolate<T extends Animatable>( | ||
...args: InterpolatorArgs<Out, T> | ||
): SpringValue<T> { | ||
return interpolate(this, ...(args as [any])) as any | ||
return (interpolate as any)(this, ...args) | ||
} | ||
getPayload() { | ||
return is.arr(this.source) | ||
? this.payload || (this.payload = toPayload(this.source)) | ||
: this.source.getPayload() | ||
} | ||
updatePayload(prev: Animated, next: Animated) { | ||
this.payload = void 0 | ||
if (is.arr(this.source)) { | ||
const source = [...this.source] | ||
each(source, (val, index) => { | ||
if (val === prev) source[index] = next | ||
}) | ||
this.source = source | ||
} else { | ||
this.source = next | ||
} | ||
} | ||
_attach() { | ||
each(toArray(this.source), addChild, this) | ||
} | ||
_detach() { | ||
each(toArray(this.source), removeChild, this) | ||
} | ||
} |
@@ -1,13 +0,8 @@ | ||
import { AnimatedObject } from './Animated' | ||
import { AnimatedObject } from './AnimatedObject' | ||
import * as G from '@react-spring/shared/globals' | ||
/** | ||
* Wraps the `style` property with `AnimatedStyle`. | ||
*/ | ||
export class AnimatedProps< | ||
Props extends object & { style?: any } = {} | ||
> extends AnimatedObject<Props> { | ||
update: () => void | ||
type Props = object & { style?: any } | ||
constructor(props: Props, callback: () => void) { | ||
export class AnimatedProps extends AnimatedObject { | ||
constructor(props: Props, public update: () => void) { | ||
super( | ||
@@ -18,5 +13,3 @@ props.style && G.createAnimatedStyle | ||
) | ||
this.update = callback | ||
this.attach() | ||
} | ||
} |
@@ -1,8 +0,8 @@ | ||
import { Animated, AnimatedObject } from './Animated' | ||
import { AnimatedObject } from './AnimatedObject' | ||
import * as G from '@react-spring/shared/globals' | ||
export class AnimatedStyle< | ||
Payload extends object & { transform?: Animated } = {} | ||
> extends AnimatedObject<Payload> { | ||
constructor(style = {} as Payload) { | ||
type Style = object & { transform?: any } | ||
export class AnimatedStyle extends AnimatedObject { | ||
constructor(style = {} as Style) { | ||
super( | ||
@@ -9,0 +9,0 @@ style.transform && G.createAnimatedTransform |
@@ -1,60 +0,24 @@ | ||
import { Animatable, SpringValue, InterpolatorArgs, is } from '@react-spring/shared' | ||
import { Animated, isAnimated } from './Animated' | ||
import { Animatable, SpringValue, InterpolatorArgs, is, each } from '@react-spring/shared' | ||
import { AnimatedProps } from './AnimatedProps' | ||
import { interpolate } from './interpolate' | ||
import { Animated } from './Animated' | ||
import * as G from '@react-spring/shared/globals' | ||
/** | ||
* 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 AnimatedValue 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 addAnimatedStyles( | ||
node: Animated | AnimatedProps, | ||
styles: Set<AnimatedProps> | ||
) { | ||
if ('update' in node) { | ||
styles.add(node) | ||
} else { | ||
node.getChildren().forEach(child => addAnimatedStyles(child, styles)) | ||
} | ||
} | ||
export class AnimatedValue<T = number> extends Animated | ||
/** An animated number or a native attribute value */ | ||
export class AnimatedValue<T = unknown> extends Animated | ||
implements SpringValue<T> { | ||
private animatedStyles = new Set<AnimatedProps>() | ||
private views = new Set<AnimatedProps>() | ||
public value: T | ||
public startPosition!: number | ||
public lastPosition!: number | ||
public lastVelocity?: number | ||
public startTime?: number | ||
public lastTime?: number | ||
public done = false | ||
value: T | ||
startPosition!: number | ||
lastPosition!: number | ||
lastVelocity?: number | ||
startTime?: number | ||
lastTime?: number | ||
done = false | ||
static from(value: any) { | ||
return isAnimated(value) ? value : new AnimatedValue(value) | ||
} | ||
constructor(value: T) { | ||
super() | ||
this.value = value | ||
this.payload = new Set([this]) | ||
if (is.num(value)) { | ||
@@ -66,18 +30,23 @@ this.startPosition = value | ||
public getValue() { | ||
getValue() { | ||
return this.value | ||
} | ||
public setValue = (value: T, flush = true) => { | ||
setValue(value: T, flush?: boolean) { | ||
this.value = value | ||
if (flush) this.flush() | ||
if (flush !== false) { | ||
if (!this.views.size) { | ||
collectViews(this, this.views) | ||
} | ||
each(this.views, view => view.update()) | ||
} | ||
} | ||
public interpolate<Out extends Animatable>( | ||
interpolate<Out extends Animatable>( | ||
...args: InterpolatorArgs<T, Out> | ||
): SpringValue<Out> { | ||
return interpolate(this, ...(args as [any])) as any | ||
return (interpolate as any)(this, ...args) | ||
} | ||
public reset(isActive: boolean) { | ||
reset(isActive: boolean) { | ||
if (is.num(this.value)) { | ||
@@ -91,15 +60,42 @@ this.startPosition = this.value | ||
this.done = false | ||
this.animatedStyles.clear() | ||
this.views.clear() | ||
} | ||
public clearStyles() { | ||
this.animatedStyles.clear() | ||
} | ||
// This is never called for AnimatedValue nodes. | ||
updatePayload: any | ||
private flush() { | ||
if (this.animatedStyles.size === 0) { | ||
addAnimatedStyles(this, this.animatedStyles) | ||
} | ||
this.animatedStyles.forEach(animatedStyle => animatedStyle.update()) | ||
// Do nothing for either of these. | ||
_attach() {} | ||
_detach() {} | ||
} | ||
/** | ||
* This library works by building a directed acyclic graph of dependencies | ||
* transparently whenever 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 AnimatedValue is updated, we recursively go down through this | ||
* graph in order to find leaf nodes: the components that depend on our value. | ||
* | ||
* B) Bottom Up phase | ||
* When a view is flagged as needing an update, we recursively go back up | ||
* in order to build the new props that it needs. This two-phase process is | ||
* necessary because some props (eg: "transform") can have multiple parents. | ||
*/ | ||
function collectViews( | ||
node: Animated | AnimatedProps, | ||
views: Set<AnimatedProps> | ||
) { | ||
if ('update' in node) { | ||
views.add(node) | ||
} else { | ||
each(node.getChildren(), child => collectViews(child, views)) | ||
} | ||
} |
import './globals' | ||
export * from './Animated' | ||
export * from './AnimatedValue' | ||
export * from './AnimatedValueArray' | ||
export * from './AnimatedArray' | ||
export * from './AnimatedObject' | ||
export * from './AnimatedInterpolation' | ||
@@ -6,0 +7,0 @@ export * from './AnimatedProps' |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
63944
20
1270
1