Skip to content

A tiny promise based animation function implemented in vanilla JavaScript

License

Notifications You must be signed in to change notification settings

phegman/animate-vanilla-js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

84da313 · Dec 23, 2019

History

36 Commits
Jul 28, 2019
Jul 29, 2019
Jul 24, 2019
Sep 4, 2019
Jul 24, 2019
Jul 24, 2019
Jul 29, 2019
Jul 24, 2019
Jul 29, 2019
Dec 22, 2019
Jul 28, 2019
Jul 24, 2019
Dec 22, 2019

Repository files navigation

Animate Vanilla JS

A tiny promise based animation function implemented in vanilla JavaScript.

  • 👻 3kb (1kb gzipped)
  • 📦 No dependencies
  • 🌚 TypeScript support
  • 🕺 Multiple built-in easings
  • 🤝 Promise based
  • Uses requestAnimationFrame
  • 🙅‍♂️ Cancelable

Installation

Module bundler (e.g. Webpack)

yarn add animate-vanilla-js
import animate from 'animate-vanilla-js'

Script tag

Download animate-vanilla-js-browser.js from the latest release here: https://github.com/phegman/animate-vanilla-js/releases

<script type="text/javascript" src="animate-vanilla-js-browser.js"></script>

The animation function will then be usable via the global function animateVanillaJs()

Usage

Parameters

Parameter Type Description Options
from number Starting animation value N/A
to number Ending animation value N/A
duration number Animation duration N/A
easing string || EasingFunction Easing linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic, easeInQuart, easeOutQuart, easeInOutQuart, easeInQuint, easeOutQuint, easeInOutQuint
update Function Function with animation value passed as parameter N/A

Basic Usage

import animate from 'animate-vanilla-js'

animate(
  0, // from
  100, // to
  500, // duration
  'easeInOutQuad', // easing
  value => {
    // update function
    console.log(value)
    // Perform DOM manipulation
  }
).then(() => {
  console.log('Done animating')
})

Canceling animations

import animate from 'animate-vanilla-js'

const promise = animate(
  0, // from
  100, // to
  500, // duration
  'easeInOutQuad', // easing
  value => {
    // update function
    console.log(value)
    // Perform DOM manipulation
  }
)

document.addEventListener('keydown', event => {
  event = event || window.event
  // Escape key is pressed
  if (event.keyCode === 27) {
    promise.cancel()
  }
})

promise.then(() => {
  console.log('Done animating')
})

Custom easing

import animate from 'animate-vanilla-js'

animate(
  0, // from
  100, // to
  500, // duration
  (t, b, c, d) => (c * t) / d + b, // easing function
  value => {
    // update function
    console.log(value)
    // Perform DOM manipulation
  }
).then(() => {
  console.log('Done animating')
})

Custom Easing Function

Parameter Type Description
t number Current time
b number Beginning value
c number Change in value
d number Duration

Examples

Animate to height: auto

const el = document.getElementById('el')
const toggleButton = document.getElementById('toggle-button')

toggleButton.addEventListener('click', () => {
  if (el.classList.contains('open')) {
    animate(el.scrollHeight, 0, 500, 'easeInOutQuad', value => {
      el.style.height = `${value}px`
    }).then(() => {
      el.style.cssText = ''
      el.classList.remove('open')
      el.setAttribute('aria-hidden', 'true')
      toggleButton.setAttribute('aria-expanded', 'false')
    })
  } else {
    animate(0, el.scrollHeight, 500, 'easeInOutQuad', value => {
      el.style.height = `${value}px`
    }).then(() => {
      el.style.cssText = ''
      el.classList.add('open')
      el.setAttribute('aria-hidden', 'false')
      toggleButton.setAttribute('aria-expanded', 'true')
    })
  }
})

Smooth Scroll

function addEventListenerMulti(
  element: Element | NodeList,
  listeners: string,
  callback: (arg0: Event) => void
) {
  function addListeners(el: Element) {
    listeners
      .split(' ')
      .forEach(listener => el.addEventListener(listener, callback))
  }

  if (NodeList.prototype.isPrototypeOf(element)) {
    ;[...(<NodeList>element)].forEach(addListeners)
  } else {
    addListeners(<Element>element)
  }
}

function removeEventListenerMulti(
  element: Element | NodeList,
  listeners: string,
  callback: (arg0: Event) => void
) {
  function removeListeners(el: Element) {
    listeners
      .split(' ')
      .forEach(listener => el.removeEventListener(listener, callback))
  }

  if (NodeList.prototype.isPrototypeOf(element)) {
    ;[...(<NodeList>element)].forEach(removeListeners)
  } else {
    removeListeners(<Element>element)
  }
}

const button = document.querySelector('.scroll-button')
const scrollAnchor = document.getElementById('scroll-anchor')

button.addEventListener('click', () => {
  const currentScrollPosition =
    (document.documentElement && document.documentElement.scrollTop) ||
    document.body.scrollTop
  const scrollAnchorTop =
    scrollAnchor.getBoundingClientRect().top + currentScrollPosition

  const promise = animate(
    currentScrollPosition,
    scrollAnchorTop,
    500,
    'easeInQuad',
    value => {
      window.scrollTo(0, value)
    }
  )

  promise.then(() => {
    // Try to focus on element
    scrollAnchor.focus()

    // If that element was not able to be focused, set tabindex and then refocus
    if (document.activeElement !== scrollAnchor) {
      scrollAnchor.tabIndex = -1
      scrollAnchor.focus()
      // We can hide the outline here because normally this element wouldn't be focusable.
      // We made it focusable so the tab order could be set correctly
      scrollAnchor.style.outline = 'none'
    }

    cancelScroll()
  })

  addEventListenerMulti(
    document.querySelectorAll('html, body'),
    'scroll mousedown wheel DOMMouseScroll mousewheel touchmove',
    cancelScroll
  )

  function cancelScroll() {
    promise.cancel()
    removeEventListenerMulti(
      document.querySelectorAll('html, body'),
      'scroll mousedown wheel DOMMouseScroll mousewheel touchmove',
      cancelScroll
    )
  }
})

Browser Support

IE / Edge
IE / Edge
Firefox
Firefox
Chrome
Chrome
Safari
Safari
iOS Safari
iOS Safari
IE11, > Edge 12 > 23 > 24 > 6.1 > 7.1

Development

Start development server with hot module reloading

yarn serve

Build project for production

yarn build