Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
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

This comment has been minimized.

Show comment Hide comment
@euroflock

euroflock Oct 30, 2017

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

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