<div class="container">
<ul class="slider">
<li class="slider__el"><span>1</span></li>
<li class="slider__el"><span>2</span></li>
<li class="slider__el"><span>3</span></li>
<li class="slider__el"><span>4</span></li>
<li class="slider__el"><span>5</span></li>
<li class="slider__el" data-status="active"><span>6</span></li>
<li class="slider__el"><span>7</span></li>
<li class="slider__el"><span>8</span></li>
<li class="slider__el"><span>9</span></li>
<li class="slider__el"><span>10</span></li>
<li class="slider__el"><span>11</span></li>
</ul>
</div>
:root {
// card margin saved on a css var so we can access it in JS
--card-margin: 10%;
}
body {
height: 100vh;
margin: 0;
overflow: hidden;
color: white;
background: #e76f51;
font-family: sans-serif;
}
ul {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
.container {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.slider {
transition: transform 0.54s ease-out;
&__el {
width: 25vh;
height: 25vh;
margin: var(--card-margin) 0;
background: #f4a261;
border-radius: 5%;
font-size: 6em;
font-weight: lighter;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s ease;
&[data-status="active"] {
background: #2a9d8f;
}
}
}
View Compiled
const appController = (function () {
const key = {
up: 38,
down: 40
};
// Our total transformY value will be stored here
const data = {
total: {
Ytransform: 0
}
};
return {
debounce: (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
timeout = null;
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
// Tells which direction it has to slide to
getDir: function (e) {
if (e <= -1 || e.keyCode === key.up || e.wich === key.up) {
return "down";
} else if (
e >= 1 ||
e.keyCode === key.down ||
e.wich === key.down
) {
return "up";
}
},
// Updates our total transformY value
updateTotal: function () {
return function (status, value) {
return status === "inc"
? (data.total.Ytransform += value)
: status === "dec"
? (data.total.Ytransform -= value)
: data.total.Ytransform;
};
},
getData: function () {
return data;
}
};
})();
const UIController = (function () {
const DOMstrings = {
slider: ".slider",
card: ".slider__el"
};
const UIData = {
index: 0
};
const getElements = function () {
return {
cards: document.querySelectorAll(DOMstrings.card)
};
};
// Will give us the exact size of our element: offset height + it's margin
// To get our margin, I saved this value on a css variable '--card-margin'
const getCardSize = function () {
let height, margin;
height = document.querySelector(DOMstrings.card).offsetHeight;
margin = getComputedStyle(document.documentElement).getPropertyValue(
"--card-margin"
);
margin = margin.replace("%", "");
function calcFh() {
let fullHeight = height + (height / 100) * margin;
return fullHeight;
}
return calcFh();
};
// Gets our current active element index
const getIndex = function () {
const els = getElements(),
cards = els.cards,
cardsArr = Array.from(cards);
let currentIndex = cardsArr.findIndex(
(card) => card.dataset.status === "active"
);
UIData.index = currentIndex;
return currentIndex;
};
// Updates index
const setIndex = function () {
let index = getIndex();
return function (decrement) {
decrement ? index-- : index++;
UIData.index = index;
return index;
};
};
const keepIndex = setIndex();
// Applies style to our active element
const setActive = function (index) {
const els = getElements(),
cards = els.cards,
cardsArr = Array.from(cards);
cardsArr.forEach((card) => (card.dataset.status = ""));
cardsArr[index].dataset.status = "active";
};
// Limiting the slider to get passed the last element
const limit = function (index) {
const els = getElements(),
cards = els.cards;
if (index > cards.length - 2) return "maxReached";
if (index <= 0) return "minReached";
};
return {
// Main sliding function
slide: function (dir, updateValue) {
let cardSize, yValue, index;
const stop = limit(UIData.index);
cardSize = getCardSize();
if (dir === "down") {
if (stop !== "minReached") {
yValue = updateValue("inc", cardSize);
index = keepIndex(true);
setActive(index);
}
} else if (dir === "up") {
if (stop !== "maxReached") {
yValue = updateValue("dec", cardSize);
index = keepIndex(false);
setActive(index);
}
}
if ((yValue > 0 && yValue < 10) || (yValue < 0 && yValue > -10))
yValue = 0;
document.querySelector(DOMstrings.slider).style.transform =
"translateY(" + yValue + "px)";
}
};
})();
// Controls user actions
const controller = (function (UICtrl, appCtrl) {
const eventListeners = function () {
let debounce,
delay,
scrollDir,
dir,
arr = [];
delay = 1200;
debounce = appCtrl.debounce;
scrollDir = appCtrl.getDir;
const onScroll = debounce((e) => {
dir = scrollDir(e);
ctrlSlide(dir);
arr = [];
}, delay);
const onKey = debounce((e) => {
dir = scrollDir(e);
ctrlSlide(dir);
}, delay / 3);
//events
window.addEventListener("wheel", (e) => {
let yValue;
// fix for touchpads our magic mouse deltaY value
if (arr.length < 4) {
arr.push(e.deltaY);
if (arr.length > 3) yValue = arr[2];
}
if (yValue !== undefined) onScroll(yValue);
});
document.addEventListener("keydown", (e) => {
onKey(e);
});
};
const ctrlSlide = function (dir) {
let total;
total = appCtrl.updateTotal();
UICtrl.slide(dir, total);
};
return {
init: function () {
eventListeners();
}
};
})(UIController, appController);
controller.init();
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.