Skip to content

Instantly share code, notes, and snippets.

@kotarok
Last active August 16, 2025 09:37
Show Gist options
  • Select an option

  • Save kotarok/050dd7fa9d97016b8981967842368229 to your computer and use it in GitHub Desktop.

Select an option

Save kotarok/050dd7fa9d97016b8981967842368229 to your computer and use it in GitHub Desktop.
Animate CSS classes with easing and timing control
/**
* Animate CSS classes with easing and timing control
* @param {string} selector - CSS selector
* @param {Object} [options] - Animation options
* @param {number} [options.duration=0] - Total animation duration (ms)
* @param {string} [options.easing='easeOut'] - Easing function: 'linear', 'easeIn', 'easeOut', 'easeInOut'
* @param {boolean} [options.debug=false] - Debug mode
*/
const animateClass = (selector, options = {}) => new ClassAnimator(selector, options)
class ClassAnimator {
constructor(selector, { duration = 0, easing = 'easeOut', debug = false } = {}) {
this.elements = document.querySelectorAll(selector)
this.duration = duration
this.easing = easing
this.debug = debug
if (debug) console.log(`ClassAnimator: Found ${this.elements.length} elements`)
}
add(classes) {
return this.run('add', classes)
}
remove(classes) {
return this.run('remove', classes)
}
toggle(className) {
return this.run('toggle', className)
}
replace(oldClass, newClass) {
return this.run('replace', { oldClass, newClass })
}
each(callback) {
return this.run('each', callback)
}
run(action, data) {
const delays = this.duration > 0 ? this.getDelays() : Array.from(this.elements, () => 0)
delays.forEach((delay, i) => {
setTimeout(() => this.apply(this.elements[i], action, data, i), delay)
})
return this
}
apply(el, action, data, index) {
const actions = {
add: () => el.classList.add(...(Array.isArray(data) ? data : [data])),
remove: () =>
data instanceof RegExp
? el.classList.remove(...[...el.classList].filter((cls) => data.test(cls)))
: el.classList.remove(...(Array.isArray(data) ? data : [data])),
toggle: () => el.classList.toggle(data),
replace: () => {
el.classList.remove(data.oldClass)
el.classList.add(data.newClass)
},
each: () => data(el, index),
}
actions[action]?.()
if (this.debug) console.log(`ClassAnimator: Applied ${action} to element ${index + 1}`)
}
getDelays() {
const count = this.elements.length
const easings = {
linear: (t) => t,
easeIn: (t) => t * t,
easeOut: (t) => 1 - (1 - t) ** 2,
easeInOut: (t) => (t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) ** 2),
}
const ease = easings[this.easing] || easings.linear
return Array.from(
{ length: count },
(_, i) => ease(count === 1 ? 0 : i / (count - 1)) * this.duration
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment