Created
November 28, 2018 20:34
-
-
Save DigitalKrony/6086fd6f3b7cff300f1bd9fe8fdcc1de to your computer and use it in GitHub Desktop.
JS animation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Found HERE: https://codepen.io/xerxesnoble/pen/JNgmJR | |
* @desc Basic linear value animation that can accept simple easing functions and provides update & complete callbacks | |
* @param {Object} values - Object with numerical values. eg. { value1: 0, value2: 20, someKey: 55 } | |
* @param {Number} duration - How long (in milliseconds) the animation will be | |
* @param {Object} options - target values, update callback & complete callback | |
* @param {Function} [options.onComplete=(values) => values] - Callback that fires once animation is complete | |
* @param {Function} [options.onUpdate=(values) => values] - Callback that fires when animation frame updates | |
* @param {Function} [options.ease=(t) => t] - easing method eg. https://gist.github.com/gre/1650294 | |
* @example | |
const a = document.getElementById('a') | |
animateValues({ a: 0 }, 800, { | |
a: 500, | |
onUpdate: v => a.style.transform = 'scaleX('+ v.a +')', | |
onComplete: v => alert('Done!'), | |
ease: t => t<.5 ? 2*t*t : -1+(4-2*t)*t, // From: https://gist.github.com/gre/1650294 | |
}) | |
*/ | |
function animateValues(values, duration, options) { | |
// Linear interpolation | |
const lerp = (source, target, amount) => source + amount * (target - source) | |
// Validation methods | |
const checkNum = n => typeof n === 'number' ? n : null | |
const checkFunc = f => typeof f === 'function' ? f : _ => _ | |
// Ensure methods. | |
const onComplete = checkFunc(options.onComplete) | |
const onUpdate = checkFunc(options.onUpdate) | |
const ease = checkFunc(options.ease) | |
// Animation start time | |
const start = Date.now() | |
// Create a map <key: [from, to]> | |
const animationMap = Object.keys(values).reduce((map, key) => { | |
const _from = checkNum(values[key]) | |
const _to = checkNum(options[key]) | |
if (_from !== null && _to !== null) map[key] = [_from, _to] | |
return map | |
}, {}) | |
// List of animating values | |
const keys = Object.keys(animationMap) | |
// Create & run animation function | |
const animation = () => { | |
const now = Date.now() | |
let t = duration > 0 ? (now - start) / duration : 1 | |
// Update all values using 't' | |
keys.forEach(key => { | |
// If both 'from' and 'to' are numbers: animate! | |
const [_from, _to] = animationMap[key] | |
const progress = ease(t, _from, _to, duration) | |
// Update value | |
values[key] = lerp(_from, _to, progress) | |
}) | |
// If complete.. | |
if (t >= 1) { | |
// Final update for all keys | |
keys.forEach(key => (values[key] = options[key])) | |
onUpdate(values) | |
onComplete(values) | |
} else { | |
// Run update callback and loop until finished | |
onUpdate(values) | |
requestAnimationFrame(animation) | |
} | |
} | |
animation() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment