<main>
<header>
<h1>Let's try Flip by GreenSock</h1>
<p>In this CodePen, I use Flip to animate changes in the <code>grid</code> property. I also tried to alter the initial DOM structure, by moving an element to the top of the list. What about sorting? Let's display the items in the <button type="button" id="alpha-order" class="button-inline">alphabetical order.</button></p>
</header>
<div class="grid">
<article class="article article-1">
<a href="#">
<img src="https://images.unsplash.com/photo-1429087969512-1e85aab2683d?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0MDc5NDgwMA&ixlib=rb-1.2.1&q=85&w=600" alt="">
</a>
<section class="description">
<h2>Dearest Diary</h2>
<div class="details-view">
<p>Ginger is a flowering plant whose rhizome, ginger root or ginger, is widely used as a spice and a folk medicine.</p>
<button type="button">Move to top</button>
</div>
</section>
</article>
<article class="article article-2">
<a href="#">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/74321/fbanijhrol4-annie-spratt-776x951.jpg" alt="">
</a>
<section class="description">
<h2>Window Sill?</h2>
<div class="details-view">
<p>Ginger is a flowering plant whose rhizome, ginger root or ginger, is widely used as a spice and a folk medicine.</p>
<button type="button">Move to top</button>
</div>
</section>
</article>
<article class="article article-3">
<a href="#">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/74321/2rm8p0rkxiw-marius-masalar-776x582.jpg" alt="">
</a>
<section class="description">
<h2>Listen To Me</h2>
<div class="details-view">
<p>Ginger is a flowering plant whose rhizome, ginger root or ginger, is widely used as a spice and a folk medicine.</p>
<button type="button">Move to top</button>
</div>
</section>
</article>
<article class="article article-4">
<a href="#">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/74321/71nlan-2ya-andrew-neel-2-776x620.jpg" alt="">
</a>
<section class="description">
<h2>Travel Often</h2>
<div class="details-view">
<p>Ginger is a flowering plant whose rhizome, ginger root or ginger, is widely used as a spice and a folk medicine.</p>
<button type="button">Move to top</button>
</div>
</section>
</article>
<article class="details article article-5" style="--max:1">
<a href="#">
<img src="https://images.unsplash.com/photo-1483794344563-d27a8d18014e?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0MDg2NDUzOA&ixlib=rb-1.2.1&q=85&w=600" alt="">
</a>
<section class="description">
<h2>Another Plant?</h2>
<div class="details-view" style="opacity: 1; transform: translateY(0)">
<p>Ginger is a flowering plant whose rhizome, ginger root or ginger, is widely used as a spice and a folk medicine.</p>
<button type="button">Move to top</button>
</div>
</section>
</article>
<article class="article article-6">
<a href="#">
<img src="https://images.unsplash.com/photo-1557180295-76eee20ae8aa?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0MDc5NDc2Mw&ixlib=rb-1.2.1&q=85&w=600" alt="">
</a>
<section class="description">
<h2>On the Wave</h2>
<div class="details-view">
<p>Ginger is a flowering plant whose rhizome, ginger root or ginger, is widely used as a spice and a folk medicine.</p>
<button type="button">Move to top</button>
</div>
</section>
</article>
<article class="article article-7">
<a href="#">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/74321/typewriter-1-776x968.jpg" alt="">
</a>
<section class="description">
<h2>Great Gatsby</h2>
<div class="details-view">
<p>Ginger is a flowering plant whose rhizome, ginger root or ginger, is widely used as a spice and a folk medicine.</p>
<button type="button">Move to top</button>
</div>
</section>
</article>
<article class="article article-8">
<a href="#">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/74321/xohlruw4k8-christelle-bourgeois-776x758.jpg" alt="">
</a>
<section class="description">
<h2>In the Sun</h2>
<div class="details-view">
<p>Ginger is a flowering plant whose rhizome, ginger root or ginger, is widely used as a spice and a folk medicine.</p>
<button type="button">Move to top</button>
</div>
</section>
</article>
</div>
</main>
:root {
--base-color: #3f3352;
--overlayH: 263;
--overlayS: 23%;
--overlayL: 26%;
}
* {
box-sizing: border-box;
}
body {
color: var(--base-color);
padding: 1rem;
margin: 0;
min-height: 110vh;
line-height: 1.6;
font-family: muli, sans-serif;
font-weight: 300;
font-style: italic;
font-size: 1.125rem;
}
h2,
p {
margin-top: 0;
}
h1,
h2 {
font-family: eloquent-jf-pro, serif;
font-weight: 400;
line-height: 1.125;
}
h2 {
font-size: 1.25rem;
}
button {
font-family: inherit;
background: hsl(var(--overlayH), var(--overlayS), var(--overlayL));
color: white;
border: none;
padding: 0.5rem 0.75rem;
text-transform: uppercase;
cursor: pointer;
}
.button-inline {
padding: 0;
display: inline;
background: transparent;
color: inherit;
text-decoration: underline;
}
main {
visibility: hidden;
}
main {
max-width: 1400px;
margin: 1.5rem auto;
display: grid;
gap: 1rem;
}
@media (min-width: 800px) {
main {
grid-template-columns: 1fr 3fr;
gap: 2rem;
}
}
img,
a {
display: block;
height: 100%;
}
article a {
position: absolute;
left: 0;
right: 0;
}
img {
max-width: 100%;
width: 100%;
object-fit: cover;
}
.grid {
display: grid;
grid-gap: 1.5rem;
list-style: none;
margin: 0;
padding: 0;
grid-template-columns: 1fr 1fr;
grid-auto-rows: minmax(16rem, max-content);
position: relative;
}
@media (min-width: 1024px) {
.grid {
grid-template-columns: 1fr 1fr 1fr;
}
}
.grid article {
--max: 0;
position: relative;
overflow: hidden;
}
article:not(.details) button,
article:first-child button {
display: none;
}
article:first-child p:last-of-type {
margin: 0;
}
.description {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: clamp(1rem, 2.5vmin, 2.25rem);
}
article.details {
grid-row: span 2;
grid-column: span 2;
}
.description {
background: linear-gradient(
0deg,
hsl(var(--overlayH), var(--overlayS), var(--overlayL), calc(1 * var(--max)))
0%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.738 * var(--max))
)
19%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.541 * var(--max))
)
34%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.382 * var(--max))
)
47%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.278 * var(--max))
)
56.5%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.194 * var(--max))
)
65%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.126 * var(--max))
)
73%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.075 * var(--max))
)
80.2%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.042 * var(--max))
)
86.1%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.021 * var(--max))
)
91%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.008 * var(--max))
)
95.2%,
hsla(
var(--overlayH),
var(--overlayS),
var(--overlayL),
calc(0.002 * var(--max))
)
98.2%,
hsla(var(--overlayH), var(--overlayS), var(--overlayL), 0) 100%
);
}
.details-view {
color: white;
opacity: 0;
transform: translateY(100%);
}
.article-6 {
--overlayH: 47;
--overlayS: 92%;
--overlayL: 32%;
}
.article-2 {
--overlayH: 353;
--overlayS: 34%;
--overlayL: 58%;
}
.article-5 {
--overlayH: 136;
--overlayS: 62%;
--overlayL: 25%;
--overlayH: 176;
--overlayS: 49%;
--overlayL: 16%;
}
/*
This week we're looking at the FLIP technique. It can take a while to click - but when it does - you'll be able to handle any complex UI animation with ease.
Here's a simple FLIP animation to start you off!
*/
// start by registering the plugin
gsap.registerPlugin(Flip);
// grab our element
const articles = document.querySelectorAll("article");
const buttons = document.querySelectorAll(".grid button");
const alphaButton = document.querySelector("#alpha-order");
const container = document.querySelector(".grid");
for (let article of [...articles]) {
article.addEventListener("click", (e) => {
e.preventDefault();
if (article.classList.contains("details")) {
return;
}
const state = Flip.getState(".grid article");
const currentDetails = document.querySelector(".details");
currentDetails && currentDetails.classList.remove("details");
article.classList.add("details");
// "FLIP" animate from that saved state.
const tl = Flip.from(state, {
duration: 1,
ease: "power1.inOut",
stagger: 0.05,
absolute: true,
toggleClass: "changing",
onStart: () => {
gsap.to(".details-view", {
opacity: 0,
y: "100%",
duration: 0.5
});
gsap.to(".grid article", {
"--max": 0,
duration: 0.8
});
gsap.to(".grid article.details", {
"--max": 1,
duration: 1,
delay: 0.2
});
}
});
tl.to(
".details .details-view",
{ opacity: 1, y: 0, duration: 0.75 },
"-=.25"
);
});
}
for (let button of [...buttons]) {
button.addEventListener("click", () => {
const a = button.parentNode.parentNode.parentNode;
const state = Flip.getState(".grid article, .description .details-view");
container.prepend(a);
const tl = Flip.from(state, {
duration: 1,
ease: "power1.inOut",
stagger: 0.05,
absolute: true
});
});
}
alphaButton.addEventListener("click", () => {
const state = Flip.getState(".grid article, .description .details-view");
[...articles]
.sort((r, l) => {
const titleR = r.querySelector("h2");
const titleL = l.querySelector("h2");
if (titleR.textContent > titleL.textContent) return 1;
if (titleL.textContent > titleR.textContent) return -1;
return 0;
})
.forEach((article) => container.append(article));
const tl = Flip.from(state, {
duration: 1,
ease: "power1.inOut",
stagger: 0.05,
absolute: true
});
});
// Intro animation
window.addEventListener("load", () => {
gsap.to("main", { autoAlpha: 1, duration: 0.4 });
gsap.from(".grid article", {
autoAlpha: 0,
yPercent: 30,
stagger: 0.04
});
});
This Pen doesn't use any external CSS resources.