<div class="list">
<ul class="list__track">
<li class="list__item">Lorem ipsum dolor sit amet.</li>
<li class="list__item">Quaerat excepturi ea aspernatur magnam.</li>
<li class="list__item">Officia corrupti nam excepturi mollitia.</li>
<li class="list__item">Quod consequatur corporis odit possimus.</li>
<li class="list__item">Voluptas qui sapiente corporis debitis!</li>
<li class="list__item">Nemo ipsam quo natus sint!</li>
<li class="list__item">Cumque pariatur aliquid similique sunt?</li>
<li class="list__item">Repellendus, alias repellat. Nihil, esse.</li>
<li class="list__item">Dolorem nam eius esse voluptatum?</li>
<li class="list__item">Officiis suscipit consectetur accusamus molestias!</li>
<li class="list__item">Animi, tempore eius. Inventore, aperiam!</li>
<li class="list__item">Dolor quis illo vitae tempora!</li>
<li class="list__item">Consectetur quisquam velit magnam illo?</li>
<li class="list__item">Maxime corporis suscipit velit tempora?</li>
<li class="list__item">Nihil quisquam fugit minus animi!</li>
<li class="list__item">Labore non delectus iusto iste.</li>
<li class="list__item">Eos eveniet vero consequuntur exercitationem.</li>
<li class="list__item">Dolorem id aperiam nisi repellendus.</li>
<li class="list__item">Facere vero quos voluptates maiores!</li>
<li class="list__item">Consequuntur ut labore pariatur perspiciatis.</li>
<li class="list__item">Non molestias cum enim voluptate!</li>
<li class="list__item">Ex quae laborum animi repudiandae.</li>
<li class="list__item">Ipsa unde eveniet nemo rem?</li>
<li class="list__item">Voluptate, magnam! Non, ab tempore.</li>
<li class="list__item">Possimus pariatur commodi harum alias?</li>
<li class="list__item">Qui odit vero voluptas fugiat!</li>
<li class="list__item">Libero commodi magni praesentium doloremque!</li>
<li class="list__item">Ducimus laboriosam ullam illum voluptatem.</li>
<li class="list__item">Accusamus distinctio perferendis laboriosam quidem.</li>
<li class="list__item">Blanditiis asperiores veniam sint odio.</li>
<li class="list__item">Fuga cupiditate repudiandae natus? Eos.</li>
<li class="list__item">Asperiores distinctio quae molestiae optio.</li>
<li class="list__item">Cupiditate illum suscipit repudiandae facilis.</li>
<li class="list__item">Repudiandae ducimus ipsam architecto nostrum!</li>
<li class="list__item">Mollitia explicabo eaque voluptatem nostrum?</li>
<li class="list__item">Laborum itaque a aliquam eum?</li>
<li class="list__item">Vitae nobis alias dolorum atque?</li>
<li class="list__item">Ea nulla quisquam consectetur tenetur!</li>
<li class="list__item">Commodi quam possimus tempore eos.</li>
<li class="list__item">Dolores, temporibus. Quibusdam, fugit placeat?</li>
<li class="list__item">Autem delectus maiores sunt in!</li>
<li class="list__item">Corporis accusamus architecto modi alias!</li>
</ul>
</div>
* {
box-sizing: border-box;
}
body {
display: flex;
flex-direction: column;
margin: 0;
background-color: #112;
color: #fff;
font-family: sans-serif;
min-height: 100vh;
}
.list {
max-width: 768px;
max-height: 320px;
margin: auto;
width: 100%;
height: 100vh;
padding: 0 15px;
overflow: hidden;
}
.list__track {
position: relative;
list-style: none;
margin: 0;
padding: 0;
font-size: 28px;
line-height: 1.4;
will-change: transform;
}
.list__item {
transform-origin: 0 50%;
will-change: transform, opacity;
}
const lerp = (a, b, t) => (1 - t) * a + t * b;
const list = document.querySelector('.list');
const track = list.querySelector('.list__track');
const items = Array.from(
list.querySelectorAll('.list__item'),
el => ({ el, rect: { x: 0, y: 0, w: 0, h: 0 } })
);
let scrollY = 0;
let scrollSpeed = 0.5;
let listHeight;
function resize() {
listHeight = list.offsetHeight;
items.forEach(it => {
it.rect.x = it.el.offsetLeft;
it.rect.y = it.el.offsetTop;
it.rect.w = it.el.offsetWidth;
it.rect.h = it.el.offsetHeight;
});
}
resize();
window.addEventListener('resize', resize);
function update() {
scrollY += scrollSpeed;
const minScroll = -listHeight / 2 + items[0].rect.h / 2;
const maxScroll = list.scrollHeight - listHeight / 2 - items[items.length - 1].rect.h / 2;
if (scrollY > maxScroll) {
scrollSpeed = -Math.abs(scrollSpeed);
scrollY = maxScroll;
}
else if (scrollY < minScroll) {
scrollSpeed = Math.abs(scrollSpeed);
scrollY = minScroll;
}
track.style.transform = `translateY(${-scrollY}px)`;
items.forEach(it => {
const isInView =
it.rect.y + it.rect.h >= scrollY
&& it.rect.y <= scrollY + listHeight;
if (!isInView) return;
const dist = it.rect.y + it.rect.h / 2 - scrollY - listHeight / 2;
const f = Math.abs(dist / listHeight) * 2;
it.el.style.transform = `scale(${lerp(1, 0.5, f)})`;
it.el.style.opacity = Math.max(0, lerp(1, 0, f));
});
requestAnimationFrame(update);
}
requestAnimationFrame(update);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.