Ever wished you had an easy way to animate when you move an element from one place in the DOM to another?
Ever wanted to move other elements out of the way smoothly when you add a new one?
FlippyJS lets you animate DOM changes in the easiest way possible: give it some elements to animate, then change the DOM however you want, and FlippyJS will figure out the rest.
If you’d like to see how the below demos look without using FlippyJS, simply toggle this checkbox:
An element is moved in and out of a flexbox column.
This is about as simple as DOM changes get, but is very difficult to animate without FLIP animations.
1
2
3
4
5
6
7
8
9
<div class="column">
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
</div>
<div class="column">
<div id="flexbox-columns-movable" class="module large interactive"></div>
<div class="module"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let $elm = document.getElementById("flexbox-columns-movable");
let $parents = [
".flexbox-columns",
".flexbox-columns .column:nth-child(2)"
].map(elm=>document.querySelector(elm));
$elm.addEventListener("click", ()=>{
// when the element is clicked, animate the move
flip(
".flexbox-columns .module", // elements to animate
()=>{ // function which makes the DOM change
let $parent = $parents[0];
if ($elm.parentNode === $parents[0]) {
$parent = $parents[1];
}
$parent.insertBefore(
$elm,
$parent.firstChild
);
},
{ // options object
ease: "cubic-bezier(0.4, 2.2, 0.5, 0.7)"
}
);
});
You might have seen this kind of animation on YouTube or Twitch.
A video is moved from its position in the normal flow of the page to a fixed position.
Since FlippyJS uses transforms to animate, the video will continue playing as it’s moved.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<video autoplay loop muted
id="twitch-minimize-video"
class="minimizable"
src="/flippy.js/assets/video/train.mp4">
Your browser doesn't seem to support videos :(
</video>
<div id="twitch-minimize-video-placeholder" class="placeholder"></div>
<p class="placeholder">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.minimizable {
cursor: pointer;
&.minimized {
width: 150px;
position: fixed;
bottom: 10px;
left: 10px;
}
}
#twitch-minimize-video-placeholder {
display: none;
.minimized + & {
display: block;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// the placeholder makes sure other content on the
// page isn't moved up when we minimize the video
let $video = document.getElementById("twitch-minimize-video");
let $placeholder = document.getElementById("twitch-minimize-video-placeholder");
$video.addEventListener("loadeddata", ()=>{
$placeholder.style.width = $video.clientWidth+"px";
$placeholder.style.height = $video.clientHeight+"px";
});
// toggle minimized when the video is clicked
$video.addEventListener("click", ()=>{
flip(
$video,
()=>{
$video.classList.toggle("minimized");
}
);
});
If elements are added to the DOM, FlippyJS only animates their size (from 0), not their position.
1
2
3
4
5
6
<div class="column">
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let $column = document.querySelector(".adding-elements .column");
setInterval(addModule, 2000);
function addModule() {
let $module = document.createElement("div");
$module.classList.add("module");
flip(
[$module, ...document.querySelectorAll(".adding-elements .module")],
()=>{
// add our new module to the column
$column.insertBefore(
$module,
$column.firstChild
);
// also remove the last module in the column
$column.removeChild(
$column.lastElementChild
);
},
{
duration: 600
}
)
}
There are two ways to listen for animation end: wait for the returned Promise
to resolve, or add the callback
property to the options.
1
<div class="module square absolute"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
let $module = document.querySelector(".listening-for-animation-end .module");
let index = -1;
let baseState = {
left: "", top: "",
right: "", bottom: "",
transform: ""
};
let states = [
{
left: "50px", top: "50px",
transform: "rotate(45deg)"
},
{
left: "50px", bottom: "50px"
},
{
right: "50px", bottom: "50px",
transform: "scale(0.5)"
},
{
top: "50px", right: "50px"
}
];
function animate() {
if (index === states.length-1) {
index = -1;
}
flip(
$module,
()=>{
let state = states[++index];
for (let prop in baseState) {
if (state.hasOwnProperty(prop)) {
$module.style[prop] = state[prop];
} else {
$module.style[prop] = baseState[prop];
}
}
}
).then(animate);
}
document.addEventListener("DOMContentLoaded", animate);