ul.visual-rolodex
View Compiled
@import url('https://fonts.googleapis.com/css2?family=MuseoModerno:wght@100;200;300;400;500;600&display=swap')
:root
--font-size 2
*
box-sizing border-box
body
min-height 100vh
display flex
align-items center
justify-content center
background hsl(0, 0%, 10%)
perspective 800px
transform-style preserve-3d
.visual-rolodex
padding calc(50vh - 50px) 0
font-size calc(var(--font-size) * 1rem)
list-style none
color hsl(0, 0%, 99%)
font-family 'MuseoModerno', cursive
transform rotateY(0.5deg)
li
display flex
position relative
align-items center
font-variation-settings 'wght' var(--font-weight, 300)
img
height 100px
width 100px
object-fit cover
margin-right 1rem
filter grayscale(var(--grayscale))
border-radius 50%
View Compiled
const {
faker: {
image: { avatar },
name: { findName }
},
gsap: {
to,
set,
registerPlugin,
timeline,
utils: { mapRange }
},
ScrollTrigger
} = window;
const ROLODEX = document.querySelector(".visual-rolodex");
const PEOPLE = [];
const genPerson = () => PEOPLE.push(findName());
for (let p = 0; p < 200; p++) genPerson();
for (const person of PEOPLE.sort()) {
const ITEM = document.createElement("li");
const NAME = document.createElement('span');
const AVATAR = document.createElement('img');
AVATAR.src = avatar()
AVATAR.setAttribute('alt', person)
NAME.innerHTML = person;
ITEM.appendChild(AVATAR)
ITEM.appendChild(NAME)
ROLODEX.appendChild(ITEM);
}
const ITEMS = [...document.querySelectorAll('li')]
set(ITEMS, { scale: 0.35, opacity: 0.4, z: 250, '--font-weight': 300, '--grayscale': 1 })
set(ITEMS[0], { scale: 1, opacity: 1, z: 0, '--font-weight': 600, '--grayscale': 0})
let CURRENT = 0
ScrollTrigger.create({
trigger: "body",
start: "top top",
end: "bottom bottom",
onUpdate: self => {
const INDEX = mapRange(0, 1, 0, PEOPLE.length - 1, self.progress)
if (CURRENT !== Math.floor(INDEX)) {
timeline({
onComplete: () => {
set(ITEMS.filter((item, idx) => idx !== CURRENT && idx !== INDEX), { opacity: 0.4, z: 250, scale: 0.35, '--font-weight': 300, '--grayscale': 1 })
CURRENT = Math.floor(INDEX)
}
})
.to(ITEMS[CURRENT], { scale: 0.35, duration: 0.1, opacity: 0.4, z: 250, '--font-weight': 300, '--grayscale': 1}, 0)
.to(ITEMS[Math.floor(INDEX)], { scale: 1, duration: 0.1, opacity: 1, z: 0, '--font-weight': 600, '--grayscale': 0}, 0)
}
}
});
View Compiled
This Pen doesn't use any external CSS resources.