A few weeks ago I saw a really nice carousel on the Stripe blog. So I decided to replicate it and integrate it with WordPress. Here are my steps.
See a demo on my homepage here - https://getbutterfly.com/
The WordPress Loop
I am getting the last 10 posts in the JavaScript category, I am setting up a counter and changing background colours based on this counter.
<?php
function whiskey_carousel() {
$args = [
'post_status' => 'publish',
'post_type' => 'post',
'posts_per_page' => 10,
'category_name' => 'javascript'
];
$featuredQuery = new WP_Query($args);
$colours = [
'#02bcf5', '#0073e6', '#003ab9', '#635bff', '#002c59', '#09cbcb',
'#02bcf5', '#0073e6', '#003ab9', '#635bff', '#002c59', '#09cbcb',
];
$i = 0;
$data = '<div class="whiskey-cards">';
if ($featuredQuery->have_posts()) {
while ($featuredQuery->have_posts()) {
$featuredQuery->the_post();
$postID = get_the_ID();
$excerpt = html_entity_decode(wp_trim_words(get_the_excerpt(), 32));
$data .= '<div class="whiskey-card" style="background-color: ' . $colours[$i] . ';">
<h3><a href="' . get_permalink($postID) . '">' . get_the_title($postID) . '</a></h3>
<p class="whiskey-card--content">' . $excerpt . '</p>
<p class="whiskey-card--link"><a href="' . get_permalink($postID) . '">Learn more <svg class="HoverArrow" width="10" height="10" viewBox="0 0 10 10" aria-hidden="true"><g fill-rule="evenodd"><path class="HoverArrow__linePath" d="M0 5h7"></path><path class="HoverArrow__tipPath" d="M1 1l4 4-4 4"></path></g></svg></a></p>
</div>';
$i++;
}
}
$data .= '</div>';
return $data;
}
add_shortcode('whiskey-carousel', 'whiskey_carousel');
The JavaScript Code
The JavaScript features momentum scrolling (mouse wheel) and (almost) native HTML dragging behaviour. The dragging is also available to mobile devices, as the content is overflowing horizontally.
document.addEventListener('DOMContentLoaded', () => {
if (document.querySelector('.whiskey-cards')) {
// Slider dragging
const slider = document.querySelector('.whiskey-cards');
let isDown = false;
let startX;
let scrollLeft;
slider.addEventListener('mousedown', (e) => {
isDown = true;
slider.classList.add('active');
startX = e.pageX - slider.offsetLeft;
scrollLeft = slider.scrollLeft;
cancelMomentumTracking();
});
slider.addEventListener('mouseleave', () => {
isDown = false;
slider.classList.remove('active');
});
slider.addEventListener('mouseup', () => {
isDown = false;
slider.classList.remove('active');
beginMomentumTracking();
});
slider.addEventListener('mousemove', (e) => {
if (!isDown) return;
e.preventDefault();
const x = e.pageX - slider.offsetLeft;
const walk = (x - startX); //scroll-fast
var prevScrollLeft = slider.scrollLeft;
slider.scrollLeft = scrollLeft - walk;
velX = slider.scrollLeft - prevScrollLeft;
});
// Momentum
var velX = 0;
var momentumID;
slider.addEventListener('wheel', (e) => {
cancelMomentumTracking();
});
function beginMomentumTracking() {
cancelMomentumTracking();
momentumID = requestAnimationFrame(momentumLoop);
}
function cancelMomentumTracking() {
cancelAnimationFrame(momentumID);
}
function momentumLoop() {
slider.scrollLeft += velX * 2;
velX *= 0.95;
if (Math.abs(velX) > 0.5) {
momentumID = requestAnimationFrame(momentumLoop);
}
}
// Scroll
const scrollContainer = document.querySelector(".whiskey-cards");
scrollContainer.addEventListener("wheel", (evt) => {
evt.preventDefault();
window.requestAnimationFrame(() => {
scrollContainer.scrollTo({ top: 0, left: scrollContainer.scrollLeft + (evt.deltaY * 2), behavior: "smooth" });
});
});
}
});
The CSS Style
And finally, there are lots of opinionated styles below, so make sure you get what you need.
.whiskey-cards {
display: flex;
flex-wrap: nowrap;
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: none;
scrollbar-width: none;
padding: 48px 48px 0 48px;
}
.whiskey-cards::-webkit-scrollbar {
-webkit-appearance: none;
width: 5px;
height: 5px;
}
.whiskey-cards::-webkit-scrollbar-thumb {
border-radius: 0;
background-color: rgba(0, 0, 0, .5);
background: linear-gradient(90deg, #02bcf5, #0073e6, #003ab9, #635bff);
box-shadow: 0 0 1px rgba(255, 255, 255, .5);
border-radius: 16px;
opacity: .5;
}
.whiskey-cards:hover::-webkit-scrollbar-thumb {
opacity: 1;
}
.whiskey-card {
display: flex;
flex-direction: column;
min-width: 244px;
flex-basis: 244px;
border-radius: 16px;
margin: 8px;
padding: 16px;
box-shadow: 0 -16px 24px rgb(0 0 0 / 5%);
color: #ffffff;
transition: all 150ms cubic-bezier(0.215,0.61,0.355,1);
}
.whiskey-card:hover {
background-color: #0a2540 !important;
transform: scale(1.04) translateY(-16px);
box-shadow: 0 -16px 24px rgb(0 0 0 / 10%);
}
.whiskey-card h3 {
padding-top: 0;
line-height: 1.35;
}
.whiskey-card .whiskey-card--content {
line-height: 1.5;
font-size: 15px;
font-weight: 300;
}
.whiskey-card .whiskey-card--link {
line-height: 1.5;
font-size: 15px;
font-weight: 700;
opacity: .7;
margin: auto 0 0 0;
}
.whiskey-card h3 a,
.whiskey-card .whiskey-card--link a {
color: #ffffff;
}
.whiskey-card .whiskey-card--link a svg {
--arrowSpacing: 5px;
--arrowHoverTransition: 150ms cubic-bezier(0.215,0.61,0.355,1);
--arrowHoverOffset: translateX(3px);
--arrowTipTransform: none;
--arrowLineOpacity: 0;
position: relative;
top: 1px;
margin-left: var(--arrowSpacing);
stroke-width: 2px;
fill: none;
stroke: currentColor;
}
.HoverArrow__linePath {
opacity: var(--arrowLineOpacity);
transition: opacity var(--hoverTransition,var(--arrowHoverTransition));
}
.HoverArrow__tipPath {
transform: var(--arrowTipTransform);
transition: transform var(--hoverTransition,var(--arrowHoverTransition));
}
.whiskey-card:hover .HoverArrow__linePath {
--arrowLineOpacity: 1;
}
.whiskey-card:hover .HoverArrow__tipPath {
--arrowTipTransform: var(--arrowHoverOffset);
}
I wrote about this and other JavaScript carousels and sliders here.
Top comments (2)
Why is velocity here not distance over time.
I’ve been really invested into making a carousel like this one so I’m wondering how you came about this solution
For exemple: where did that .95 coming from
My maths are not what they used to be. That .95 came from trial and error, until it looked smooth to me :)