Last active
March 9, 2024 13:07
-
-
Save XerxesNoble/a52e9e430605589224bdc006eb014b38 to your computer and use it in GitHub Desktop.
Basic javascript linear value animation that can accept easing functions and provides update & complete callbacks
This file contains hidden or 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
/** | |
* @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 | |
* }) | |
* | |
*/ | |
// CodePen Example: https://codepen.io/xerxesnoble/pen/JNgmJR?editors=0011 | |
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
Looks nice! But mix of RAF and setInterval === a lot of visual lags after window browser tab back from inactive))
How to reproduce: open you example (https://codepen.io/xerxesnoble/pen/JNgmJR?editors=0010) in one browser tab, then switch to another tab and wait some seconds (3..5), after that just return to example page and look to animations