├── .babelrc
├── .eslintrc
├── .gitignore
├── README.md
├── example
├── app.js
├── index.html
├── index.js
└── style.css
├── lib
├── Animated.js
├── Easing.js
├── Interpolation.js
├── bezier.js
└── index.js
├── package.json
├── server.js
├── src
├── Animated.js
├── Easing.js
├── Interpolation.js
├── bezier.js
└── index.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "stage": 0
3 | }
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "rules" :{
4 | "quotes": [1, "single"],
5 | "no-unused-vars": [1, {"vars": "all", "args": "all"}],
6 | "strict": [2, "global"]
7 | },
8 | "env":{
9 | "browser": true,
10 | "node" : true
11 | }
12 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # osx noise
2 | .DS_Store
3 | profile
4 |
5 | # xcode noise
6 | build/*
7 | *.mode1
8 | *.mode1v3
9 | *.mode2v3
10 | *.perspective
11 | *.perspectivev3
12 | *.pbxuser
13 | *.xcworkspace
14 | xcuserdata
15 |
16 | # svn & cvs
17 | .svn
18 | CVS
19 | node_modules
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | *unofficial* bootleg of Animated for web, the new animation library for reactjs from the fine folks at facebook. this code was ripped from the link http://fooo.fr/~vjeux/fb/animated-docs/
2 |
3 | `npm install threepointone/react-animated-web-bootleg --save`
4 |
5 | to run example, `npm install && npm start`, then open `http://localhost:3000/example/index.html`
--------------------------------------------------------------------------------
/example/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {Component} from 'react';
4 | import Animated from '../src';
5 |
6 | export class App extends Component{
7 | state = {
8 | anim : new Animated.Value(0)
9 | }
10 | onClick = () => {
11 | Animated.timing(this.state.anim, {toValue: 400}).start();
12 | }
13 | render(){
14 | return
18 | click me
19 |
20 | }
21 | }
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {App} from './app';
3 |
4 | React.render(, document.getElementById('root'));
--------------------------------------------------------------------------------
/example/style.css:
--------------------------------------------------------------------------------
1 | /*html,
2 | h1,
3 | h2 {
4 | font-family: 'Roboto', sans-serif;
5 | font-weight: 300
6 | }
7 | *//*.container {
8 | width: 800px;
9 | margin: 0 auto
10 | }
11 | */.circle {
12 | margin: 2px;
13 | width: 50px;
14 | height: 50px;
15 | position: absolute;
16 | display: inline-block;
17 | box-shadow: 0 1px 2px #999;
18 | text-shadow: 0 1px 2px #999;
19 | background-image: url(https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xtf1/v/t1.0-1/p320x320/10958296_10152690932671188_4256540723631642566_n.jpg?oh=a69694fcf15345249df14dea4c3e3066&oe=5659ED51&__gda__=1444749767_96630067bf758b113706aa1c91fde392);
20 | background-size: cover;
21 | line-height: 80px;
22 | vertical-align: bottom;
23 | text-align: center;
24 | color: #fff;
25 | font-size: 10px
26 | }
27 | .circle:nth-child(2) {
28 | background-image: url(https://scontent-cdg2-1.xx.fbcdn.net/hphotos-xtf1/v/t1.0-9/1923173_716264837373_4589103_n.jpg?oh=e0ec3bd50f6567875a44ea6173e466cd&oe=5619375B)
29 | }
30 | .circle:nth-child(3) {
31 | background-image: url(https://scontent-cdg2-1.xx.fbcdn.net/hphotos-xtf1/v/t1.0-9/10170789_2386880322730_6755618519839435556_n.jpg?oh=107da1d6c5a1c7783a0e09ea7b04102f&oe=5657F184)
32 | }
33 | /*div.code {
34 | box-shadow: 0 1px 2px #999;
35 | width: 600px;
36 | padding: 5px;
37 | position: relative;
38 | margin: 0 auto;
39 | margin-bottom: 40px
40 | }
41 | div.code .reset {
42 | float: right
43 | }
44 | div.code pre {
45 | padding: 2px
46 | }
47 | hr {
48 | border: none;
49 | border-bottom: 1px solid #d9d9d9;
50 | margin: 0
51 | }
52 | button {
53 | vertical-align: top
54 | }
55 | .example>span {
56 | color: #333;
57 | font-size: 13px
58 | }
59 | .example {
60 | position: relative;
61 | height: 60px
62 | }
63 | .code pre {
64 | margin: 0;
65 | font-size: 11px;
66 | line-height: 1
67 | }
68 | .highlight {
69 | background: #e4fefd
70 | }*/
--------------------------------------------------------------------------------
/lib/Animated.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule Animated
10 | */
11 | 'use strict';
12 |
13 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
14 |
15 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
16 |
17 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
20 |
21 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
22 |
23 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
24 |
25 | var _Easing = require('./Easing');
26 |
27 | var _Easing2 = _interopRequireDefault(_Easing);
28 |
29 | var _interpolation = require('./interpolation');
30 |
31 | var _interpolation2 = _interopRequireDefault(_interpolation);
32 |
33 | var _react = require('react');
34 |
35 | var _react2 = _interopRequireDefault(_react);
36 |
37 | // var Animated = (function() {
38 |
39 | // Note(vjeux): this would be better as an interface but flow doesn't
40 | // support them yet
41 |
42 | var Animated = (function () {
43 | function Animated() {
44 | _classCallCheck(this, Animated);
45 | }
46 |
47 | // Important note: start() and stop() will only be called at most once.
48 | // Once an animation has been stopped or finished its course, it will
49 | // not be reused.
50 |
51 | _createClass(Animated, [{
52 | key: 'attach',
53 | value: function attach() {}
54 | }, {
55 | key: 'detach',
56 | value: function detach() {}
57 | }, {
58 | key: 'getValue',
59 | value: function getValue() {}
60 | }, {
61 | key: 'getAnimatedValue',
62 | value: function getAnimatedValue() {
63 | return this.getValue();
64 | }
65 | }, {
66 | key: 'addChild',
67 | value: function addChild(child) {}
68 | }, {
69 | key: 'removeChild',
70 | value: function removeChild(child) {}
71 | }, {
72 | key: 'getChildren',
73 | value: function getChildren() {
74 | return [];
75 | }
76 | }]);
77 |
78 | return Animated;
79 | })();
80 |
81 | var Animation = (function () {
82 | function Animation() {
83 | _classCallCheck(this, Animation);
84 | }
85 |
86 | _createClass(Animation, [{
87 | key: 'start',
88 | value: function start(fromValue, onUpdate, onEnd, previousAnimation) {}
89 | }, {
90 | key: 'stop',
91 | value: function stop() {}
92 | }]);
93 |
94 | return Animation;
95 | })();
96 |
97 | var AnimatedWithChildren = (function (_Animated) {
98 | _inherits(AnimatedWithChildren, _Animated);
99 |
100 | function AnimatedWithChildren() {
101 | _classCallCheck(this, AnimatedWithChildren);
102 |
103 | _get(Object.getPrototypeOf(AnimatedWithChildren.prototype), 'constructor', this).call(this);
104 | this._children = [];
105 | }
106 |
107 | /**
108 | * Animated works by building a directed acyclic graph of dependencies
109 | * transparently when you render your Animated components.
110 | *
111 | * new Animated.Value(0)
112 | * .interpolate() .interpolate() new Animated.Value(1)
113 | * opacity translateY scale
114 | * style transform
115 | * View#234 style
116 | * View#123
117 | *
118 | * A) Top Down phase
119 | * When an Animated.Value is updated, we recursively go down through this
120 | * graph in order to find leaf nodes: the views that we flag as needing
121 | * an update.
122 | *
123 | * B) Bottom Up phase
124 | * When a view is flagged as needing an update, we recursively go back up
125 | * in order to build the new value that it needs. The reason why we need
126 | * this two-phases process is to deal with composite props such as
127 | * transform which can receive values from multiple parents.
128 | */
129 |
130 | _createClass(AnimatedWithChildren, [{
131 | key: 'addChild',
132 | value: function addChild(child) {
133 | if (this._children.length === 0) {
134 | this.attach();
135 | }
136 | this._children.push(child);
137 | }
138 | }, {
139 | key: 'removeChild',
140 | value: function removeChild(child) {
141 | var index = this._children.indexOf(child);
142 | if (index === -1) {
143 | console.warn('Trying to remove a child that doesn\'t exist');
144 | return;
145 | }
146 | this._children.splice(index, 1);
147 | if (this._children.length === 0) {
148 | this.detach();
149 | }
150 | }
151 | }, {
152 | key: 'getChildren',
153 | value: function getChildren() {
154 | return this._children;
155 | }
156 | }]);
157 |
158 | return AnimatedWithChildren;
159 | })(Animated);
160 |
161 | function _flush(node) {
162 | var animatedStyles = new Set();
163 | function findAnimatedStyles(theNode) {
164 | if ('update' in theNode) {
165 | animatedStyles.add(theNode);
166 | } else {
167 | theNode.getChildren().forEach(findAnimatedStyles);
168 | }
169 | }
170 | findAnimatedStyles(node);
171 | animatedStyles.forEach(function (animatedStyle) {
172 | return animatedStyle.update();
173 | });
174 | }
175 |
176 | var TimingAnimation = (function (_Animation) {
177 | _inherits(TimingAnimation, _Animation);
178 |
179 | function TimingAnimation(config) {
180 | _classCallCheck(this, TimingAnimation);
181 |
182 | _get(Object.getPrototypeOf(TimingAnimation.prototype), 'constructor', this).call(this);
183 | this._toValue = config.toValue;
184 | this._easing = config.easing || _Easing2['default'].inOut(_Easing2['default'].ease);
185 | this._duration = config.duration !== undefined ? config.duration : 500;
186 | this._delay = config.delay || 0;
187 | }
188 |
189 | _createClass(TimingAnimation, [{
190 | key: 'start',
191 | value: function start(fromValue, onUpdate, onEnd) {
192 | var _this = this;
193 |
194 | this._fromValue = fromValue;
195 | this._onUpdate = onUpdate;
196 | this._onEnd = onEnd;
197 |
198 | var start = function start() {
199 | _this._startTime = Date.now();
200 | _this._animationFrame = window.requestAnimationFrame(_this.onUpdate.bind(_this));
201 | };
202 | if (this._delay) {
203 | this._timeout = setTimeout(start, this._delay);
204 | } else {
205 | start();
206 | }
207 | }
208 | }, {
209 | key: 'onUpdate',
210 | value: function onUpdate() {
211 | var now = Date.now();
212 |
213 | if (now > this._startTime + this._duration) {
214 | this._onUpdate(this._fromValue + this._easing(1) * (this._toValue - this._fromValue));
215 | var onEnd = this._onEnd;
216 | this._onEnd = null;
217 | onEnd && onEnd( /* finished */true);
218 | return;
219 | }
220 |
221 | this._onUpdate(this._fromValue + this._easing((now - this._startTime) / this._duration) * (this._toValue - this._fromValue));
222 |
223 | this._animationFrame = window.requestAnimationFrame(this.onUpdate.bind(this));
224 | }
225 | }, {
226 | key: 'stop',
227 | value: function stop() {
228 | clearTimeout(this._timeout);
229 | window.cancelAnimationFrame(this._animationFrame);
230 | var onEnd = this._onEnd;
231 | this._onEnd = null;
232 | onEnd && onEnd( /* finished */false);
233 | }
234 | }]);
235 |
236 | return TimingAnimation;
237 | })(Animation);
238 |
239 | var DecayAnimation = (function (_Animation2) {
240 | _inherits(DecayAnimation, _Animation2);
241 |
242 | function DecayAnimation(config) {
243 | _classCallCheck(this, DecayAnimation);
244 |
245 | _get(Object.getPrototypeOf(DecayAnimation.prototype), 'constructor', this).call(this);
246 | this._deceleration = config.deceleration || 0.998;
247 | this._velocity = config.velocity;
248 | }
249 |
250 | _createClass(DecayAnimation, [{
251 | key: 'start',
252 | value: function start(fromValue, onUpdate, onEnd) {
253 | this._lastValue = fromValue;
254 | this._fromValue = fromValue;
255 | this._onUpdate = onUpdate;
256 | this._onEnd = onEnd;
257 | this._startTime = Date.now();
258 | this._animationFrame = window.requestAnimationFrame(this.onUpdate.bind(this));
259 | }
260 | }, {
261 | key: 'onUpdate',
262 | value: function onUpdate() {
263 | var now = Date.now();
264 |
265 | var value = this._fromValue + this._velocity / (1 - this._deceleration) * (1 - Math.exp(-(1 - this._deceleration) * (now - this._startTime)));
266 |
267 | this._onUpdate(value);
268 |
269 | if (Math.abs(this._lastValue - value) < 0.1) {
270 | var onEnd = this._onEnd;
271 | this._onEnd = null;
272 | onEnd && onEnd( /* finished */true);
273 | return;
274 | }
275 |
276 | this._lastValue = value;
277 | this._animationFrame = window.requestAnimationFrame(this.onUpdate.bind(this));
278 | }
279 | }, {
280 | key: 'stop',
281 | value: function stop() {
282 | window.cancelAnimationFrame(this._animationFrame);
283 | var onEnd = this._onEnd;
284 | this._onEnd = null;
285 | onEnd && onEnd( /* finished */false);
286 | }
287 | }]);
288 |
289 | return DecayAnimation;
290 | })(Animation);
291 |
292 | function withDefault(value, defaultValue) {
293 | if (value === undefined || value === null) {
294 | return defaultValue;
295 | }
296 | return value;
297 | }
298 |
299 | function tensionFromOrigamiValue(oValue) {
300 | return (oValue - 30.0) * 3.62 + 194.0;
301 | }
302 | function frictionFromOrigamiValue(oValue) {
303 | return (oValue - 8.0) * 3.0 + 25.0;
304 | }
305 |
306 | var fromOrigamiTensionAndFriction = function fromOrigamiTensionAndFriction(tension, friction) {
307 | return {
308 | tension: tensionFromOrigamiValue(tension),
309 | friction: frictionFromOrigamiValue(friction)
310 | };
311 | };
312 |
313 | var fromBouncinessAndSpeed = function fromBouncinessAndSpeed(bounciness, speed) {
314 | function normalize(value, startValue, endValue) {
315 | return (value - startValue) / (endValue - startValue);
316 | }
317 | function projectNormal(n, start, end) {
318 | return start + n * (end - start);
319 | }
320 | function linearInterpolation(t, start, end) {
321 | return t * end + (1.0 - t) * start;
322 | }
323 | function quadraticOutInterpolation(t, start, end) {
324 | return linearInterpolation(2 * t - t * t, start, end);
325 | }
326 | function b3Friction1(x) {
327 | return 0.0007 * Math.pow(x, 3) - 0.031 * Math.pow(x, 2) + 0.64 * x + 1.28;
328 | }
329 | function b3Friction2(x) {
330 | return 0.000044 * Math.pow(x, 3) - 0.006 * Math.pow(x, 2) + 0.36 * x + 2.;
331 | }
332 | function b3Friction3(x) {
333 | return 0.00000045 * Math.pow(x, 3) - 0.000332 * Math.pow(x, 2) + 0.1078 * x + 5.84;
334 | }
335 | function b3Nobounce(tension) {
336 | if (tension <= 18) {
337 | return b3Friction1(tension);
338 | } else if (tension > 18 && tension <= 44) {
339 | return b3Friction2(tension);
340 | } else {
341 | return b3Friction3(tension);
342 | }
343 | }
344 |
345 | var b = normalize(bounciness / 1.7, 0, 20.0);
346 | b = projectNormal(b, 0.0, 0.8);
347 | var s = normalize(speed / 1.7, 0, 20.0);
348 | var bouncyTension = projectNormal(s, 0.5, 200);
349 | var bouncyFriction = quadraticOutInterpolation(b, b3Nobounce(bouncyTension), 0.01);
350 |
351 | return {
352 | tension: tensionFromOrigamiValue(bouncyTension),
353 | friction: frictionFromOrigamiValue(bouncyFriction)
354 | };
355 | };
356 |
357 | var SpringAnimation = (function (_Animation3) {
358 | _inherits(SpringAnimation, _Animation3);
359 |
360 | function SpringAnimation(config) {
361 | _classCallCheck(this, SpringAnimation);
362 |
363 | _get(Object.getPrototypeOf(SpringAnimation.prototype), 'constructor', this).call(this);
364 |
365 | this._overshootClamping = withDefault(config.overshootClamping, false);
366 | this._restDisplacementThreshold = withDefault(config.restDisplacementThreshold, 0.001);
367 | this._restSpeedThreshold = withDefault(config.restSpeedThreshold, 0.001);
368 | this._lastVelocity = withDefault(config.velocity, 0);
369 | this._tempVelocity = this._lastVelocity;
370 | this._toValue = config.toValue;
371 |
372 | var springConfig;
373 | if (config.bounciness !== undefined || config.speed !== undefined) {
374 | invariant(config.tension === undefined && config.friction === undefined, 'You can only define bounciness/speed or tension/friction but not both');
375 | springConfig = fromBouncinessAndSpeed(withDefault(config.bounciness, 8), withDefault(config.speed, 12));
376 | } else {
377 | springConfig = fromOrigamiTensionAndFriction(withDefault(config.tension, 40), withDefault(config.friction, 7));
378 | }
379 | this._tension = springConfig.tension;
380 | this._friction = springConfig.friction;
381 | }
382 |
383 | _createClass(SpringAnimation, [{
384 | key: 'start',
385 | value: function start(fromValue, onUpdate, onEnd, previousAnimation) {
386 | this._active = true;
387 | this._startPosition = fromValue;
388 | this._lastPosition = this._startPosition;
389 | this._tempPosition = this._lastPosition;
390 |
391 | this._onUpdate = onUpdate;
392 | this._onEnd = onEnd;
393 | this._lastTime = Date.now();
394 |
395 | if (previousAnimation instanceof SpringAnimation) {
396 | var internalState = previousAnimation.getInternalState();
397 | this._lastPosition = internalState.lastPosition;
398 | this._tempPosition = internalState.tempPosition;
399 | this._lastVelocity = internalState.lastVelocity;
400 | this._tempVelocity = internalState.tempVelocity;
401 | this._lastTime = internalState.lastTime;
402 | }
403 |
404 | this.onUpdate();
405 | }
406 | }, {
407 | key: 'getInternalState',
408 | value: function getInternalState() {
409 | return {
410 | lastPosition: this._lastPosition,
411 | tempPosition: this._tempPosition,
412 | lastVelocity: this._lastVelocity,
413 | tempVelocity: this._tempVelocity,
414 | lastTime: this._lastTime
415 | };
416 | }
417 | }, {
418 | key: 'onUpdate',
419 | value: function onUpdate() {
420 | if (!this._active) {
421 | return;
422 | }
423 | var now = Date.now();
424 |
425 | var position = this._lastPosition;
426 | var velocity = this._lastVelocity;
427 |
428 | var tempPosition = position;
429 | var tempVelocity = velocity;
430 |
431 | var TIMESTEP_MSEC = 4;
432 | var numSteps = Math.floor((now - this._lastTime) / TIMESTEP_MSEC);
433 | for (var i = 0; i < numSteps; ++i) {
434 | // Velocity is based on seconds instead of milliseconds
435 | var step = TIMESTEP_MSEC / 1000;
436 |
437 | var aVelocity = velocity;
438 | var aAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
439 | tempPosition = position + aVelocity * step / 2;
440 | tempVelocity = velocity + aAcceleration * step / 2;
441 |
442 | var bVelocity = tempVelocity;
443 | var bAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
444 | tempPosition = position + bVelocity * step / 2;
445 | tempVelocity = velocity + bAcceleration * step / 2;
446 |
447 | var cVelocity = tempVelocity;
448 | var cAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
449 | tempPosition = position + cVelocity * step;
450 | tempVelocity = velocity + cAcceleration * step;
451 |
452 | var dVelocity = tempVelocity;
453 | var dAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
454 |
455 | var dxdt = (aVelocity + 2 * (bVelocity + cVelocity) + dVelocity) / 6;
456 | var dvdt = (aAcceleration + 2 * (bAcceleration + cAcceleration) + dAcceleration) / 6;
457 |
458 | position += dxdt * step;
459 | velocity += dvdt * step;
460 | }
461 |
462 | this._lastTime = now;
463 | this._tempPosition = tempPosition;
464 | this._tempVelocity = tempVelocity;
465 | this._lastPosition = position;
466 | this._lastVelocity = velocity;
467 |
468 | this._onUpdate(position);
469 |
470 | // Conditions for stopping the spring animation
471 | var isOvershooting = false;
472 | if (this._overshootClamping && this._tension !== 0) {
473 | if (this._startPosition < this._toValue) {
474 | isOvershooting = position > this._toValue;
475 | } else {
476 | isOvershooting = position < this._toValue;
477 | }
478 | }
479 | var isVelocity = Math.abs(velocity) <= this._restSpeedThreshold;
480 | var isDisplacement = true;
481 | if (this._tension !== 0) {
482 | isDisplacement = Math.abs(this._toValue - position) <= this._restDisplacementThreshold;
483 | }
484 | if (isOvershooting || isVelocity && isDisplacement) {
485 | var onEnd = this._onEnd;
486 | this._onEnd = null;
487 | onEnd && onEnd( /* finished */true);
488 | return;
489 | }
490 | this._animationFrame = window.requestAnimationFrame(this.onUpdate.bind(this));
491 | }
492 | }, {
493 | key: 'stop',
494 | value: function stop() {
495 | this._active = false;
496 | window.cancelAnimationFrame(this._animationFrame);
497 | var onEnd = this._onEnd;
498 | this._onEnd = null;
499 | onEnd && onEnd( /* finished */false);
500 | }
501 | }]);
502 |
503 | return SpringAnimation;
504 | })(Animation);
505 |
506 | var _uniqueId = 1;
507 |
508 | var AnimatedValue = (function (_AnimatedWithChildren) {
509 | _inherits(AnimatedValue, _AnimatedWithChildren);
510 |
511 | function AnimatedValue(value) {
512 | _classCallCheck(this, AnimatedValue);
513 |
514 | _get(Object.getPrototypeOf(AnimatedValue.prototype), 'constructor', this).call(this);
515 | this._value = value;
516 | this._offset = 0;
517 | this._animation = null;
518 | this._listeners = {};
519 | }
520 |
521 | _createClass(AnimatedValue, [{
522 | key: 'detach',
523 | value: function detach() {
524 | this.stopAnimation();
525 | }
526 | }, {
527 | key: 'getValue',
528 | value: function getValue() {
529 | return this._value + this._offset;
530 | }
531 | }, {
532 | key: 'setValue',
533 | value: function setValue(value) {
534 | if (this._animation) {
535 | this._animation.stop();
536 | this._animation = null;
537 | }
538 | this._updateValue(value);
539 | }
540 | }, {
541 | key: 'getOffset',
542 | value: function getOffset() {
543 | return this._offset;
544 | }
545 | }, {
546 | key: 'setOffset',
547 | value: function setOffset(offset) {
548 | this._offset = offset;
549 | }
550 | }, {
551 | key: 'addListener',
552 | value: function addListener(callback) {
553 | var id = _uniqueId++;
554 | this._listeners[id] = callback;
555 | return id;
556 | }
557 | }, {
558 | key: 'removeListener',
559 | value: function removeListener(id) {
560 | delete this._listeners[id];
561 | }
562 | }, {
563 | key: 'animate',
564 | value: function animate(animation, callback) {
565 | var _this2 = this;
566 |
567 | var previousAnimation = this._animation;
568 | this._animation && this._animation.stop();
569 | this._animation = animation;
570 | animation.start(this._value, function (value) {
571 | _this2._updateValue(value);
572 | }, function (finished) {
573 | _this2._animation = null;
574 | callback && callback(finished);
575 | }, previousAnimation);
576 | }
577 | }, {
578 | key: 'stopAnimation',
579 | value: function stopAnimation(callback) {
580 | this.stopTracking();
581 | this._animation && this._animation.stop();
582 | callback && callback(this._value);
583 | }
584 | }, {
585 | key: 'stopTracking',
586 | value: function stopTracking() {
587 | this._tracking && this._tracking.detach();
588 | }
589 | }, {
590 | key: 'track',
591 | value: function track(tracking) {
592 | this.stopTracking();
593 | this._tracking = tracking;
594 | }
595 | }, {
596 | key: 'interpolate',
597 | value: function interpolate(config) {
598 | return new AnimatedInterpolation(this, _interpolation2['default'].create(config));
599 | }
600 | }, {
601 | key: '_updateValue',
602 | value: function _updateValue(value) {
603 | if (value === this._value) {
604 | return;
605 | }
606 | this._value = value;
607 | _flush(this);
608 | for (var key in this._listeners) {
609 | this._listeners[key]({ value: this.getValue() });
610 | }
611 | }
612 | }]);
613 |
614 | return AnimatedValue;
615 | })(AnimatedWithChildren);
616 |
617 | var AnimatedVec2 = (function (_AnimatedWithChildren2) {
618 | _inherits(AnimatedVec2, _AnimatedWithChildren2);
619 |
620 | function AnimatedVec2(value) {
621 | _classCallCheck(this, AnimatedVec2);
622 |
623 | _get(Object.getPrototypeOf(AnimatedVec2.prototype), 'constructor', this).call(this);
624 | value = value || { x: 0, y: 0 };
625 | if (typeof value.x === 'number') {
626 | this.x = new AnimatedValue(value.x);
627 | this.y = new AnimatedValue(value.y);
628 | } else {
629 | this.x = value.x;
630 | this.y = value.y;
631 | }
632 | this._listeners = {};
633 | }
634 |
635 | _createClass(AnimatedVec2, [{
636 | key: 'setValue',
637 | value: function setValue(value) {
638 | this.x.setValue(value.x);
639 | this.y.setValue(value.y);
640 | }
641 | }, {
642 | key: 'setOffset',
643 | value: function setOffset(offset) {
644 | this.x.setOffset(offset.x);
645 | this.y.setOffset(offset.y);
646 | }
647 | }, {
648 | key: 'addListener',
649 | value: function addListener(callback) {
650 | var _this3 = this;
651 |
652 | var id = _uniqueId++;
653 | var jointCallback = function jointCallback(value) {
654 | callback({ x: _this3.x.getValue(), y: _this3.y.getValue() });
655 | };
656 | this._listeners[id] = {
657 | x: this.x.addListener(jointCallback),
658 | y: this.y.addListener(jointCallback)
659 | };
660 | return id;
661 | }
662 | }, {
663 | key: 'removeListener',
664 | value: function removeListener(id) {
665 | this.x.removeListener(this._listeners[id].x);
666 | this.y.removeListener(this._listeners[id].y);
667 | delete this._listeners[id];
668 | }
669 | }, {
670 | key: 'offset',
671 | value: function offset(theOffset) {
672 | // chunky...perf?
673 | return new AnimatedVec2({
674 | x: this.x.interpolate({
675 | inputRange: [0, 1],
676 | outputRange: [theOffset.x, theOffset.x + 1]
677 | }),
678 | y: this.y.interpolate({
679 | inputRange: [0, 1],
680 | outputRange: [theOffset.y, theOffset.y + 1]
681 | })
682 | });
683 | }
684 | }, {
685 | key: 'getLayout',
686 | value: function getLayout() {
687 | return {
688 | left: this.x,
689 | top: this.y
690 | };
691 | }
692 | }, {
693 | key: 'getTranslateTransform',
694 | value: function getTranslateTransform() {
695 | return [{ translateX: this.x }, { translateY: this.y }];
696 | }
697 | }]);
698 |
699 | return AnimatedVec2;
700 | })(AnimatedWithChildren);
701 |
702 | var AnimatedInterpolation = (function (_AnimatedWithChildren3) {
703 | _inherits(AnimatedInterpolation, _AnimatedWithChildren3);
704 |
705 | function AnimatedInterpolation(parent, interpolation) {
706 | _classCallCheck(this, AnimatedInterpolation);
707 |
708 | _get(Object.getPrototypeOf(AnimatedInterpolation.prototype), 'constructor', this).call(this);
709 | this._parent = parent;
710 | this._interpolation = interpolation;
711 | }
712 |
713 | _createClass(AnimatedInterpolation, [{
714 | key: 'getValue',
715 | value: function getValue() {
716 | var parentValue = this._parent.getValue();
717 | invariant(typeof parentValue === 'number', 'Cannot interpolate an input which is not a number.');
718 | return this._interpolation(parentValue);
719 | }
720 | }, {
721 | key: 'interpolate',
722 | value: function interpolate(config) {
723 | return new AnimatedInterpolation(this, _interpolation2['default'].create(config));
724 | }
725 | }, {
726 | key: 'attach',
727 | value: function attach() {
728 | this._parent.addChild(this);
729 | }
730 | }, {
731 | key: 'detach',
732 | value: function detach() {
733 | this._parent.removeChild(this);
734 | }
735 | }]);
736 |
737 | return AnimatedInterpolation;
738 | })(AnimatedWithChildren);
739 |
740 | var AnimatedTransform = (function (_AnimatedWithChildren4) {
741 | _inherits(AnimatedTransform, _AnimatedWithChildren4);
742 |
743 | function AnimatedTransform(transforms) {
744 | _classCallCheck(this, AnimatedTransform);
745 |
746 | _get(Object.getPrototypeOf(AnimatedTransform.prototype), 'constructor', this).call(this);
747 | this._transforms = transforms;
748 | }
749 |
750 | _createClass(AnimatedTransform, [{
751 | key: 'getValue',
752 | value: function getValue() {
753 | return this._transforms.map(function (transform) {
754 | var result = '';
755 | for (var key in transform) {
756 | var value = transform[key];
757 | if (value instanceof Animated) {
758 | result += key + '(' + value.getValue() + ')';
759 | } else {
760 | result += key + '(' + value.join(',') + ')';
761 | }
762 | }
763 | return result;
764 | }).join(' ');
765 | }
766 | }, {
767 | key: 'getAnimatedValue',
768 | value: function getAnimatedValue() {
769 | return this._transforms.map(function (transform) {
770 | var result = '';
771 | for (var key in transform) {
772 | var value = transform[key];
773 | if (value instanceof Animated) {
774 | result += key + '(' + value.getValue() + ') ';
775 | } else {
776 | // All transform components needed to recompose matrix
777 | result += key + '(' + value.join(',') + ') ';
778 | }
779 | }
780 | return result;
781 | }).join('').trim();
782 | }
783 | }, {
784 | key: 'attach',
785 | value: function attach() {
786 | var _this4 = this;
787 |
788 | this._transforms.forEach(function (transform) {
789 | for (var key in transform) {
790 | var value = transform[key];
791 | if (value instanceof Animated) {
792 | value.addChild(_this4);
793 | }
794 | }
795 | });
796 | }
797 | }, {
798 | key: 'detach',
799 | value: function detach() {
800 | var _this5 = this;
801 |
802 | this._transforms.forEach(function (transform) {
803 | for (var key in transform) {
804 | var value = transform[key];
805 | if (value instanceof Animated) {
806 | value.removeChild(_this5);
807 | }
808 | }
809 | });
810 | }
811 | }]);
812 |
813 | return AnimatedTransform;
814 | })(AnimatedWithChildren);
815 |
816 | var AnimatedStyle = (function (_AnimatedWithChildren5) {
817 | _inherits(AnimatedStyle, _AnimatedWithChildren5);
818 |
819 | function AnimatedStyle(style) {
820 | _classCallCheck(this, AnimatedStyle);
821 |
822 | _get(Object.getPrototypeOf(AnimatedStyle.prototype), 'constructor', this).call(this);
823 | style = style || {};
824 | if (style.transform) {
825 | style = _extends({}, style, {
826 | transform: new AnimatedTransform(style.transform)
827 | });
828 | }
829 | this._style = style;
830 | }
831 |
832 | _createClass(AnimatedStyle, [{
833 | key: 'getValue',
834 | value: function getValue() {
835 | var style = {};
836 | for (var key in this._style) {
837 | var value = this._style[key];
838 | if (value instanceof Animated) {
839 | style[key] = value.getValue();
840 | } else {
841 | style[key] = value;
842 | }
843 | }
844 | return style;
845 | }
846 | }, {
847 | key: 'getAnimatedValue',
848 | value: function getAnimatedValue() {
849 | var style = {};
850 | for (var key in this._style) {
851 | var value = this._style[key];
852 | if (value instanceof Animated) {
853 | style[key] = value.getAnimatedValue();
854 | }
855 | }
856 | return style;
857 | }
858 | }, {
859 | key: 'attach',
860 | value: function attach() {
861 | for (var key in this._style) {
862 | var value = this._style[key];
863 | if (value instanceof Animated) {
864 | value.addChild(this);
865 | }
866 | }
867 | }
868 | }, {
869 | key: 'detach',
870 | value: function detach() {
871 | for (var key in this._style) {
872 | var value = this._style[key];
873 | if (value instanceof Animated) {
874 | value.removeChild(this);
875 | }
876 | }
877 | }
878 | }]);
879 |
880 | return AnimatedStyle;
881 | })(AnimatedWithChildren);
882 |
883 | var AnimatedProps = (function (_Animated2) {
884 | _inherits(AnimatedProps, _Animated2);
885 |
886 | function AnimatedProps(props, callback) {
887 | _classCallCheck(this, AnimatedProps);
888 |
889 | _get(Object.getPrototypeOf(AnimatedProps.prototype), 'constructor', this).call(this);
890 | if (props.style) {
891 | props = _extends({}, props, {
892 | style: new AnimatedStyle(props.style)
893 | });
894 | }
895 | this._props = props;
896 | this._callback = callback;
897 | this.attach();
898 | }
899 |
900 | _createClass(AnimatedProps, [{
901 | key: 'getValue',
902 | value: function getValue() {
903 | var props = {};
904 | for (var key in this._props) {
905 | var value = this._props[key];
906 | if (value instanceof Animated) {
907 | props[key] = value.getValue();
908 | } else {
909 | props[key] = value;
910 | }
911 | }
912 | return props;
913 | }
914 | }, {
915 | key: 'getAnimatedValue',
916 | value: function getAnimatedValue() {
917 | var props = {};
918 | for (var key in this._props) {
919 | var value = this._props[key];
920 | if (value instanceof Animated) {
921 | props[key] = value.getAnimatedValue();
922 | }
923 | }
924 | return props;
925 | }
926 | }, {
927 | key: 'attach',
928 | value: function attach() {
929 | for (var key in this._props) {
930 | var value = this._props[key];
931 | if (value instanceof Animated) {
932 | value.addChild(this);
933 | }
934 | }
935 | }
936 | }, {
937 | key: 'detach',
938 | value: function detach() {
939 | for (var key in this._props) {
940 | var value = this._props[key];
941 | if (value instanceof Animated) {
942 | value.removeChild(this);
943 | }
944 | }
945 | }
946 | }, {
947 | key: 'update',
948 | value: function update() {
949 | this._callback();
950 | }
951 | }]);
952 |
953 | return AnimatedProps;
954 | })(Animated);
955 |
956 | function createAnimatedComponent(Component) {
957 | var refName = 'node';
958 |
959 | var AnimatedComponent = (function (_React$Component) {
960 | _inherits(AnimatedComponent, _React$Component);
961 |
962 | function AnimatedComponent() {
963 | _classCallCheck(this, AnimatedComponent);
964 |
965 | _get(Object.getPrototypeOf(AnimatedComponent.prototype), 'constructor', this).apply(this, arguments);
966 | }
967 |
968 | _createClass(AnimatedComponent, [{
969 | key: 'componentWillUnmount',
970 | value: function componentWillUnmount() {
971 | this._propsAnimated && this._propsAnimated.detach();
972 | }
973 | }, {
974 | key: 'setNativeProps',
975 | value: function setNativeProps(props) {
976 | this.refs[refName].setNativeProps(props);
977 | }
978 | }, {
979 | key: 'componentWillMount',
980 | value: function componentWillMount() {
981 | this.attachProps(this.props);
982 | }
983 | }, {
984 | key: 'attachProps',
985 | value: function attachProps(nextProps) {
986 | var _this6 = this;
987 |
988 | var oldPropsAnimated = this._propsAnimated;
989 |
990 | // The system is best designed when setNativeProps is implemented. It is
991 | // able to avoid re-rendering and directly set the attributes that
992 | // changed. However, setNativeProps can only be implemented on leaf
993 | // native components. If you want to animate a composite component, you
994 | // need to re-render it. In this case, we have a fallback that uses
995 | // forceUpdate.
996 | var callback = function callback() {
997 | if (_this6.refs[refName].setNativeProps) {
998 | var value = _this6._propsAnimated.getAnimatedValue();
999 | _this6.refs[refName].setNativeProps(value);
1000 | } else {
1001 | _this6.forceUpdate();
1002 | }
1003 | };
1004 |
1005 | this._propsAnimated = new AnimatedProps(nextProps, callback);
1006 |
1007 | // When you call detach, it removes the element from the parent list
1008 | // of children. If it goes to 0, then the parent also detaches itself
1009 | // and so on.
1010 | // An optimization is to attach the new elements and THEN detach the old
1011 | // ones instead of detaching and THEN attaching.
1012 | // This way the intermediate state isn't to go to 0 and trigger
1013 | // this expensive recursive detaching to then re-attach everything on
1014 | // the very next operation.
1015 | oldPropsAnimated && oldPropsAnimated.detach();
1016 | }
1017 | }, {
1018 | key: 'componentWillReceiveProps',
1019 | value: function componentWillReceiveProps(nextProps) {
1020 | this.attachProps(nextProps);
1021 | }
1022 | }, {
1023 | key: 'render',
1024 | value: function render() {
1025 | return _react2['default'].createElement(Component, _extends({}, this._propsAnimated.getValue(), {
1026 | ref: refName
1027 | }));
1028 | }
1029 | }]);
1030 |
1031 | return AnimatedComponent;
1032 | })(_react2['default'].Component);
1033 |
1034 | return AnimatedComponent;
1035 | }
1036 |
1037 | var AnimatedTracking = (function (_Animated3) {
1038 | _inherits(AnimatedTracking, _Animated3);
1039 |
1040 | function AnimatedTracking(value, parent, animationClass, animationConfig, callback) {
1041 | _classCallCheck(this, AnimatedTracking);
1042 |
1043 | _get(Object.getPrototypeOf(AnimatedTracking.prototype), 'constructor', this).call(this);
1044 | this._value = value;
1045 | this._parent = parent;
1046 | this._animationClass = animationClass;
1047 | this._animationConfig = animationConfig;
1048 | this._callback = callback;
1049 | this.attach();
1050 | }
1051 |
1052 | _createClass(AnimatedTracking, [{
1053 | key: 'getValue',
1054 | value: function getValue() {
1055 | return this._parent.getValue();
1056 | }
1057 | }, {
1058 | key: 'attach',
1059 | value: function attach() {
1060 | this._active = true;
1061 | this._parent.addChild(this);
1062 | }
1063 | }, {
1064 | key: 'detach',
1065 | value: function detach() {
1066 | this._parent.removeChild(this);
1067 | this._active = false;
1068 | }
1069 | }, {
1070 | key: 'update',
1071 | value: function update() {
1072 | if (!this._active) {
1073 | console.warn('calling update on detached AnimatedTracking');
1074 | return;
1075 | }
1076 | // console.log('AnimatedTracking update with ',
1077 | // {toValue: this._animationConfig.toValue.getValue(), value: this._value.getValue()});
1078 | this._value.animate(new this._animationClass(_extends({}, this._animationConfig, {
1079 | toValue: this._animationConfig.toValue.getValue()
1080 | })), this._callback);
1081 | }
1082 | }]);
1083 |
1084 | return AnimatedTracking;
1085 | })(Animated);
1086 |
1087 | var maybeVectorAnim = function maybeVectorAnim(value, config, anim) {
1088 | if (value instanceof AnimatedVec2) {
1089 | var configX = _extends({}, config);
1090 | var configY = _extends({}, config);
1091 | for (var key in config) {
1092 | var _config$key = config[key];
1093 | var x = _config$key.x;
1094 | var y = _config$key.y;
1095 |
1096 | if (x !== undefined && y !== undefined) {
1097 | configX[key] = x;
1098 | configY[key] = y;
1099 | }
1100 | }
1101 | // TODO: Urg, parallel breaks tracking :(
1102 | // return parallel([
1103 | // anim(value.x, configX),
1104 | // anim(value.y, configY),
1105 | // ]);
1106 | anim(value.x, configX).start();
1107 | return anim(value.y, configY);
1108 | }
1109 | return null;
1110 | };
1111 |
1112 | var spring = function spring(value, config) {
1113 | return maybeVectorAnim(value, config, spring) || {
1114 | start: function start(callback) {
1115 | value.stopTracking();
1116 | if (config.toValue instanceof Animated) {
1117 | value.track(new AnimatedTracking(value, config.toValue, SpringAnimation, config, callback));
1118 | } else {
1119 | value.animate(new SpringAnimation(config), callback);
1120 | }
1121 | },
1122 |
1123 | stop: function stop() {
1124 | value.stopAnimation();
1125 | }
1126 | };
1127 | };
1128 |
1129 | var timing = function timing(value, config) {
1130 | return maybeVectorAnim(value, config, timing) || {
1131 | start: function start(callback) {
1132 | value.stopTracking();
1133 | value.animate(new TimingAnimation(config), callback);
1134 | },
1135 |
1136 | stop: function stop() {
1137 | value.stopAnimation();
1138 | }
1139 | };
1140 | };
1141 |
1142 | var decay = function decay(value, config) {
1143 | return maybeVectorAnim(value, config, decay) || {
1144 | start: function start(callback) {
1145 | value.stopTracking();
1146 | value.animate(new DecayAnimation(config), callback);
1147 | },
1148 |
1149 | stop: function stop() {
1150 | value.stopAnimation();
1151 | }
1152 | };
1153 | };
1154 |
1155 | var sequence = function sequence(animations) {
1156 | var current = 0;
1157 | return {
1158 | start: function start(callback) {
1159 | var onComplete = function onComplete(finished) {
1160 | if (!finished) {
1161 | callback && callback(finished);
1162 | return;
1163 | }
1164 |
1165 | current++;
1166 |
1167 | if (current === animations.length) {
1168 | callback && callback( /* finished */true);
1169 | return;
1170 | }
1171 |
1172 | animations[current].start(onComplete);
1173 | };
1174 |
1175 | if (animations.length === 0) {
1176 | callback && callback( /* finished */true);
1177 | } else {
1178 | animations[current].start(onComplete);
1179 | }
1180 | },
1181 |
1182 | stop: function stop() {
1183 | if (current < animations.length) {
1184 | animations[current].stop();
1185 | }
1186 | }
1187 | };
1188 | };
1189 |
1190 | var parallel = function parallel(animations) {
1191 | var doneCount = 0;
1192 | // Variable to make sure we only call stop() at most once
1193 | var hasBeenStopped = false;
1194 |
1195 | var result = {
1196 | start: function start(callback) {
1197 | if (doneCount === animations.length) {
1198 | callback && callback( /* finished */true);
1199 | return;
1200 | }
1201 |
1202 | animations.forEach(function (animation, idx) {
1203 | animation.start(function (finished) {
1204 | doneCount++;
1205 | if (doneCount === animations.length) {
1206 | callback && callback(finished);
1207 | return;
1208 | }
1209 |
1210 | if (!finished && !hasBeenStopped) {
1211 | result.stop();
1212 | }
1213 | });
1214 | });
1215 | },
1216 |
1217 | stop: function stop() {
1218 | hasBeenStopped = true;
1219 | animations.forEach(function (animation) {
1220 | animation.stop();
1221 | });
1222 | }
1223 | };
1224 |
1225 | return result;
1226 | };
1227 |
1228 | var delay = function delay(time) {
1229 | // Would be nice to make a specialized implementation.
1230 | return timing(new AnimatedValue(0), { toValue: 0, delay: time, duration: 0 });
1231 | };
1232 |
1233 | var stagger = function stagger(time, animations) {
1234 | return parallel(animations.map(function (animation, i) {
1235 | return sequence([delay(time * i), animation]);
1236 | }));
1237 | };
1238 |
1239 | /**
1240 | * Takes an array of mappings and extracts values from each arg accordingly,
1241 | * then calls setValue on the mapped outputs. e.g.
1242 | *
1243 | * onScroll={this.AnimatedEvent(
1244 | * [{nativeEvent: {contentOffset: {x: this._scrollX}}}]
1245 | * {listener, updatePeriod: 100} // optional listener invoked every 100ms
1246 | * )
1247 | * ...
1248 | * onPanResponderMove: this.AnimatedEvent([
1249 | * null, // raw event arg
1250 | * {dx: this._panX}, // gestureState arg
1251 | * ]),
1252 | *
1253 | */
1254 | var event = function event(argMapping, config) {
1255 | var lastUpdate = 0;
1256 | var timer;
1257 | var isEnabled = true;
1258 | if (config && config.ref) {
1259 | config.ref({
1260 | enable: function enable() {
1261 | isEnabled = true;
1262 | },
1263 | disable: function disable() {
1264 | isEnabled = false;
1265 | clearTimeout(timer);
1266 | timer = null;
1267 | }
1268 | });
1269 | }
1270 | var lastArgs;
1271 | return function () {
1272 | lastArgs = arguments;
1273 | if (!isEnabled) {
1274 | clearTimeout(timer);
1275 | timer = null;
1276 | return;
1277 | }
1278 | var traverse = function traverse(recMapping, recEvt, key) {
1279 | if (recMapping instanceof AnimatedValue || recMapping instanceof AnimatedInterpolation) {
1280 | invariant(typeof recEvt === 'number', 'Bad event element of type ' + typeof recEvt + ' for key ' + key);
1281 | recMapping.setValue(recEvt);
1282 | return;
1283 | }
1284 | invariant(typeof recMapping === 'object', 'Bad mapping of type ' + typeof recMapping + ' for key ' + key);
1285 | invariant(typeof recEvt === 'object', 'Bad event of type ' + typeof recEvt + ' for key ' + key);
1286 | for (var key in recMapping) {
1287 | traverse(recMapping[key], recEvt[key], key);
1288 | }
1289 | };
1290 | argMapping.forEach(function (mapping, idx) {
1291 | traverse(mapping, lastArgs[idx], null);
1292 | });
1293 | if (config && config.listener && !timer) {
1294 | var cb = function cb() {
1295 | lastUpdate = Date.now();
1296 | timer = null;
1297 | config.listener.apply(null, lastArgs);
1298 | };
1299 | if (config.updatePeriod) {
1300 | timer = setTimeout(cb, config.updatePeriod - Date.now() + lastUpdate);
1301 | } else {
1302 | cb();
1303 | }
1304 | }
1305 | };
1306 | };
1307 |
1308 | module.exports = {
1309 | delay: delay,
1310 | sequence: sequence,
1311 | parallel: parallel,
1312 | stagger: stagger,
1313 |
1314 | decay: decay,
1315 | timing: timing,
1316 | spring: spring,
1317 |
1318 | event: event,
1319 |
1320 | Value: AnimatedValue,
1321 | Vec2: AnimatedVec2,
1322 | __PropsOnlyForTests: AnimatedProps,
1323 | div: createAnimatedComponent('div'),
1324 | createAnimatedComponent: createAnimatedComponent
1325 | };
1326 |
1327 | // })();
--------------------------------------------------------------------------------
/lib/Easing.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule Easing
10 | */
11 | 'use strict';
12 |
13 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
16 |
17 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
18 |
19 | var _bezier2 = require('./bezier');
20 |
21 | var _bezier3 = _interopRequireDefault(_bezier2);
22 |
23 | /**
24 | * This class implements common easing functions. The math is pretty obscure,
25 | * but this cool website has nice visual illustrations of what they represent:
26 | * http://xaedes.de/dev/transitions/
27 | */
28 |
29 | var Easing = (function () {
30 | function Easing() {
31 | _classCallCheck(this, Easing);
32 | }
33 |
34 | _createClass(Easing, null, [{
35 | key: 'step0',
36 | value: function step0(n) {
37 | return n > 0 ? 1 : 0;
38 | }
39 | }, {
40 | key: 'step1',
41 | value: function step1(n) {
42 | return n >= 1 ? 1 : 0;
43 | }
44 | }, {
45 | key: 'linear',
46 | value: function linear(t) {
47 | return t;
48 | }
49 | }, {
50 | key: 'ease',
51 | value: (function (_ease) {
52 | function ease(_x) {
53 | return _ease.apply(this, arguments);
54 | }
55 |
56 | ease.toString = function () {
57 | return _ease.toString();
58 | };
59 |
60 | return ease;
61 | })(function (t) {
62 | return ease(t);
63 | })
64 | }, {
65 | key: 'quad',
66 | value: function quad(t) {
67 | return t * t;
68 | }
69 | }, {
70 | key: 'cubic',
71 | value: function cubic(t) {
72 | return t * t * t;
73 | }
74 | }, {
75 | key: 'poly',
76 | value: function poly(n) {
77 | return function (t) {
78 | return Math.pow(t, n);
79 | };
80 | }
81 | }, {
82 | key: 'sin',
83 | value: function sin(t) {
84 | return 1 - Math.cos(t * Math.PI / 2);
85 | }
86 | }, {
87 | key: 'circle',
88 | value: function circle(t) {
89 | return 1 - Math.sqrt(1 - t * t);
90 | }
91 | }, {
92 | key: 'exp',
93 | value: function exp(t) {
94 | return Math.pow(2, 10 * (t - 1));
95 | }
96 | }, {
97 | key: 'elastic',
98 | value: function elastic(a, p) {
99 | var tau = Math.PI * 2;
100 | // flow isn't smart enough to figure out that s is always assigned to a
101 | // number before being used in the returned function
102 | var s;
103 | if (arguments.length < 2) {
104 | p = 0.45;
105 | }
106 | if (arguments.length) {
107 | s = p / tau * Math.asin(1 / a);
108 | } else {
109 | a = 1;
110 | s = p / 4;
111 | }
112 | return function (t) {
113 | return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * tau / p);
114 | };
115 | }
116 | }, {
117 | key: 'back',
118 | value: function back(s) {
119 | if (s === undefined) {
120 | s = 1.70158;
121 | }
122 | return function (t) {
123 | return t * t * ((s + 1) * t - s);
124 | };
125 | }
126 | }, {
127 | key: 'bounce',
128 | value: function bounce(t) {
129 | if (t < 1 / 2.75) {
130 | return 7.5625 * t * t;
131 | }
132 |
133 | if (t < 2 / 2.75) {
134 | t -= 1.5 / 2.75;
135 | return 7.5625 * t * t + 0.75;
136 | }
137 |
138 | if (t < 2.5 / 2.75) {
139 | t -= 2.25 / 2.75;
140 | return 7.5625 * t * t + 0.9375;
141 | }
142 |
143 | t -= 2.625 / 2.75;
144 | return 7.5625 * t * t + 0.984375;
145 | }
146 | }, {
147 | key: 'bezier',
148 | value: function bezier(x1, y1, x2, y2, epsilon) {
149 | if (epsilon === undefined) {
150 | // epsilon determines the precision of the solved values
151 | // a good approximation is:
152 | var duration = 500; // duration of animation in milliseconds.
153 | epsilon = 1000 / 60 / duration / 4;
154 | }
155 |
156 | return (0, _bezier3['default'])(x1, y1, x2, y2, epsilon);
157 | }
158 | }, {
159 | key: 'in',
160 | value: function _in(easing) {
161 | return easing;
162 | }
163 | }, {
164 | key: 'out',
165 | value: function out(easing) {
166 | return function (t) {
167 | return 1 - easing(1 - t);
168 | };
169 | }
170 | }, {
171 | key: 'inOut',
172 | value: function inOut(easing) {
173 | return function (t) {
174 | if (t < 0.5) {
175 | return easing(t * 2) / 2;
176 | }
177 | return 1 - easing((1 - t) * 2) / 2;
178 | };
179 | }
180 | }]);
181 |
182 | return Easing;
183 | })();
184 |
185 | var ease = Easing.bezier(0.42, 0, 1, 1);
186 |
187 | module.exports = Easing;
--------------------------------------------------------------------------------
/lib/Interpolation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule Interpolation
10 | *
11 | */
12 | // 'use strict';
13 |
14 | 'use strict';
15 |
16 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
17 |
18 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
19 |
20 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
21 |
22 | var linear = function linear(t) {
23 | return t;
24 | };
25 |
26 | /**
27 | * Very handy helper to map input ranges to output ranges with an easing
28 | * function and custom behavior outside of the ranges.
29 | */
30 |
31 | var Interpolation = (function () {
32 | function Interpolation() {
33 | _classCallCheck(this, Interpolation);
34 | }
35 |
36 | _createClass(Interpolation, null, [{
37 | key: 'create',
38 | value: function create(config) {
39 |
40 | if (config.outputRange && typeof config.outputRange[0] === 'string') {
41 | return createInterpolationFromStringOutputRange(config);
42 | }
43 |
44 | var outputRange = config.outputRange;
45 | checkInfiniteRange('outputRange', outputRange);
46 |
47 | var inputRange = config.inputRange;
48 | checkInfiniteRange('inputRange', inputRange);
49 | checkValidInputRange(inputRange);
50 |
51 | invariant(inputRange.length === outputRange.length, 'inputRange (' + inputRange.length + ') and outputRange (' + outputRange.length + ') must have the same length');
52 |
53 | var easing = config.easing || linear;
54 |
55 | var extrapolateLeft = 'extend';
56 | if (config.extrapolateLeft !== undefined) {
57 | extrapolateLeft = config.extrapolateLeft;
58 | } else if (config.extrapolate !== undefined) {
59 | extrapolateLeft = config.extrapolate;
60 | }
61 |
62 | var extrapolateRight = 'extend';
63 | if (config.extrapolateRight !== undefined) {
64 | extrapolateRight = config.extrapolateRight;
65 | } else if (config.extrapolate !== undefined) {
66 | extrapolateRight = config.extrapolate;
67 | }
68 |
69 | return function (input) {
70 | invariant(typeof input === 'number', 'Cannot interpolation an input which is not a number');
71 |
72 | var range = findRange(input, inputRange);
73 | return interpolate(input, inputRange[range], inputRange[range + 1], outputRange[range], outputRange[range + 1], easing, extrapolateLeft, extrapolateRight);
74 | };
75 | }
76 | }]);
77 |
78 | return Interpolation;
79 | })();
80 |
81 | function interpolate(input, inputMin, inputMax, outputMin, outputMax, easing, extrapolateLeft, extrapolateRight) {
82 | var result = input;
83 |
84 | // Extrapolate
85 | if (result < inputMin) {
86 | if (extrapolateLeft === 'identity') {
87 | return result;
88 | } else if (extrapolateLeft === 'clamp') {
89 | result = inputMin;
90 | } else if (extrapolateLeft === 'extend') {
91 | // noop
92 | }
93 | }
94 |
95 | if (result > inputMax) {
96 | if (extrapolateRight === 'identity') {
97 | return result;
98 | } else if (extrapolateRight === 'clamp') {
99 | result = inputMax;
100 | } else if (extrapolateRight === 'extend') {
101 | // noop
102 | }
103 | }
104 |
105 | if (outputMin === outputMax) {
106 | return outputMin;
107 | }
108 |
109 | if (inputMin === inputMax) {
110 | if (input <= inputMin) {
111 | return outputMin;
112 | }
113 | return outputMax;
114 | }
115 |
116 | // Input Range
117 | if (inputMin === -Infinity) {
118 | result = -result;
119 | } else if (inputMax === Infinity) {
120 | result = result - inputMin;
121 | } else {
122 | result = (result - inputMin) / (inputMax - inputMin);
123 | }
124 |
125 | // Easing
126 | result = easing(result);
127 |
128 | // Output Range
129 | if (outputMin === -Infinity) {
130 | result = -result;
131 | } else if (outputMax === Infinity) {
132 | result = result + outputMin;
133 | } else {
134 | result = result * (outputMax - outputMin) + outputMin;
135 | }
136 |
137 | return result;
138 | }
139 |
140 | var stringShapeRegex = /[0-9\.-]+/g;
141 |
142 | /**
143 | * Supports string shapes by extracting numbers so new values can be computed,
144 | * and recombines those values into new strings of the same shape. Supports
145 | * things like:
146 | *
147 | * rgba(123, 42, 99, 0.36) // colors
148 | * -45deg // values with units
149 | */
150 | function createInterpolationFromStringOutputRange(config) {
151 | var outputRange = config.outputRange;
152 | invariant(outputRange.length >= 2, 'Bad output range');
153 | checkPattern(outputRange);
154 |
155 | // ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.5)']
156 | // ->
157 | // [
158 | // [0, 50],
159 | // [100, 150],
160 | // [200, 250],
161 | // [0, 0.5],
162 | // ]
163 | var outputRanges = outputRange[0].match(stringShapeRegex).map(function () {
164 | return [];
165 | });
166 | outputRange.forEach(function (value) {
167 | value.match(stringShapeRegex).forEach(function (number, i) {
168 | outputRanges[i].push(+number);
169 | });
170 | });
171 |
172 | var interpolations = outputRange[0].match(stringShapeRegex).map(function (value, i) {
173 | return Interpolation.create(_extends({}, config, {
174 | outputRange: outputRanges[i]
175 | }));
176 | });
177 |
178 | return function (input) {
179 | var i = 0;
180 | // 'rgba(0, 100, 200, 0)'
181 | // ->
182 | // 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...'
183 | return outputRange[0].replace(stringShapeRegex, function () {
184 | return String(interpolations[i++](input));
185 | });
186 | };
187 | }
188 |
189 | function checkPattern(arr) {
190 | var pattern = arr[0].replace(stringShapeRegex, '');
191 | for (var i = 1; i < arr.length; ++i) {
192 | invariant(pattern === arr[i].replace(stringShapeRegex, ''), 'invalid pattern ' + arr[0] + ' and ' + arr[i]);
193 | }
194 | }
195 |
196 | function findRange(input, inputRange) {
197 | for (var i = 1; i < inputRange.length - 1; ++i) {
198 | if (inputRange[i] >= input) {
199 | break;
200 | }
201 | }
202 | return i - 1;
203 | }
204 |
205 | function checkValidInputRange(arr) {
206 | invariant(arr.length >= 2, 'inputRange must have at least 2 elements');
207 | for (var i = 1; i < arr.length; ++i) {
208 | invariant(arr[i] >= arr[i - 1],
209 | /* $FlowFixMe(>=0.13.0) - In the addition expression below this comment,
210 | * one or both of the operands may be something that doesn't cleanly
211 | * convert to a string, like undefined, null, and object, etc. If you really
212 | * mean this implicit string conversion, you can do something like
213 | * String(myThing)
214 | */
215 | 'inputRange must be monolithically increasing ' + arr);
216 | }
217 | }
218 |
219 | function checkInfiniteRange(name, arr) {
220 | invariant(arr.length >= 2, name + ' must have at least 2 elements');
221 | invariant(arr.length !== 2 || arr[0] !== -Infinity || arr[1] !== Infinity,
222 | /* $FlowFixMe(>=0.13.0) - In the addition expression below this comment,
223 | * one or both of the operands may be something that doesn't cleanly convert
224 | * to a string, like undefined, null, and object, etc. If you really mean
225 | * this implicit string conversion, you can do something like
226 | * String(myThing)
227 | */
228 | name + 'cannot be ]-infinity;+infinity[ ' + arr);
229 | }
230 |
231 | module.exports = Interpolation;
--------------------------------------------------------------------------------
/lib/bezier.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (x1, y1, x2, y2, epsilon) {
4 | var curveX = function curveX(t) {
5 | var v = 1 - t;
6 | return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t;
7 | };
8 | var curveY = function curveY(t) {
9 | var v = 1 - t;
10 | return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t;
11 | };
12 | var derivativeCurveX = function derivativeCurveX(t) {
13 | var v = 1 - t;
14 | return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (-t * t * t + 2 * v * t) * x2;
15 | };
16 | return function (t) {
17 | var x = t,
18 | t0,
19 | t1,
20 | t2,
21 | x2,
22 | d2,
23 | i;
24 | for (t2 = x, i = 0; i < 8; i++) {
25 | x2 = curveX(t2) - x;
26 | if (Math.abs(x2) < epsilon) return curveY(t2);
27 | d2 = derivativeCurveX(t2);
28 | if (Math.abs(d2) < 1e-6) break;
29 | t2 = t2 - x2 / d2;
30 | }
31 | t0 = 0, t1 = 1, t2 = x;
32 | if (t2 < t0) return curveY(t0);
33 | if (t2 > t1) return curveY(t1);
34 | while (t0 < t1) {
35 | x2 = curveX(t2);
36 | if (Math.abs(x2 - x) < epsilon) return curveY(t2);
37 | if (x > x2) t0 = t2;else t1 = t2;
38 | t2 = (t1 - t0) * .5 + t0;
39 | }
40 | return curveY(t2);
41 | };
42 | };
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('./Animated');
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-animated-web-bootleg",
3 | "version": "1.0.0",
4 | "description": "bootleg of code from http://fooo.fr/~vjeux/fb/animated-docs/ until they release ",
5 | "main": "./lib/index.js",
6 | "scripts": {
7 | "start": "babel-node server.js",
8 | "test": "echo \"Error: no test specified\" && exit 1",
9 | "build": "babel src -d lib"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "babel": "^5.8.23",
15 | "babel-core": "^5.8.23",
16 | "babel-eslint": "^4.1.1",
17 | "babel-loader": "^5.3.2",
18 | "eslint": "^1.3.1",
19 | "react": "^0.13.3",
20 | "react-hot-loader": "^1.3.0",
21 | "webpack": "^1.12.1",
22 | "webpack-dev-server": "^1.10.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack';
2 | import WebpackDevServer from 'webpack-dev-server';
3 | import config from './webpack.config';
4 |
5 | const isHot = !!process.env.HOT;
6 |
7 | new WebpackDevServer(webpack(config), {
8 | publicPath: config.output.publicPath,
9 | hot: isHot,
10 | historyApiFallback: true
11 | }).listen(3000, 'localhost', err => console.log(err || 'webpack at localhost:3000'));
12 |
--------------------------------------------------------------------------------
/src/Animated.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule Animated
10 | */
11 | 'use strict';
12 |
13 | import Easing from './Easing';
14 | import Interpolation from './interpolation';
15 | import React from 'react';
16 |
17 | // var Animated = (function() {
18 |
19 | // Note(vjeux): this would be better as an interface but flow doesn't
20 | // support them yet
21 | class Animated {
22 | attach(): void {}
23 | detach(): void {}
24 | getValue(): any {}
25 | getAnimatedValue(): any { return this.getValue(); }
26 | addChild(child: Animated) {}
27 | removeChild(child: Animated) {}
28 | getChildren(): Array { return []; }
29 | }
30 |
31 | // Important note: start() and stop() will only be called at most once.
32 | // Once an animation has been stopped or finished its course, it will
33 | // not be reused.
34 | class Animation {
35 | start(
36 | fromValue: number,
37 | onUpdate: (value: number) => void,
38 | onEnd: ?((finished: bool) => void),
39 | previousAnimation: ?Animation
40 | ): void {}
41 | stop(): void {}
42 | }
43 |
44 | class AnimatedWithChildren extends Animated {
45 | _children: Array;
46 |
47 | constructor() {
48 | super();
49 | this._children = [];
50 | }
51 |
52 | addChild(child: Animated): void {
53 | if (this._children.length === 0) {
54 | this.attach();
55 | }
56 | this._children.push(child);
57 | }
58 |
59 | removeChild(child: Animated): void {
60 | var index = this._children.indexOf(child);
61 | if (index === -1) {
62 | console.warn('Trying to remove a child that doesn\'t exist');
63 | return;
64 | }
65 | this._children.splice(index, 1);
66 | if (this._children.length === 0) {
67 | this.detach();
68 | }
69 | }
70 |
71 | getChildren(): Array {
72 | return this._children;
73 | }
74 | }
75 |
76 | /**
77 | * Animated works by building a directed acyclic graph of dependencies
78 | * transparently when you render your Animated components.
79 | *
80 | * new Animated.Value(0)
81 | * .interpolate() .interpolate() new Animated.Value(1)
82 | * opacity translateY scale
83 | * style transform
84 | * View#234 style
85 | * View#123
86 | *
87 | * A) Top Down phase
88 | * When an Animated.Value is updated, we recursively go down through this
89 | * graph in order to find leaf nodes: the views that we flag as needing
90 | * an update.
91 | *
92 | * B) Bottom Up phase
93 | * When a view is flagged as needing an update, we recursively go back up
94 | * in order to build the new value that it needs. The reason why we need
95 | * this two-phases process is to deal with composite props such as
96 | * transform which can receive values from multiple parents.
97 | */
98 | function _flush(node: AnimatedValue): void {
99 | var animatedStyles = new Set();
100 | function findAnimatedStyles(theNode) {
101 | if ('update' in theNode) {
102 | animatedStyles.add(theNode);
103 | } else {
104 | theNode.getChildren().forEach(findAnimatedStyles);
105 | }
106 | }
107 | findAnimatedStyles(node);
108 | animatedStyles.forEach(animatedStyle => animatedStyle.update());
109 | }
110 |
111 | type TimingAnimationConfig = {
112 | toValue: number;
113 | easing?: (value: number) => number;
114 | duration?: number;
115 | delay?: number;
116 | };
117 |
118 | class TimingAnimation extends Animation {
119 | _startTime: number;
120 | _fromValue: number;
121 | _toValue: number;
122 | _duration: number;
123 | _delay: number;
124 | _easing: (value: number) => number;
125 | _onUpdate: (value: number) => void;
126 | _onEnd: ?((finished: bool) => void);
127 | _animationFrame: any;
128 | _timeout: any;
129 |
130 | constructor(
131 | config: TimingAnimationConfig
132 | ) {
133 | super();
134 | this._toValue = config.toValue;
135 | this._easing = config.easing || Easing.inOut(Easing.ease);
136 | this._duration = config.duration !== undefined ? config.duration : 500;
137 | this._delay = config.delay || 0;
138 | }
139 |
140 | start(
141 | fromValue: number,
142 | onUpdate: (value: number) => void,
143 | onEnd: ?((finished: bool) => void)
144 | ): void {
145 | this._fromValue = fromValue;
146 | this._onUpdate = onUpdate;
147 | this._onEnd = onEnd;
148 |
149 | var start = () => {
150 | this._startTime = Date.now();
151 | this._animationFrame = window.requestAnimationFrame(this.onUpdate.bind(this));
152 | };
153 | if (this._delay) {
154 | this._timeout = setTimeout(start, this._delay);
155 | } else {
156 | start();
157 | }
158 | }
159 |
160 | onUpdate(): void {
161 | var now = Date.now();
162 |
163 | if (now > this._startTime + this._duration) {
164 | this._onUpdate(
165 | this._fromValue + this._easing(1) * (this._toValue - this._fromValue)
166 | );
167 | var onEnd = this._onEnd;
168 | this._onEnd = null;
169 | onEnd && onEnd(/* finished */ true);
170 | return;
171 | }
172 |
173 | this._onUpdate(
174 | this._fromValue +
175 | this._easing((now - this._startTime) / this._duration) *
176 | (this._toValue - this._fromValue)
177 | );
178 |
179 | this._animationFrame = window.requestAnimationFrame(this.onUpdate.bind(this));
180 | }
181 |
182 | stop(): void {
183 | clearTimeout(this._timeout);
184 | window.cancelAnimationFrame(this._animationFrame);
185 | var onEnd = this._onEnd;
186 | this._onEnd = null;
187 | onEnd && onEnd(/* finished */ false);
188 | }
189 | }
190 |
191 | type DecayAnimationConfig = {
192 | velocity: number;
193 | deceleration?: number;
194 | };
195 |
196 | class DecayAnimation extends Animation {
197 | _startTime: number;
198 | _lastValue: number;
199 | _fromValue: number;
200 | _deceleration: number;
201 | _velocity: number;
202 | _onUpdate: (value: number) => void;
203 | _onEnd: ?((finished: bool) => void);
204 | _animationFrame: any;
205 |
206 | constructor(
207 | config: DecayAnimationConfig
208 | ) {
209 | super();
210 | this._deceleration = config.deceleration || 0.998;
211 | this._velocity = config.velocity;
212 | }
213 |
214 | start(
215 | fromValue: number,
216 | onUpdate: (value: number) => void,
217 | onEnd: ?((finished: bool) => void)
218 | ): void {
219 | this._lastValue = fromValue;
220 | this._fromValue = fromValue;
221 | this._onUpdate = onUpdate;
222 | this._onEnd = onEnd;
223 | this._startTime = Date.now();
224 | this._animationFrame = window.requestAnimationFrame(this.onUpdate.bind(this));
225 | }
226 |
227 | onUpdate(): void {
228 | var now = Date.now();
229 |
230 | var value = this._fromValue +
231 | (this._velocity / (1 - this._deceleration)) *
232 | (1 - Math.exp(-(1 - this._deceleration) * (now - this._startTime)));
233 |
234 | this._onUpdate(value);
235 |
236 | if (Math.abs(this._lastValue - value) < 0.1) {
237 | var onEnd = this._onEnd;
238 | this._onEnd = null;
239 | onEnd && onEnd(/* finished */ true);
240 | return;
241 | }
242 |
243 | this._lastValue = value;
244 | this._animationFrame = window.requestAnimationFrame(this.onUpdate.bind(this));
245 | }
246 |
247 | stop(): void {
248 | window.cancelAnimationFrame(this._animationFrame);
249 | var onEnd = this._onEnd;
250 | this._onEnd = null;
251 | onEnd && onEnd(/* finished */ false);
252 | }
253 | }
254 |
255 | type SpringAnimationConfig = {
256 | toValue: number;
257 | overshootClamping?: bool;
258 | restDisplacementThreshold?: number;
259 | restSpeedThreshold?: number;
260 | velocity?: number;
261 | bounciness?: number;
262 | speed?: number;
263 | tension?: number;
264 | friction?: number;
265 | };
266 |
267 | function withDefault(value: ?T, defaultValue: T): T {
268 | if (value === undefined || value === null) {
269 | return defaultValue;
270 | }
271 | return value;
272 | }
273 |
274 |
275 | function tensionFromOrigamiValue(oValue) {
276 | return (oValue - 30.0) * 3.62 + 194.0;
277 | }
278 | function frictionFromOrigamiValue(oValue) {
279 | return (oValue - 8.0) * 3.0 + 25.0;
280 | }
281 |
282 | var fromOrigamiTensionAndFriction = function(tension, friction) {
283 | return {
284 | tension: tensionFromOrigamiValue(tension),
285 | friction: frictionFromOrigamiValue(friction)
286 | };
287 | }
288 |
289 | var fromBouncinessAndSpeed = function(bounciness, speed) {
290 | function normalize(value, startValue, endValue) {
291 | return (value - startValue) / (endValue - startValue);
292 | }
293 | function projectNormal(n, start, end) {
294 | return start + (n * (end - start));
295 | }
296 | function linearInterpolation(t, start, end) {
297 | return t * end + (1.0 - t) * start;
298 | }
299 | function quadraticOutInterpolation(t, start, end) {
300 | return linearInterpolation(2 * t - t * t, start, end);
301 | }
302 | function b3Friction1(x) {
303 | return (0.0007 * Math.pow(x, 3)) -
304 | (0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28;
305 | }
306 | function b3Friction2(x) {
307 | return (0.000044 * Math.pow(x, 3)) -
308 | (0.006 * Math.pow(x, 2)) + 0.36 * x + 2.;
309 | }
310 | function b3Friction3(x) {
311 | return (0.00000045 * Math.pow(x, 3)) -
312 | (0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84;
313 | }
314 | function b3Nobounce(tension) {
315 | if (tension <= 18) {
316 | return b3Friction1(tension);
317 | } else if (tension > 18 && tension <= 44) {
318 | return b3Friction2(tension);
319 | } else {
320 | return b3Friction3(tension);
321 | }
322 | }
323 |
324 | var b = normalize(bounciness / 1.7, 0, 20.0);
325 | b = projectNormal(b, 0.0, 0.8);
326 | var s = normalize(speed / 1.7, 0, 20.0);
327 | var bouncyTension = projectNormal(s, 0.5, 200)
328 | var bouncyFriction = quadraticOutInterpolation(
329 | b,
330 | b3Nobounce(bouncyTension),
331 | 0.01
332 | );
333 |
334 | return {
335 | tension: tensionFromOrigamiValue(bouncyTension),
336 | friction: frictionFromOrigamiValue(bouncyFriction)
337 | };
338 | }
339 |
340 | class SpringAnimation extends Animation {
341 | _overshootClamping: bool;
342 | _restDisplacementThreshold: number;
343 | _restSpeedThreshold: number;
344 | _lastVelocity: number;
345 | _tempVelocity: number;
346 | _startPosition: number;
347 | _lastPosition: number;
348 | _tempPosition: number;
349 | _fromValue: number;
350 | _toValue: number;
351 | _tension: number;
352 | _friction: number;
353 | _lastTime: number;
354 | _onUpdate: (value: number) => void;
355 | _onEnd: ?((finished: bool) => void);
356 | _animationFrame: any;
357 | _active: bool;
358 |
359 | constructor(
360 | config: SpringAnimationConfig
361 | ) {
362 | super();
363 |
364 | this._overshootClamping = withDefault(config.overshootClamping, false);
365 | this._restDisplacementThreshold = withDefault(config.restDisplacementThreshold, 0.001);
366 | this._restSpeedThreshold = withDefault(config.restSpeedThreshold, 0.001);
367 | this._lastVelocity = withDefault(config.velocity, 0);
368 | this._tempVelocity = this._lastVelocity;
369 | this._toValue = config.toValue;
370 |
371 | var springConfig;
372 | if (config.bounciness !== undefined || config.speed !== undefined) {
373 | invariant(
374 | config.tension === undefined && config.friction === undefined,
375 | 'You can only define bounciness/speed or tension/friction but not both'
376 | );
377 | springConfig = fromBouncinessAndSpeed(
378 | withDefault(config.bounciness, 8),
379 | withDefault(config.speed, 12)
380 | );
381 | } else {
382 | springConfig = fromOrigamiTensionAndFriction(
383 | withDefault(config.tension, 40),
384 | withDefault(config.friction, 7)
385 | );
386 | }
387 | this._tension = springConfig.tension;
388 | this._friction = springConfig.friction;
389 | }
390 |
391 | start(
392 | fromValue: number,
393 | onUpdate: (value: number) => void,
394 | onEnd: ?((finished: bool) => void),
395 | previousAnimation: ?Animation
396 | ): void {
397 | this._active = true;
398 | this._startPosition = fromValue;
399 | this._lastPosition = this._startPosition;
400 | this._tempPosition = this._lastPosition;
401 |
402 | this._onUpdate = onUpdate;
403 | this._onEnd = onEnd;
404 | this._lastTime = Date.now();
405 |
406 | if (previousAnimation instanceof SpringAnimation) {
407 | var internalState = previousAnimation.getInternalState();
408 | this._lastPosition = internalState.lastPosition;
409 | this._tempPosition = internalState.tempPosition;
410 | this._lastVelocity = internalState.lastVelocity;
411 | this._tempVelocity = internalState.tempVelocity;
412 | this._lastTime = internalState.lastTime;
413 | }
414 |
415 | this.onUpdate();
416 | }
417 |
418 | getInternalState(): any {
419 | return {
420 | lastPosition: this._lastPosition,
421 | tempPosition: this._tempPosition,
422 | lastVelocity: this._lastVelocity,
423 | tempVelocity: this._tempVelocity,
424 | lastTime: this._lastTime,
425 | };
426 | }
427 |
428 | onUpdate(): void {
429 | if (!this._active) {
430 | return;
431 | }
432 | var now = Date.now();
433 |
434 | var position = this._lastPosition;
435 | var velocity = this._lastVelocity;
436 |
437 | var tempPosition = position;
438 | var tempVelocity = velocity;
439 |
440 | var TIMESTEP_MSEC = 4;
441 | var numSteps = Math.floor((now - this._lastTime) / TIMESTEP_MSEC);
442 | for (var i = 0; i < numSteps; ++i) {
443 | // Velocity is based on seconds instead of milliseconds
444 | var step = TIMESTEP_MSEC / 1000;
445 |
446 | var aVelocity = velocity;
447 | var aAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
448 | tempPosition = position + aVelocity * step / 2;
449 | tempVelocity = velocity + aAcceleration * step / 2;
450 |
451 | var bVelocity = tempVelocity;
452 | var bAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
453 | tempPosition = position + bVelocity * step / 2;
454 | tempVelocity = velocity + bAcceleration * step / 2;
455 |
456 | var cVelocity = tempVelocity;
457 | var cAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
458 | tempPosition = position + cVelocity * step;
459 | tempVelocity = velocity + cAcceleration * step;
460 |
461 | var dVelocity = tempVelocity;
462 | var dAcceleration = this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
463 |
464 | var dxdt = (aVelocity + 2 * (bVelocity + cVelocity) + dVelocity) / 6;
465 | var dvdt = (aAcceleration + 2 * (bAcceleration + cAcceleration) + dAcceleration) / 6;
466 |
467 | position += dxdt * step;
468 | velocity += dvdt * step;
469 | }
470 |
471 | this._lastTime = now;
472 | this._tempPosition = tempPosition;
473 | this._tempVelocity = tempVelocity;
474 | this._lastPosition = position;
475 | this._lastVelocity = velocity;
476 |
477 | this._onUpdate(position);
478 |
479 | // Conditions for stopping the spring animation
480 | var isOvershooting = false;
481 | if (this._overshootClamping && this._tension !== 0) {
482 | if (this._startPosition < this._toValue) {
483 | isOvershooting = position > this._toValue;
484 | } else {
485 | isOvershooting = position < this._toValue;
486 | }
487 | }
488 | var isVelocity = Math.abs(velocity) <= this._restSpeedThreshold;
489 | var isDisplacement = true;
490 | if (this._tension !== 0) {
491 | isDisplacement = Math.abs(this._toValue - position) <= this._restDisplacementThreshold;
492 | }
493 | if (isOvershooting || (isVelocity && isDisplacement)) {
494 | var onEnd = this._onEnd;
495 | this._onEnd = null;
496 | onEnd && onEnd(/* finished */ true);
497 | return;
498 | }
499 | this._animationFrame = window.requestAnimationFrame(this.onUpdate.bind(this));
500 | }
501 |
502 | stop(): void {
503 | this._active = false;
504 | window.cancelAnimationFrame(this._animationFrame);
505 | var onEnd = this._onEnd;
506 | this._onEnd = null;
507 | onEnd && onEnd(/* finished */ false);
508 | }
509 | }
510 |
511 | type ValueListenerCallback = (state: {value: number}) => void;
512 |
513 | var _uniqueId = 1;
514 |
515 | class AnimatedValue extends AnimatedWithChildren {
516 | _value: number;
517 | _offset: number;
518 | _animation: ?Animation;
519 | _listeners: {[key: number]: ValueListenerCallback};
520 |
521 | constructor(value: number) {
522 | super();
523 | this._value = value;
524 | this._offset = 0;
525 | this._animation = null;
526 | this._listeners = {};
527 | }
528 |
529 | detach() {
530 | this.stopAnimation();
531 | }
532 |
533 | getValue(): number {
534 | return this._value + this._offset;
535 | }
536 |
537 | setValue(value: number): void {
538 | if (this._animation) {
539 | this._animation.stop();
540 | this._animation = null;
541 | }
542 | this._updateValue(value);
543 | }
544 |
545 | getOffset(): number {
546 | return this._offset;
547 | }
548 |
549 | setOffset(offset: number): void {
550 | this._offset = offset;
551 | }
552 |
553 | addListener(callback: ValueListenerCallback): number {
554 | var id = _uniqueId++;
555 | this._listeners[id] = callback;
556 | return id;
557 | }
558 |
559 | removeListener(id: number): void {
560 | delete this._listeners[id];
561 | }
562 |
563 | animate(animation: Animation, callback: ?((finished: bool) => void)): void {
564 | var previousAnimation = this._animation;
565 | this._animation && this._animation.stop();
566 | this._animation = animation;
567 | animation.start(
568 | this._value,
569 | (value) => {
570 | this._updateValue(value);
571 | },
572 | (finished) => {
573 | this._animation = null;
574 | callback && callback(finished);
575 | },
576 | previousAnimation
577 | );
578 | }
579 |
580 | stopAnimation(callback?: ?() => number): void {
581 | this.stopTracking();
582 | this._animation && this._animation.stop();
583 | callback && callback(this._value);
584 | }
585 |
586 | stopTracking(): void {
587 | this._tracking && this._tracking.detach();
588 | }
589 |
590 | track(tracking: Animation): void {
591 | this.stopTracking();
592 | this._tracking = tracking;
593 | }
594 |
595 | interpolate(config: InterpolationConfigType): AnimatedInterpolation {
596 | return new AnimatedInterpolation(this, Interpolation.create(config));
597 | }
598 |
599 | _updateValue(value: number): void {
600 | if (value === this._value) {
601 | return;
602 | }
603 | this._value = value;
604 | _flush(this);
605 | for (var key in this._listeners) {
606 | this._listeners[key]({value: this.getValue()});
607 | }
608 | }
609 | }
610 |
611 | type Vec2ListenerCallback = (state: {x: number; y: number}) => void;
612 | class AnimatedVec2 extends AnimatedWithChildren {
613 | x: AnimatedValue;
614 | y: AnimatedValue;
615 | _listeners: {[key: number]: Vec2ListenerCallback};
616 |
617 | constructor(value?: {x: number; y: number}) {
618 | super();
619 | value = value || {x: 0, y: 0};
620 | if (typeof value.x === 'number') {
621 | this.x = new AnimatedValue(value.x);
622 | this.y = new AnimatedValue(value.y);
623 | } else {
624 | this.x = value.x;
625 | this.y = value.y;
626 | }
627 | this._listeners = {};
628 | }
629 |
630 | setValue(value: {x: number; y: number}) {
631 | this.x.setValue(value.x);
632 | this.y.setValue(value.y);
633 | }
634 |
635 | setOffset(offset: {x: number; y: number}) {
636 | this.x.setOffset(offset.x);
637 | this.y.setOffset(offset.y);
638 | }
639 |
640 | addListener(callback: Vec2ListenerCallback): number {
641 | var id = _uniqueId++;
642 | var jointCallback = (value) => {
643 | callback({x: this.x.getValue(), y: this.y.getValue()});
644 | };
645 | this._listeners[id] = {
646 | x: this.x.addListener(jointCallback),
647 | y: this.y.addListener(jointCallback),
648 | };
649 | return id;
650 | }
651 |
652 | removeListener(id: number): void {
653 | this.x.removeListener(this._listeners[id].x);
654 | this.y.removeListener(this._listeners[id].y);
655 | delete this._listeners[id];
656 | }
657 |
658 | offset(theOffset) { // chunky...perf?
659 | return new AnimatedVec2({
660 | x: this.x.interpolate({
661 | inputRange: [0, 1],
662 | outputRange: [theOffset.x, theOffset.x + 1],
663 | }),
664 | y: this.y.interpolate({
665 | inputRange: [0, 1],
666 | outputRange: [theOffset.y, theOffset.y + 1],
667 | }),
668 | });
669 | }
670 |
671 | getLayout() {
672 | return {
673 | left: this.x,
674 | top: this.y,
675 | };
676 | }
677 |
678 | getTranslateTransform() {
679 | return [
680 | {translateX: this.x},
681 | {translateY: this.y}
682 | ];
683 | }
684 | }
685 |
686 | class AnimatedInterpolation extends AnimatedWithChildren {
687 | _parent: Animated;
688 | _interpolation: (input: number) => number | string;
689 |
690 | constructor(parent: Animated, interpolation: (input: number) => number | string) {
691 | super();
692 | this._parent = parent;
693 | this._interpolation = interpolation;
694 | }
695 |
696 | getValue(): number | string {
697 | var parentValue: number = this._parent.getValue();
698 | invariant(
699 | typeof parentValue === 'number',
700 | 'Cannot interpolate an input which is not a number.'
701 | );
702 | return this._interpolation(parentValue);
703 | }
704 |
705 | interpolate(config: InterpolationConfigType): AnimatedInterpolation {
706 | return new AnimatedInterpolation(this, Interpolation.create(config));
707 | }
708 |
709 | attach(): void {
710 | this._parent.addChild(this);
711 | }
712 |
713 | detach(): void {
714 | this._parent.removeChild(this);
715 | }
716 | }
717 |
718 | class AnimatedTransform extends AnimatedWithChildren {
719 | _transforms: Array