humdinger
v0.0.11
Published
Animation library by SDG to build interactive static sites
Downloads
5
Readme
Humdinger 🙈🙉🙊
A toolkit for rapidly building interactive websites
Get Started
Npm
npm install humdinger
UNPKG
<script src="https://unpkg.com/[email protected]/dist/humdinger.iife.js"></script>
Features
Project Roadmap
Watcher
The Watcher
allows you "watch" for certain events on a specific element and run callbacks based on that event.
Here are the events available through the Watcher
Methods
Log
Will log the current element being watched
const el = new Watcher('.el');
// Will console.log() the el
el.log();
Node
Will return the current element being watched
const el = new Watcher('.el');
const elementNode = el.node();
Click
Run callbacks after click events.
Callback
Each callback has access to the following properties
button.click((el) => {
// The clicked element
console.log(el);
});
el
- The clicked element
Usage
HTML
<button>Click me to change the content</button>
JS
import { Watcher } from 'humdinger';
const button = new Watcher('button');
button.click((el) => {
el.innerHTML = 'New Name';
});
Scroll
Scroll uses the Intersection Observer under the hood, to allow you to run callbacks based off certain scroll events.
Events
enter
- When an element enters the observer viewexit
- When an element leaves the observer view
Callback
Each callback has access to the following properties
const scrollWatcher = new Watcher('.element');
scrollWatcher.scroll('enter', (el => {
// Will log the DOM element of '.element'
console.log(el);
})
el
- The element that is being observed
Observer Options
Customize the Intersection Observer options. Values can be changed in an object after the callback.
// Default options
{
root: null,
rootMargin: '0px',
threshold: 1.0
}
Usage
Html
<div class="scroll-element">I am a scroll element</div>
CSS
.scroll-element {
opacity: 0;
}
.scroll-element[data-state='visible'] {
opacity: 1;
transition: opacity 0.2s;
}
JS
import { Watcher } from 'humdinger';
// You can also pass a DOM Element into the watcher
const scrollEl = document.querySelector('.scroll-element');
const scrollWatcher = new Watcher(scrollEl);
scrollWatcher.scroll(
'enter',
el) => {
el.setAttribute('data-state', 'visible');
},
{
threshold: 0.5,
}
);
scrollWatcher.scroll('exit', (el) => {
el.setAttribute('data-state', 'hidden');
});
Element Transitions
FLIP
Create native-like animations using FLIP. The flip
functions takes 3 arguments.
flip(
'.box',
(el) => {
console.log('The callback has access to the element', el);
},
{
duration: 200,
}
);
element
- The selector that you would like to animatecb
- function that can be used to adjust the layoutoptions
- an object that can be used to adjust the animation
Options
// Default options
{
duration: 300,
easing: 'ease-in-out',
direction: 'forwards',
scale: true,
done: () => {}
}
duration
- duration of animation in millisecondseasing
- ease of the animationdirection
- animation directionscale
- elements within the animating element will scale with itdone
- callback function that can be run once the animation is complete
Usage
HTML
<div class="layout">
<h2 class="heading">This is the heading</h2>
<div>This is just filler content</div>
</div>
CSS
.layout {
display: flex;
}
.layout.reverse {
flex-direction: row-reverse;
}
JS
import { flip } from 'humdinger';
flip(
'.heading',
() => {
// Adjust layout so '.heading' will switch positions
document.querySelector('.layout').classList.add('.reverse');
},
{
duration: 250,
scale: false,
done: () => {
console.log('Animation finished');
},
}
);
Crossfade
Seamlessly animate between two different element using crossfade. The crossfade
function takes 4 arguments.
// crossfade(first, last, cb, options)
crossfade('.first', '.last', (el) => {}, {});
first
- The first element in the animationlast
- That last element in the animationcb
- The callback function to handle layout change has access to thelast
element.options
- Options to adjust the animation
Options
// Default options
{
duration: 300,
easing: 'ease-in-out',
direction: 'forwards',
scale: true,
done: () => {}
}
duration
- duration of animation in millisecondseasing
- ease of the animationdirection
- animation directionscale
- elements within the animating element will scale with itdone
- callback function that can be run once the animation is complete
Usage
HTML
<img src="image.jpg" class="small-image" alt="image" />
<img src="image.jpg" class="large-image" alt="image" />
CSS
.small-image,
.large-image {
height: var(--height, 300px);
width: auto;
}
.large-image {
/* Only add this to the element that will be initially hidden */
visibility: hidden;
--height: 600px;
}
JS
import { crossfade } from 'humdinger';
crossfade(
'.small-image',
'.large-image',
(el) => {
el.style.visibility = 'visible';
},
{
duration: 500,
}
);
Animate Height Auto
The animateHeightAuto
function watches for changes to an element and animates the height.
animateHeightAuto(el, cb, options);
animateHeightAuto
has 2 arguments
el
- The element that's height will changecb
- A callback function to adjust the layoutoptions
- Animation options for the heightduration
(Number) - Duration of animation in millisecondseasing
(String) - Ease of animation
Usage
HTML
<div class="dropdown">
<div class="dropdown__item">
<button class="dropdown__toggle">Item 1</button>
<p class="dropdown__content">
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Voluptate, omnis
numquam. Cumque quae dolore sint debitis a deleniti ipsa nostrum?
</p>
</div>
<div class="dropdown__item">
<button class="dropdown__toggle">Item 2</button>
<p class="dropdown__content">
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Voluptate, omnis
numquam. Cumque quae dolore sint debitis a deleniti ipsa nostrum?
</p>
</div>
<div class="dropdown__item">
<button class="dropdown__toggle">Item 3</button>
<p class="dropdown__content">
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Voluptate, omnis
numquam. Cumque quae dolore sint debitis a deleniti ipsa nostrum?
</p>
</div>
</div>
JS
import { Watcher, animateHeightAuto } from 'humdinger';
const dropdownItems = document.querySelectorAll('.dropdown__item');
dropdownItems.forEach((item) => {
const toggleEl = item.querySelector('.dropdown__toggle');
const dropdownToggle = new Watcher(toggleEl);
const dropdownContent = item.querySelector('.dropdown__content');
dropdownToggle.click(() => {
animateHeightAuto(item, () => {
dropdownContent.classList.toggle('open');
});
});
});
CSS
.dropdown {
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.dropdown__toggle {
padding: 30px 50px;
width: 100%;
}
.dropdown__content {
--border: 1px solid black;
background: white;
border-left: var(--border);
border-right: var(--border);
border-bottom: var(--border);
font-size: 4em;
display: none;
padding: 20px;
&.open {
display: block;
}
}
Watch Height Change
The watchHeightChange
function uses the Mutation Observer to animate changes within an element's height. It's useful to use when a height change is not happening within a callback. For example a tabbed section using details
and summary
.
watchHeightChange(el, cb);
animateHeightAuto
has 2 arguments
el
- The element being watchedcb
- A callback function that is executed after the height change. It has access to an object of the following propertiesmutation
- the mutation from the observerelement
- the DOM element being animatedbeforeHeight
- the element height before the changeafterHeight
- the element height after the change
Usage
JS
import { watchHeightChange } from 'humdinger';
import { watchHeightChange } from '../../lib';
const tabElement = document.querySelector('.css-tabbable__tab');
watchHeightChange(tabElement, ({ afterHeight }) =>
console.log(`Height has changed to ${afterHeight}`)
).observe();
HTML
<section class="css-tabbable">
<details class="css-tabbable__tab">
<summary class="css-tabbable__tab__toogle">Toggle Me</summary>
<div class="css-tabbable__tab__content">
This is the inner content of the tabbable section
</div>
</details>
</section>
Mounting
The mounting functions handles showing and hiding elements, allowing users to run css animation during the process.
mount
- When you want show an elementunmount
- When you want to hide an elementtoggleMounting
- When you want to toggle the mounting of an element
Each of the functions take an element
as an argument.
Initialization
- Create an element with a
data-transition
attribute, the value you pass here will be used in the css animation.
HTML
<div class="element" data-transition="fade">Animate Me</div>
- Use on of the mounting functions to animate the element
const element = document.querySelector('.element');
mount(element);
- Running the mounting functions will take the elements
data-transition
attribute and add a data attribute with the value and current state. Use css to animate between the states
CSS
/* data-transition="fade" will create the data-fade attribute */
/* 'mount' will create the 'enter' */
[data-fade='enter'] {
opacity: 1;
transition: opacity 0.4s linear;
}
/* 'unmount' will create the 'exit' */
[data-fade='exit'] {
opacity: 0;
transition: opacity 0.4s linear;
}
Usage
HTML
<button class="toggle__show">Show</button>
<button class="toggle__hide">Hide</button>
<div class="toggle__element" data-transition="fade-up">
<h2>This is the toggle element</h2>
<p>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Delectus, maiores
at! Quia illum ipsum, minus ratione totam reprehenderit. Totam, dolorum.
Dicta dolores sint totam animi non a labore accusamus hic.
</p>
</div>
JS
import { mount, unmount, Watcher } from 'humdinger';
const toggleShow = new Watcher('.toggle__show');
const toggleHide = new Watcher('.toggle__hide');
const toggleElement = document.querySelector('.toggle__element');
toggleShow.click(() => {
mount(toggleElement);
});
toggleHide.click(() => {
unmount(toggleElement);
});
CSS
[data-fade-up='enter'] {
opacity: 1;
transform: translateY(0px);
transition: opacity 0.3s linear, transform 0.4s ease-in;
}
[data-fade-up='exit'] {
opacity: 0;
transform: translateY(10px);
transition: opacity 0.3s linear, transform 0.4s ease-out;
}
Gallery
Handy Class for building image galleries.
Initialization
- Import the
Gallery
class
import { Gallery } from 'humdinger';
- Configure: The
Gallery
class takes two arguments
element
- a string or DOM element that contains the galleryoptions
- configuration options for the galleryitemSelector
- a selector for the items in the gallery elementstart
- the starting index of the gallerytimer
- Value of timer duration in ms (ex: 5000)timerFn
- Function to run during timer
// Gallery(element, options)
const gallery = new Gallery('.gallery', {
itemSelector: '.gallery__item',
start: 0,
});
Methods
node
- returns the gallery elementinitTimer
- starts gallery timerresetTimer
- reset gallery timergetItems
- gets all items elements that are queried from the itemSelector optiongetIndex
- gets the current active indexgetCurrent
- returns the active items and indexgetNext
- returns the next item and indexgetPrevious
- returns the previous item and indexgetItem
- returns and items at a certain indexnext
- increments the current index (automatically loops)previous
- decrements the current index (automatically loops)log
- logs the gallery element, options, items, currentIndex and currentItem
Usage
HTML
<section class="hero-gallery">
<div class="hero-gallery__item" data-state="visible">
<img src="img/image-1.jpeg" alt="" class="hero-gallery__item__image" />
</div>
<div class="hero-gallery__item" data-state="hidden">
<img src="img/image-2.jpeg" alt="" class="hero-gallery__item__image" />
</div>
<button class="hero-gallery__previous">previous</button>
<button class="hero-gallery__next">next</button>
</section>
JS
import { Watcher, Gallery } from '../../lib';
const heroGallery = new Gallery('.hero-gallery', {
itemSelector: '.hero-gallery__item',
start: 0,
timer: 5000,
timerFn: nextSlide,
});
const heroGalleryNext = new Watcher('.hero-gallery__next');
const heroGalleryPrev = new Watcher('.hero-gallery__previous');
function nextSlide() {
const current = heroGallery.getCurrent().element;
const next = heroGallery.getNext().element;
current.setAttribute('data-state', 'hidden');
next.setAttribute('data-state', 'visible');
heroGallery.next();
}
function previousSlide() {
const current = heroGallery.getCurrent().element;
const prev = heroGallery.getPrevious().element;
current.setAttribute('data-state', 'hidden');
prev.setAttribute('data-state', 'visible');
heroGallery.previous();
}
heroGallery.initTimer(() => {
nextSlide();
});
heroGalleryNext.click(() => {
heroGallery.resetTimer();
nextSlide();
});
heroGalleryPrev.click(() => {
heroGallery.resetTimer();
previousSlide();
});
CSS
.hero-gallery {
height: 60vh;
position: relative;
}
.hero-gallery__item {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
opacity: 0;
transition: opacity 0.3s linear;
&[data-state='visible'] {
opacity: 1;
}
}
.hero-gallery__item img {
height: 100%;
width: 100%;
object-fit: cover;
}
.hero-gallery__previous,
.hero-gallery__next {
--spacing: 2%;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.hero-gallery__previous {
left: var(--spacing);
}
.hero-gallery__next {
right: var(--spacing);
}
Combining Features
Features can be chained together to create different interactions
FLIP on scroll
HTML
<div class="parent">
<div class="box"></div>
<p>Lorem ipsum dolor</p>
</div>
CSS
.parent {
display: flex;
}
.parent.switch {
flex-direction: row-reverse;
}
.box {
height: 300px;
width: 300px;
}
JS
import { Watcher, flip } from 'humdinger';
const parentEl = new Watcher('.parent');
parentEl.scroll('enter', (parent) => {
flip('.box', () => {
parent.classList.add('switch');
});
});