Skip to content

Instantly share code, notes, and snippets.

@XerxesNoble
Last active March 9, 2024 13:07
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save XerxesNoble/a52e9e430605589224bdc006eb014b38 to your computer and use it in GitHub Desktop.
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
/**
* @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()
}
@euroflock
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment