Skip to content

Commit

Permalink
Merge branch '2.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
juliangarnier committed Sep 25, 2017
2 parents a2d2b4c + b14a3a9 commit ae9eb90
Show file tree
Hide file tree
Showing 16 changed files with 2,475 additions and 298 deletions.
164 changes: 109 additions & 55 deletions anime.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* http://animejs.com
* JavaScript animation engine
* @version v2.1.0
* @version v2.2.0
* @author Julian Garnier
* @copyright ©2017 Julian Garnier
* Released under the MIT license
Expand Down Expand Up @@ -55,6 +55,7 @@
const is = {
arr: a => Array.isArray(a),
obj: a => stringContains(Object.prototype.toString.call(a), 'Object'),
pth: a => is.obj(a) && a.hasOwnProperty('totalLength'),
svg: a => a instanceof SVGElement,
dom: a => a.nodeType || is.svg(a),
str: a => typeof a === 'string',
Expand Down Expand Up @@ -229,6 +230,21 @@

// Arrays

function filterArray(arr, callback) {
const len = arr.length;
const thisArg = arguments.length >= 2 ? arguments[1] : void 0;
let result = [];
for (let i = 0; i < len; i++) {
if (i in arr) {
const val = arr[i];
if (callback.call(thisArg, val, i, arr)) {
result.push(val);
}
}
}
return result;
}

function flattenArray(arr) {
return arr.reduce((a, b) => a.concat(is.arr(b) ? flattenArray(b) : b), []);
}
Expand All @@ -246,10 +262,6 @@

// Objects

function objectHas(obj, prop) {
return obj.hasOwnProperty(prop);
}

function cloneObject(o) {
let clone = {};
for (let p in o) clone[p] = o[p];
Expand All @@ -258,7 +270,7 @@

function replaceObjectProps(o1, o2) {
let o = cloneObject(o1);
for (let p in o1) o[p] = objectHas(o2, p) ? o2[p] : o1[p];
for (let p in o1) o[p] = o2.hasOwnProperty(p) ? o2[p] : o1[p];
return o;
}

Expand All @@ -270,21 +282,27 @@

// Colors

function hexToRgb(hexValue) {
function rgbToRgba(rgbValue) {
const rgb = /rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(rgbValue);
return rgb ? `rgba(${rgb[1]},1)` : rgbValue;
}

function hexToRgba(hexValue) {
const rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
const hex = hexValue.replace(rgx, (m, r, g, b) => r + r + g + g + b + b );
const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
const r = parseInt(rgb[1], 16);
const g = parseInt(rgb[2], 16);
const b = parseInt(rgb[3], 16);
return `rgb(${r},${g},${b})`;
return `rgba(${r},${g},${b},1)`;
}

function hslToRgb(hslValue) {
const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue);
function hslToRgba(hslValue) {
const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
const h = parseInt(hsl[1]) / 360;
const s = parseInt(hsl[2]) / 100;
const l = parseInt(hsl[3]) / 100;
const a = hsl[4] || 1;
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
Expand All @@ -303,13 +321,13 @@
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return `rgb(${r * 255},${g * 255},${b * 255})`;
return `rgba(${r * 255},${g * 255},${b * 255},${a})`;
}

function colorToRgb(val) {
if (is.rgb(val)) return val;
if (is.hex(val)) return hexToRgb(val);
if (is.hsl(val)) return hslToRgb(val);
if (is.rgb(val)) return rgbToRgba(val);
if (is.hex(val)) return hexToRgba(val);
if (is.hsl(val)) return hslToRgba(val);
}

// Units
Expand Down Expand Up @@ -361,7 +379,7 @@
props.push(match[1]);
values.push(match[2]);
}
const value = values.filter((val, i) => props[i] === propName );
const value = filterArray(values, (val, i) => props[i] === propName);
return value.length ? value[0] : defaultVal;
}

Expand Down Expand Up @@ -454,10 +472,6 @@

// Motion path

function isPath(val) {
return is.obj(val) && objectHas(val, 'totalLength');
}

function getPath(path, percent) {
const el = is.str(path) ? selectString(path)[0] : path;
const p = percent || 100;
Expand Down Expand Up @@ -485,27 +499,23 @@
}
}

// Decompose / recompose functions adapted from Animate Plus https://github.com/bendc/animateplus
// Decompose value

function decomposeValue(val, unit) {
const rgx = /-?\d*\.?\d+/g;
const value = validateValue((isPath(val) ? val.totalLength : val), unit) + '';
const value = validateValue((is.pth(val) ? val.totalLength : val), unit) + '';
return {
original: value,
numbers: value.match(rgx) ? value.match(rgx).map(Number) : [0],
strings: (is.str(val) || unit) ? value.split(rgx) : []
}
}

function recomposeValue(numbers, strings) {
return (strings.length === 0) ? numbers[0] : strings.reduce((a, b, i) => a + numbers[i - 1] + (b ? b : ' '));
}

// Animatables

function parseTargets(targets) {
const targetsArray = targets ? (flattenArray(is.arr(targets) ? targets.map(toArray) : toArray(targets))) : [];
return targetsArray.filter((item, pos, self) => self.indexOf(item) === pos);
return filterArray(targetsArray, (item, pos, self) => self.indexOf(item) === pos);
}

function getAnimatables(targets) {
Expand Down Expand Up @@ -534,7 +544,7 @@
// Default delay value should be applied only on the first tween
const delay = !i ? tweenSettings.delay : 0;
// Use path object as a tween value
let obj = is.obj(v) && !isPath(v) ? v : {value: v};
let obj = is.obj(v) && !is.pth(v) ? v : {value: v};
// Set default delay value
if (is.und(obj.delay)) obj.delay = delay;
return obj;
Expand All @@ -545,7 +555,7 @@
let properties = [];
const settings = mergeObjects(instanceSettings, tweenSettings);
for (let p in params) {
if (!objectHas(settings, p) && p !== 'targets') {
if (!settings.hasOwnProperty(p) && p !== 'targets') {
properties.push({
name: p,
offset: settings['offset'],
Expand Down Expand Up @@ -587,14 +597,15 @@
const from = is.arr(tweenValue) ? tweenValue[0] : previousValue;
const to = getRelativeValue(is.arr(tweenValue) ? tweenValue[1] : tweenValue, from);
const unit = getUnit(to) || getUnit(from) || getUnit(originalValue);
tween.isPath = isPath(tweenValue);
tween.from = decomposeValue(from, unit);
tween.to = decomposeValue(to, unit);
tween.start = previousTween ? previousTween.end : prop.offset;
tween.end = tween.start + tween.delay + tween.duration;
tween.easing = normalizeEasing(tween.easing);
tween.elasticity = (1000 - minMaxValue(tween.elasticity, 1, 999)) / 1000;
if (is.col(tween.from.original)) tween.round = 1;
tween.isPath = is.pth(tweenValue);
tween.isColor = is.col(tween.from.original);
if (tween.isColor) tween.round = 1;
previousTween = tween;
return tween;
});
Expand Down Expand Up @@ -630,18 +641,22 @@
}

function getAnimations(animatables, properties) {
return flattenArray(animatables.map(animatable => {
return filterArray(flattenArray(animatables.map(animatable => {
return properties.map(prop => {
return createAnimation(animatable, prop);
});
})).filter(a => !is.und(a));
})), a => !is.und(a));
}

// Create Instance

function getInstanceTimings(type, animations, tweenSettings) {
const math = (type === 'delay') ? Math.min : Math.max;
return animations.length ? math.apply(Math, animations.map(anim => anim[type])) : tweenSettings[type];
function getInstanceTimings(type, animations, instanceSettings, tweenSettings) {
const isDelay = (type === 'delay');
if (animations.length) {
return (isDelay ? Math.min : Math.max).apply(Math, animations.map(anim => anim[type]));
} else {
return isDelay ? tweenSettings.delay : instanceSettings.offset + tweenSettings.delay + tweenSettings.duration;
}
}

function createNewInstance(params) {
Expand All @@ -654,8 +669,8 @@
children: [],
animatables: animatables,
animations: animations,
duration: getInstanceTimings('duration', animations, tweenSettings),
delay: getInstanceTimings('delay', animations, tweenSettings)
duration: getInstanceTimings('duration', animations, instanceSettings, tweenSettings),
delay: getInstanceTimings('delay', animations, instanceSettings, tweenSettings)
});
}

Expand Down Expand Up @@ -710,38 +725,76 @@

function syncInstanceChildren(time) {
const children = instance.children;
const childrenLength = children.length;
if (time >= instance.currentTime) {
for (let i = 0; i < children.length; i++) children[i].seek(time);
for (let i = 0; i < childrenLength; i++) children[i].seek(time);
} else {
for (let i = children.length; i--;) children[i].seek(time);
for (let i = childrenLength; i--;) children[i].seek(time);
}
}

function setAnimationsProgress(insTime) {
let i = 0;
let transforms = {};
const animations = instance.animations;
while (i < animations.length) {
const animationsLength = animations.length;
while (i < animationsLength) {
const anim = animations[i];
const animatable = anim.animatable;
const tweens = anim.tweens;
const tween = tweens.filter(t => (insTime < t.end))[0] || tweens[tweens.length - 1];
const tweenLength = tweens.length - 1;
let tween = tweens[tweenLength];
// Only check for keyframes if there is more than one tween
if (tweenLength) tween = filterArray(tweens, t => (insTime < t.end))[0] || tween;
const elapsed = minMaxValue(insTime - tween.start - tween.delay, 0, tween.duration) / tween.duration;
const eased = isNaN(elapsed) ? 1 : tween.easing(elapsed, tween.elasticity);
const strings = tween.to.strings;
const round = tween.round;
const progress = recomposeValue(tween.to.numbers.map((number, p) => {
const start = tween.from.numbers[p];
let value = start + eased * (number - start);
if (tween.isPath) value = getPathProgress(tween.value, value);
if (round) value = Math.round(value * round) / round;
return value;
}), tween.to.strings);
let numbers = [];
let progress;
const toNumbersLength = tween.to.numbers.length;
for (let n = 0; n < toNumbersLength; n++) {
let value;
const toNumber = tween.to.numbers[n];
const fromNumber = tween.from.numbers[n];
if (!tween.isPath) {
value = fromNumber + (eased * (toNumber - fromNumber));
} else {
value = getPathProgress(tween.value, eased * toNumber);
}
if (round) {
if (!(tween.isColor && n > 2)) {
value = Math.round(value * round) / round;
}
}
numbers.push(value);
}
// Manual Array.reduce for better performances
const stringsLength = strings.length;
if (!stringsLength) {
progress = numbers[0];
} else {
progress = strings[0];
for (let s = 0; s < stringsLength; s++) {
const a = strings[s];
const b = strings[s + 1];
const n = numbers[s];
if (!isNaN(n)) {
if (!b) {
progress += n + ' ';
} else {
progress += n + b;
}
}
}
}
setTweenProgress[anim.type](animatable.target, anim.property, progress, transforms, animatable.id);
anim.currentValue = progress;
i++;
}
if (transforms) {
let id; for (id in transforms) {
const transformsLength = Object.keys(transforms).length;
if (transformsLength) {
for (let id = 0; id < transformsLength; id++) {
if (!transformString) {
const t = 'transform';
transformString = (getCSSValue(document.body, t) ? t : `-webkit-${t}`);
Expand All @@ -766,17 +819,17 @@
function setInstanceProgress(engineTime) {
const insDuration = instance.duration;
const insOffset = instance.offset;
const insDelay = instance.delay;
const insStart = insOffset + instance.delay;
const insCurrentTime = instance.currentTime;
const insReversed = instance.reversed;
const insTime = adjustTime(engineTime);
if (instance.children.length) syncInstanceChildren(insTime);
if (insTime >= insDelay) {
setCallback('run');
if (insTime >= insStart || !insDuration) {
if (!instance.began) {
instance.began = true;
setCallback('begin');
}
setCallback('run');
}
if (insTime > insOffset && insTime < insDuration) {
setAnimationsProgress(insTime);
Expand All @@ -785,7 +838,7 @@
setAnimationsProgress(0);
if (insReversed) countIteration();
}
if (insTime >= insDuration && insCurrentTime !== insDuration) {
if ((insTime >= insDuration && insCurrentTime !== insDuration) || !insDuration) {
setAnimationsProgress(insDuration);
if (!insReversed) countIteration();
}
Expand Down Expand Up @@ -904,6 +957,7 @@
const tlDuration = tl.duration;
const insOffset = insParams.offset;
insParams.autoplay = false;
insParams.direction = tl.direction;
insParams.offset = is.und(insOffset) ? tlDuration : getRelativeValue(insOffset, tlDuration);
tl.began = true;
tl.completed = true;
Expand All @@ -922,7 +976,7 @@
return tl;
}

anime.version = '2.1.0';
anime.version = '2.2.0';
anime.speed = 1;
anime.running = activeInstances;
anime.remove = removeTargets;
Expand Down

0 comments on commit ae9eb90

Please sign in to comment.