animejs 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +94 -96
  2. package/anime.js +236 -113
  3. package/anime.min.js +29 -23
  4. package/package.json +4 -4
package/anime.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * http://anime-js.com
2
+ * http://animejs.com
3
3
  * JavaScript animation engine
4
- * @version v2.0.0
4
+ * @version v2.2.0
5
5
  * @author Julian Garnier
6
6
  * @copyright ©2017 Julian Garnier
7
7
  * Released under the MIT license
@@ -43,7 +43,7 @@
43
43
  round: 0
44
44
  }
45
45
 
46
- const validTransforms = ['translateX', 'translateY', 'translateZ', 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scaleX', 'scaleY', 'scaleZ', 'skewX', 'skewY'];
46
+ const validTransforms = ['translateX', 'translateY', 'translateZ', 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scaleX', 'scaleY', 'scaleZ', 'skewX', 'skewY', 'perspective'];
47
47
  let transformString;
48
48
 
49
49
  // Utils
@@ -55,7 +55,9 @@
55
55
  const is = {
56
56
  arr: a => Array.isArray(a),
57
57
  obj: a => stringContains(Object.prototype.toString.call(a), 'Object'),
58
- dom: a => a.nodeType || a instanceof SVGElement,
58
+ pth: a => is.obj(a) && a.hasOwnProperty('totalLength'),
59
+ svg: a => a instanceof SVGElement,
60
+ dom: a => a.nodeType || is.svg(a),
59
61
  str: a => typeof a === 'string',
60
62
  fnc: a => typeof a === 'function',
61
63
  und: a => typeof a === 'undefined',
@@ -228,8 +230,19 @@
228
230
 
229
231
  // Arrays
230
232
 
231
- function arrayLength(arr) {
232
- return arr.length;
233
+ function filterArray(arr, callback) {
234
+ const len = arr.length;
235
+ const thisArg = arguments.length >= 2 ? arguments[1] : void 0;
236
+ let result = [];
237
+ for (let i = 0; i < len; i++) {
238
+ if (i in arr) {
239
+ const val = arr[i];
240
+ if (callback.call(thisArg, val, i, arr)) {
241
+ result.push(val);
242
+ }
243
+ }
244
+ }
245
+ return result;
233
246
  }
234
247
 
235
248
  function flattenArray(arr) {
@@ -249,10 +262,6 @@
249
262
 
250
263
  // Objects
251
264
 
252
- function objectHas(obj, prop) {
253
- return obj.hasOwnProperty(prop);
254
- }
255
-
256
265
  function cloneObject(o) {
257
266
  let clone = {};
258
267
  for (let p in o) clone[p] = o[p];
@@ -261,7 +270,7 @@
261
270
 
262
271
  function replaceObjectProps(o1, o2) {
263
272
  let o = cloneObject(o1);
264
- for (let p in o1) o[p] = objectHas(o2, p) ? o2[p] : o1[p];
273
+ for (let p in o1) o[p] = o2.hasOwnProperty(p) ? o2[p] : o1[p];
265
274
  return o;
266
275
  }
267
276
 
@@ -273,21 +282,27 @@
273
282
 
274
283
  // Colors
275
284
 
276
- function hexToRgb(hexValue) {
285
+ function rgbToRgba(rgbValue) {
286
+ const rgb = /rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(rgbValue);
287
+ return rgb ? `rgba(${rgb[1]},1)` : rgbValue;
288
+ }
289
+
290
+ function hexToRgba(hexValue) {
277
291
  const rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
278
292
  const hex = hexValue.replace(rgx, (m, r, g, b) => r + r + g + g + b + b );
279
293
  const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
280
294
  const r = parseInt(rgb[1], 16);
281
295
  const g = parseInt(rgb[2], 16);
282
296
  const b = parseInt(rgb[3], 16);
283
- return `rgb(${r},${g},${b})`;
297
+ return `rgba(${r},${g},${b},1)`;
284
298
  }
285
299
 
286
- function hslToRgb(hslValue) {
287
- const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue);
300
+ function hslToRgba(hslValue) {
301
+ const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
288
302
  const h = parseInt(hsl[1]) / 360;
289
303
  const s = parseInt(hsl[2]) / 100;
290
304
  const l = parseInt(hsl[3]) / 100;
305
+ const a = hsl[4] || 1;
291
306
  function hue2rgb(p, q, t) {
292
307
  if (t < 0) t += 1;
293
308
  if (t > 1) t -= 1;
@@ -306,33 +321,29 @@
306
321
  g = hue2rgb(p, q, h);
307
322
  b = hue2rgb(p, q, h - 1/3);
308
323
  }
309
- return `rgb(${r * 255},${g * 255},${b * 255})`;
324
+ return `rgba(${r * 255},${g * 255},${b * 255},${a})`;
310
325
  }
311
326
 
312
327
  function colorToRgb(val) {
313
- if (is.rgb(val)) return val;
314
- if (is.hex(val)) return hexToRgb(val);
315
- if (is.hsl(val)) return hslToRgb(val);
328
+ if (is.rgb(val)) return rgbToRgba(val);
329
+ if (is.hex(val)) return hexToRgba(val);
330
+ if (is.hsl(val)) return hslToRgba(val);
316
331
  }
317
332
 
318
333
  // Units
319
334
 
320
335
  function getUnit(val) {
321
- const split = /([\+\-]?[0-9#\.]+)(%|px|pt|em|rem|in|cm|mm|ex|pc|vw|vh|deg|rad|turn)?/.exec(val);
336
+ const split = /([\+\-]?[0-9#\.]+)(%|px|pt|em|rem|in|cm|mm|ex|ch|pc|vw|vh|vmin|vmax|deg|rad|turn)?$/.exec(val);
322
337
  if (split) return split[2];
323
338
  }
324
339
 
325
340
  function getTransformUnit(propName) {
326
- if (stringContains(propName, 'translate')) return 'px';
341
+ if (stringContains(propName, 'translate') || propName === 'perspective') return 'px';
327
342
  if (stringContains(propName, 'rotate') || stringContains(propName, 'skew')) return 'deg';
328
343
  }
329
344
 
330
345
  // Values
331
346
 
332
- function parseFloatValue(val) {
333
- return parseFloat(val);
334
- }
335
-
336
347
  function minMaxValue(val, min, max) {
337
348
  return Math.min(Math.max(val, min), max);
338
349
  }
@@ -350,7 +361,7 @@
350
361
 
351
362
  function getAnimationType(el, prop) {
352
363
  if (is.dom(el) && arrayContains(validTransforms, prop)) return 'transform';
353
- if (is.dom(el) && (el.getAttribute(prop))) return 'attribute';
364
+ if (is.dom(el) && (el.getAttribute(prop) || (is.svg(el) && el[prop]))) return 'attribute';
354
365
  if (is.dom(el) && (prop !== 'transform' && getCSSValue(el, prop))) return 'css';
355
366
  if (el[prop] != null) return 'object';
356
367
  }
@@ -368,8 +379,8 @@
368
379
  props.push(match[1]);
369
380
  values.push(match[2]);
370
381
  }
371
- const value = values.filter((val, i) => props[i] === propName );
372
- return arrayLength(value) ? value[0] : defaultVal;
382
+ const value = filterArray(values, (val, i) => props[i] === propName);
383
+ return value.length ? value[0] : defaultVal;
373
384
  }
374
385
 
375
386
  function getOriginalTargetValue(target, propName) {
@@ -384,34 +395,83 @@
384
395
  function getRelativeValue(to, from) {
385
396
  const operator = /^(\*=|\+=|-=)/.exec(to);
386
397
  if (!operator) return to;
387
- const x = parseFloatValue(from);
388
- const y = parseFloatValue(to.replace(operator[0], ''));
398
+ const u = getUnit(to) || 0;
399
+ const x = parseFloat(from);
400
+ const y = parseFloat(to.replace(operator[0], ''));
389
401
  switch (operator[0][0]) {
390
- case '+': return x + y;
391
- case '-': return x - y;
392
- case '*': return x * y;
402
+ case '+': return x + y + u;
403
+ case '-': return x - y + u;
404
+ case '*': return x * y + u;
393
405
  }
394
406
  }
395
407
 
396
408
  function validateValue(val, unit) {
397
409
  if (is.col(val)) return colorToRgb(val);
398
410
  const originalUnit = getUnit(val);
399
- const unitLess = originalUnit ? val.substr(0, arrayLength(val) - arrayLength(originalUnit)) : val;
400
- return unit ? unitLess + unit : unitLess;
411
+ const unitLess = originalUnit ? val.substr(0, val.length - originalUnit.length) : val;
412
+ return unit && !/\s/g.test(val) ? unitLess + unit : unitLess;
401
413
  }
402
414
 
403
- // Motion path
415
+ // getTotalLength() equivalent for circle, rect, polyline, polygon and line shapes.
416
+ // adapted from https://gist.github.com/SebLambla/3e0550c496c236709744
404
417
 
405
- function isPath(val) {
406
- return is.obj(val) && objectHas(val, 'totalLength');
418
+ function getDistance(p1, p2) {
419
+ return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
420
+ }
421
+
422
+ function getCircleLength(el) {
423
+ return 2 * Math.PI * el.getAttribute('r');
424
+ }
425
+
426
+ function getRectLength(el) {
427
+ return (el.getAttribute('width') * 2) + (el.getAttribute('height') * 2);
428
+ }
429
+
430
+ function getLineLength(el) {
431
+ return getDistance(
432
+ {x: el.getAttribute('x1'), y: el.getAttribute('y1')},
433
+ {x: el.getAttribute('x2'), y: el.getAttribute('y2')}
434
+ );
435
+ }
436
+
437
+ function getPolylineLength(el) {
438
+ const points = el.points;
439
+ let totalLength = 0;
440
+ let previousPos;
441
+ for (let i = 0 ; i < points.numberOfItems; i++) {
442
+ const currentPos = points.getItem(i);
443
+ if (i > 0) totalLength += getDistance(previousPos, currentPos);
444
+ previousPos = currentPos;
445
+ }
446
+ return totalLength;
447
+ }
448
+
449
+ function getPolygonLength(el) {
450
+ const points = el.points;
451
+ return getPolylineLength(el) + getDistance(points.getItem(points.numberOfItems - 1), points.getItem(0));
452
+ }
453
+
454
+ // Path animation
455
+
456
+ function getTotalLength(el) {
457
+ if (el.getTotalLength) return el.getTotalLength();
458
+ switch(el.tagName.toLowerCase()) {
459
+ case 'circle': return getCircleLength(el);
460
+ case 'rect': return getRectLength(el);
461
+ case 'line': return getLineLength(el);
462
+ case 'polyline': return getPolylineLength(el);
463
+ case 'polygon': return getPolygonLength(el);
464
+ }
407
465
  }
408
466
 
409
467
  function setDashoffset(el) {
410
- const pathLength = el.getTotalLength();
468
+ const pathLength = getTotalLength(el);
411
469
  el.setAttribute('stroke-dasharray', pathLength);
412
470
  return pathLength;
413
471
  }
414
472
 
473
+ // Motion path
474
+
415
475
  function getPath(path, percent) {
416
476
  const el = is.str(path) ? selectString(path)[0] : path;
417
477
  const p = percent || 100;
@@ -419,7 +479,7 @@
419
479
  return {
420
480
  el: el,
421
481
  property: prop,
422
- totalLength: el.getTotalLength() * (p / 100)
482
+ totalLength: getTotalLength(el) * (p / 100)
423
483
  }
424
484
  }
425
485
  }
@@ -439,33 +499,29 @@
439
499
  }
440
500
  }
441
501
 
442
- // Decompose / recompose functions adapted from Animate Plus https://github.com/bendc/animateplus
502
+ // Decompose value
443
503
 
444
504
  function decomposeValue(val, unit) {
445
505
  const rgx = /-?\d*\.?\d+/g;
446
- const value = validateValue((isPath(val) ? val.totalLength : val), unit) + '';
506
+ const value = validateValue((is.pth(val) ? val.totalLength : val), unit) + '';
447
507
  return {
448
508
  original: value,
449
509
  numbers: value.match(rgx) ? value.match(rgx).map(Number) : [0],
450
- strings: value.split(rgx)
510
+ strings: (is.str(val) || unit) ? value.split(rgx) : []
451
511
  }
452
512
  }
453
513
 
454
- function recomposeValue(numbers, strings) {
455
- return strings.reduce((a, b, i) => a + numbers[i - 1] + b);
456
- }
457
-
458
514
  // Animatables
459
515
 
460
516
  function parseTargets(targets) {
461
517
  const targetsArray = targets ? (flattenArray(is.arr(targets) ? targets.map(toArray) : toArray(targets))) : [];
462
- return targetsArray.filter((item, pos, self) => self.indexOf(item) === pos);
518
+ return filterArray(targetsArray, (item, pos, self) => self.indexOf(item) === pos);
463
519
  }
464
520
 
465
521
  function getAnimatables(targets) {
466
522
  const parsed = parseTargets(targets);
467
523
  return parsed.map((t, i) => {
468
- return {target: t, id: i, total: arrayLength(parsed)};
524
+ return {target: t, id: i, total: parsed.length};
469
525
  });
470
526
  }
471
527
 
@@ -474,7 +530,7 @@
474
530
  function normalizePropertyTweens(prop, tweenSettings) {
475
531
  let settings = cloneObject(tweenSettings);
476
532
  if (is.arr(prop)) {
477
- const l = arrayLength(prop);
533
+ const l = prop.length;
478
534
  const isFromTo = (l === 2 && !is.obj(prop[0]));
479
535
  if (!isFromTo) {
480
536
  // Duration divided by the number of tweens
@@ -488,7 +544,7 @@
488
544
  // Default delay value should be applied only on the first tween
489
545
  const delay = !i ? tweenSettings.delay : 0;
490
546
  // Use path object as a tween value
491
- let obj = is.obj(v) && !isPath(v) ? v : {value: v};
547
+ let obj = is.obj(v) && !is.pth(v) ? v : {value: v};
492
548
  // Set default delay value
493
549
  if (is.und(obj.delay)) obj.delay = delay;
494
550
  return obj;
@@ -499,7 +555,7 @@
499
555
  let properties = [];
500
556
  const settings = mergeObjects(instanceSettings, tweenSettings);
501
557
  for (let p in params) {
502
- if (!objectHas(settings, p) && p !== 'targets') {
558
+ if (!settings.hasOwnProperty(p) && p !== 'targets') {
503
559
  properties.push({
504
560
  name: p,
505
561
  offset: settings['offset'],
@@ -518,12 +574,12 @@
518
574
  let value = getFunctionValue(tween[p], animatable);
519
575
  if (is.arr(value)) {
520
576
  value = value.map(v => getFunctionValue(v, animatable));
521
- if (arrayLength(value) === 1) value = value[0];
577
+ if (value.length === 1) value = value[0];
522
578
  }
523
579
  t[p] = value;
524
580
  }
525
- t.duration = parseFloatValue(t.duration);
526
- t.delay = parseFloatValue(t.delay);
581
+ t.duration = parseFloat(t.duration);
582
+ t.delay = parseFloat(t.delay);
527
583
  return t;
528
584
  }
529
585
 
@@ -541,14 +597,15 @@
541
597
  const from = is.arr(tweenValue) ? tweenValue[0] : previousValue;
542
598
  const to = getRelativeValue(is.arr(tweenValue) ? tweenValue[1] : tweenValue, from);
543
599
  const unit = getUnit(to) || getUnit(from) || getUnit(originalValue);
544
- tween.isPath = isPath(tweenValue);
545
600
  tween.from = decomposeValue(from, unit);
546
601
  tween.to = decomposeValue(to, unit);
547
602
  tween.start = previousTween ? previousTween.end : prop.offset;
548
603
  tween.end = tween.start + tween.delay + tween.duration;
549
604
  tween.easing = normalizeEasing(tween.easing);
550
605
  tween.elasticity = (1000 - minMaxValue(tween.elasticity, 1, 999)) / 1000;
551
- if (is.col(tween.from.original)) tween.round = 1;
606
+ tween.isPath = is.pth(tweenValue);
607
+ tween.isColor = is.col(tween.from.original);
608
+ if (tween.isColor) tween.round = 1;
552
609
  previousTween = tween;
553
610
  return tween;
554
611
  });
@@ -577,25 +634,29 @@
577
634
  property: prop.name,
578
635
  animatable: animatable,
579
636
  tweens: tweens,
580
- duration: tweens[arrayLength(tweens) - 1].end,
637
+ duration: tweens[tweens.length - 1].end,
581
638
  delay: tweens[0].delay
582
639
  }
583
640
  }
584
641
  }
585
642
 
586
643
  function getAnimations(animatables, properties) {
587
- return flattenArray(animatables.map(animatable => {
644
+ return filterArray(flattenArray(animatables.map(animatable => {
588
645
  return properties.map(prop => {
589
646
  return createAnimation(animatable, prop);
590
647
  });
591
- })).filter(a => !is.und(a));
648
+ })), a => !is.und(a));
592
649
  }
593
650
 
594
651
  // Create Instance
595
652
 
596
- function getInstanceTimings(type, animations, tweenSettings) {
597
- const math = (type === 'delay') ? Math.min : Math.max;
598
- return arrayLength(animations) ? math.apply(Math, animations.map(anim => anim[type])) : tweenSettings[type];
653
+ function getInstanceTimings(type, animations, instanceSettings, tweenSettings) {
654
+ const isDelay = (type === 'delay');
655
+ if (animations.length) {
656
+ return (isDelay ? Math.min : Math.max).apply(Math, animations.map(anim => anim[type]));
657
+ } else {
658
+ return isDelay ? tweenSettings.delay : instanceSettings.offset + tweenSettings.delay + tweenSettings.duration;
659
+ }
599
660
  }
600
661
 
601
662
  function createNewInstance(params) {
@@ -605,10 +666,11 @@
605
666
  const properties = getProperties(instanceSettings, tweenSettings, params);
606
667
  const animations = getAnimations(animatables, properties);
607
668
  return mergeObjects(instanceSettings, {
669
+ children: [],
608
670
  animatables: animatables,
609
671
  animations: animations,
610
- duration: getInstanceTimings('duration', animations, tweenSettings),
611
- delay: getInstanceTimings('delay', animations, tweenSettings)
672
+ duration: getInstanceTimings('duration', animations, instanceSettings, tweenSettings),
673
+ delay: getInstanceTimings('delay', animations, instanceSettings, tweenSettings)
612
674
  });
613
675
  }
614
676
 
@@ -620,7 +682,7 @@
620
682
  const engine = (() => {
621
683
  function play() { raf = requestAnimationFrame(step); };
622
684
  function step(t) {
623
- const activeLength = arrayLength(activeInstances);
685
+ const activeLength = activeInstances.length;
624
686
  if (activeLength) {
625
687
  let i = 0;
626
688
  while (i < activeLength) {
@@ -653,18 +715,6 @@
653
715
 
654
716
  let instance = createNewInstance(params);
655
717
 
656
- instance.reset = function() {
657
- const direction = instance.direction;
658
- const loops = instance.loop;
659
- instance.currentTime = 0;
660
- instance.progress = 0;
661
- instance.paused = true;
662
- instance.began = false;
663
- instance.completed = false;
664
- instance.reversed = direction === 'reverse';
665
- instance.remaining = direction === 'alternate' && loops === 1 ? 2 : loops;
666
- }
667
-
668
718
  function toggleInstanceDirection() {
669
719
  instance.reversed = !instance.reversed;
670
720
  }
@@ -673,37 +723,78 @@
673
723
  return instance.reversed ? instance.duration - time : time;
674
724
  }
675
725
 
676
- function syncInstanceChildren(insTime) {
726
+ function syncInstanceChildren(time) {
677
727
  const children = instance.children;
678
- for (let i = 0; i < arrayLength(children); i++) children[i].seek(insTime);
728
+ const childrenLength = children.length;
729
+ if (time >= instance.currentTime) {
730
+ for (let i = 0; i < childrenLength; i++) children[i].seek(time);
731
+ } else {
732
+ for (let i = childrenLength; i--;) children[i].seek(time);
733
+ }
679
734
  }
680
735
 
681
736
  function setAnimationsProgress(insTime) {
682
737
  let i = 0;
683
738
  let transforms = {};
684
739
  const animations = instance.animations;
685
- while (i < arrayLength(animations)) {
740
+ const animationsLength = animations.length;
741
+ while (i < animationsLength) {
686
742
  const anim = animations[i];
687
743
  const animatable = anim.animatable;
688
744
  const tweens = anim.tweens;
689
- const tween = tweens.filter(t => (insTime < t.end))[0] || tweens[arrayLength(tweens) - 1];
690
- const isPath = tween.isPath;
691
- const round = tween.round;
745
+ const tweenLength = tweens.length - 1;
746
+ let tween = tweens[tweenLength];
747
+ // Only check for keyframes if there is more than one tween
748
+ if (tweenLength) tween = filterArray(tweens, t => (insTime < t.end))[0] || tween;
692
749
  const elapsed = minMaxValue(insTime - tween.start - tween.delay, 0, tween.duration) / tween.duration;
693
- const eased = tween.easing(elapsed, tween.elasticity);
694
- const progress = recomposeValue(tween.to.numbers.map((number, p) => {
695
- const start = isPath ? 0 : tween.from.numbers[p];
696
- let value = start + eased * (number - start);
697
- if (isPath) value = getPathProgress(tween.value, value);
698
- if (round) value = Math.round(value * round) / round;
699
- return value;
700
- }), tween.to.strings);
750
+ const eased = isNaN(elapsed) ? 1 : tween.easing(elapsed, tween.elasticity);
751
+ const strings = tween.to.strings;
752
+ const round = tween.round;
753
+ let numbers = [];
754
+ let progress;
755
+ const toNumbersLength = tween.to.numbers.length;
756
+ for (let n = 0; n < toNumbersLength; n++) {
757
+ let value;
758
+ const toNumber = tween.to.numbers[n];
759
+ const fromNumber = tween.from.numbers[n];
760
+ if (!tween.isPath) {
761
+ value = fromNumber + (eased * (toNumber - fromNumber));
762
+ } else {
763
+ value = getPathProgress(tween.value, eased * toNumber);
764
+ }
765
+ if (round) {
766
+ if (!(tween.isColor && n > 2)) {
767
+ value = Math.round(value * round) / round;
768
+ }
769
+ }
770
+ numbers.push(value);
771
+ }
772
+ // Manual Array.reduce for better performances
773
+ const stringsLength = strings.length;
774
+ if (!stringsLength) {
775
+ progress = numbers[0];
776
+ } else {
777
+ progress = strings[0];
778
+ for (let s = 0; s < stringsLength; s++) {
779
+ const a = strings[s];
780
+ const b = strings[s + 1];
781
+ const n = numbers[s];
782
+ if (!isNaN(n)) {
783
+ if (!b) {
784
+ progress += n + ' ';
785
+ } else {
786
+ progress += n + b;
787
+ }
788
+ }
789
+ }
790
+ }
701
791
  setTweenProgress[anim.type](animatable.target, anim.property, progress, transforms, animatable.id);
702
792
  anim.currentValue = progress;
703
793
  i++;
704
794
  }
705
- if (transforms) {
706
- let id; for (id in transforms) {
795
+ const transformsLength = Object.keys(transforms).length;
796
+ if (transformsLength) {
797
+ for (let id = 0; id < transformsLength; id++) {
707
798
  if (!transformString) {
708
799
  const t = 'transform';
709
800
  transformString = (getCSSValue(document.body, t) ? t : `-webkit-${t}`);
@@ -728,44 +819,64 @@
728
819
  function setInstanceProgress(engineTime) {
729
820
  const insDuration = instance.duration;
730
821
  const insOffset = instance.offset;
731
- const insDelay = instance.delay;
822
+ const insStart = insOffset + instance.delay;
732
823
  const insCurrentTime = instance.currentTime;
733
824
  const insReversed = instance.reversed;
734
- const insTime = minMaxValue(adjustTime(engineTime), 0, insDuration);
735
- if (insTime > insOffset && insTime < insDuration) {
736
- setAnimationsProgress(insTime);
737
- if (!instance.began && insTime >= insDelay) {
825
+ const insTime = adjustTime(engineTime);
826
+ if (instance.children.length) syncInstanceChildren(insTime);
827
+ if (insTime >= insStart || !insDuration) {
828
+ if (!instance.began) {
738
829
  instance.began = true;
739
830
  setCallback('begin');
740
831
  }
741
832
  setCallback('run');
833
+ }
834
+ if (insTime > insOffset && insTime < insDuration) {
835
+ setAnimationsProgress(insTime);
742
836
  } else {
743
837
  if (insTime <= insOffset && insCurrentTime !== 0) {
744
838
  setAnimationsProgress(0);
745
839
  if (insReversed) countIteration();
746
840
  }
747
- if (insTime >= insDuration && insCurrentTime !== insDuration) {
841
+ if ((insTime >= insDuration && insCurrentTime !== insDuration) || !insDuration) {
748
842
  setAnimationsProgress(insDuration);
749
843
  if (!insReversed) countIteration();
750
844
  }
751
845
  }
846
+ setCallback('update');
752
847
  if (engineTime >= insDuration) {
753
848
  if (instance.remaining) {
754
849
  startTime = now;
755
850
  if (instance.direction === 'alternate') toggleInstanceDirection();
756
851
  } else {
757
852
  instance.pause();
758
- resolve();
759
- promise = makePromise();
760
853
  if (!instance.completed) {
761
854
  instance.completed = true;
762
855
  setCallback('complete');
856
+ if ('Promise' in window) {
857
+ resolve();
858
+ promise = makePromise();
859
+ }
763
860
  }
764
861
  }
765
862
  lastTime = 0;
766
863
  }
767
- if (instance.children) syncInstanceChildren(insTime);
768
- setCallback('update');
864
+ }
865
+
866
+ instance.reset = function() {
867
+ const direction = instance.direction;
868
+ const loops = instance.loop;
869
+ instance.currentTime = 0;
870
+ instance.progress = 0;
871
+ instance.paused = true;
872
+ instance.began = false;
873
+ instance.completed = false;
874
+ instance.reversed = direction === 'reverse';
875
+ instance.remaining = direction === 'alternate' && loops === 1 ? 2 : loops;
876
+ setAnimationsProgress(0);
877
+ for (let i = instance.children.length; i--; ){
878
+ instance.children[i].reset();
879
+ }
769
880
  }
770
881
 
771
882
  instance.tick = function(t) {
@@ -789,7 +900,7 @@
789
900
  if (!instance.paused) return;
790
901
  instance.paused = false;
791
902
  startTime = 0;
792
- lastTime = instance.completed ? 0 : adjustTime(instance.currentTime);
903
+ lastTime = adjustTime(instance.currentTime);
793
904
  activeInstances.push(instance);
794
905
  if (!raf) engine();
795
906
  }
@@ -820,13 +931,13 @@
820
931
 
821
932
  function removeTargets(targets) {
822
933
  const targetsArray = parseTargets(targets);
823
- for (let i = arrayLength(activeInstances)-1; i >= 0; i--) {
934
+ for (let i = activeInstances.length; i--;) {
824
935
  const instance = activeInstances[i];
825
936
  const animations = instance.animations;
826
- for (let a = arrayLength(animations)-1; a >= 0; a--) {
937
+ for (let a = animations.length; a--;) {
827
938
  if (arrayContains(targetsArray, animations[a].animatable.target)) {
828
939
  animations.splice(a, 1);
829
- if (!arrayLength(animations)) instance.pause();
940
+ if (!animations.length) instance.pause();
830
941
  }
831
942
  }
832
943
  }
@@ -836,24 +947,36 @@
836
947
 
837
948
  function timeline(params) {
838
949
  let tl = anime(params);
950
+ tl.pause();
839
951
  tl.duration = 0;
840
- tl.children = [];
841
952
  tl.add = function(instancesParams) {
842
- toArray(instancesParams).forEach(insParams => {
843
- const offset = insParams.offset;
953
+ tl.children.forEach(i => { i.began = true; i.completed = true; });
954
+ toArray(instancesParams).forEach(instanceParams => {
955
+ let insParams = mergeObjects(instanceParams, replaceObjectProps(defaultTweenSettings, params || {}));
956
+ insParams.targets = insParams.targets || params.targets;
844
957
  const tlDuration = tl.duration;
958
+ const insOffset = insParams.offset;
845
959
  insParams.autoplay = false;
846
- insParams.offset = is.und(offset) ? tlDuration : getRelativeValue(offset, tlDuration);
960
+ insParams.direction = tl.direction;
961
+ insParams.offset = is.und(insOffset) ? tlDuration : getRelativeValue(insOffset, tlDuration);
962
+ tl.began = true;
963
+ tl.completed = true;
964
+ tl.seek(insParams.offset);
847
965
  const ins = anime(insParams);
966
+ ins.began = true;
967
+ ins.completed = true;
848
968
  if (ins.duration > tlDuration) tl.duration = ins.duration;
849
969
  tl.children.push(ins);
850
970
  });
971
+ tl.seek(0);
972
+ tl.reset();
973
+ if (tl.autoplay) tl.restart();
851
974
  return tl;
852
975
  }
853
976
  return tl;
854
977
  }
855
978
 
856
- anime.version = '2.0.0';
979
+ anime.version = '2.2.0';
857
980
  anime.speed = 1;
858
981
  anime.running = activeInstances;
859
982
  anime.remove = removeTargets;