Слайдер карточек с точками и стрелками, крайние карточки упираются в границы.
uc-rsn..arrows со стрелками .arrow-left / .arrow-right, .cards-container с карточками .card и .dots с точками .dot.<script src="https://cdn.jsdelivr.net/npm/scrollbooster@3.0.2/dist/scrollbooster.min.js" defer=""></script>
<style>
.uc-rsn .scrollbooster-viewport {
/* горизонтальный скролл, скрытые скроллбары */
cursor: grab;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE 10+ */
}
/* Скрываем в WebKit-браузерах */
.uc-rsn .scrollbooster-viewport::-webkit-scrollbar {
height: 0;
background: transparent;
}
.uc-rsn .dot.active .tn-atom {
opacity: 1 !important;
}
</style>
<script>
$(() => {
const $record = $(".uc-rsn");
const $leftArrow = $record.find(".arrow-left");
const $rightArrow = $record.find(".arrow-right");
const $scrollContainer = $record.find(".cards-container").addClass("scrollbooster-viewport");
$scrollContainer.wrapInner('<div class="scrollbooster-content"></div>');
$scrollContainer.on("scroll", () => updateControls());
$(window).on("resize", () => updateControls());
const cards = $scrollContainer.find(".card").toArray().toSorted((a, b) => getX(a) - getX(b));
$leftArrow.click(e => {
e.preventDefault();
$scrollContainer
.animate( { scrollLeft: '-=' + getShiftSize(false) }, 300 )
.queue(updateControls);
})
$rightArrow.click(e => {
e.preventDefault();
$scrollContainer
.animate( { scrollLeft: '+=' + getShiftSize(true) }, 300 )
.queue(updateControls);
})
const $dotsContainer = $record.find(".dots");
const $firstDot = $dotsContainer.find(".dot");
for(let i = 1; i < cards.length; i++) {
$firstDot.clone().appendTo($dotsContainer.find(".tn-molecule"));
}
$firstDot.addClass("active");
const $dots = $dotsContainer.find(".dot");
function updateControls(next) {
let leftArrowDisabled = $scrollContainer.scrollLeft <= 10;
let rightArrowDisabled = $scrollContainer.scrollLeft >= $scrollContainer.scrollWidth - $scrollContainer.clientWidth - 10;
$leftArrow.add($rightArrow).toggleClass("hidden", leftArrowDisabled && rightArrowDisabled);
$leftArrow.toggleClass("disabled", leftArrowDisabled);
$rightArrow.toggleClass("disabled", rightArrowDisabled);
const currentSlideIndex = cards.indexOf(
getNearestBlock(cards, window.innerWidth / 2, false)
);
$dotsContainer.find(".dot.active").removeClass("active");
$dots[currentSlideIndex].classList.add("active");
if(next) next();
}
function getX(block) {
return block.getBoundingClientRect().x;
}
function getNearestBlock(blocks, pointX, direction) {
let nearestBlock;
let nearestBlockX;
for(const block of blocks) {
const blockX = getX(block);
if(direction ? (blockX <= pointX+10) : (blockX >= pointX-10)) continue;
if(
nearestBlockX === undefined ||
(direction ? (blockX < nearestBlockX) : (blockX > nearestBlockX))
) {
nearestBlock = block;
nearestBlockX = blockX;
}
}
if(nearestBlockX === undefined)
return blocks.at(direction ? -1 : 0);
return nearestBlock;
}
function getShiftSize(direction) {
const containerX = getX( $scrollContainer[0] );
// const containerX = $scrollContainer[0].offsetLeft + cards[0].offsetLeft;
const nearestCardX = getX( getNearestBlock(cards, containerX, direction) );
return Math.abs(containerX - nearestCardX);
}
// Ширина скроллбара для css костылей
function _calculateScrollbarWidth() {
document.documentElement.style.setProperty('--scrollbar-width', (window.innerWidth - document.documentElement.clientWidth) + "px");
}
window.addEventListener('resize', _calculateScrollbarWidth, false);
document.addEventListener('DOMContentLoaded', _calculateScrollbarWidth, false);
window.addEventListener('load', _calculateScrollbarWidth);
_calculateScrollbarWidth();
setTimeout(() => {
const sb = new ScrollBooster({
viewport: $scrollContainer[0],
content: $record.find(".scrollbooster-content")[0],
scrollMode: 'native',
pointerMode: 'all',
bounce: false,
});
}, 1);
});
</script>