Skip to content

Instantly share code, notes, and snippets.

@acdha
Created June 21, 2018 17:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acdha/10fe15b1486e12875f9104cd30ab3bb4 to your computer and use it in GitHub Desktop.
Save acdha/10fe15b1486e12875f9104cd30ab3bb4 to your computer and use it in GitHub Desktop.
WDL Homepage Carousel: minimal source extraction
<div id="featured-items-container" class="well">
<div id="featured-items" class="multi-carousel" data-json-url="{% url 'featured-items-json' %}">
<ul class="items list-unstyled list-inline">
{% for wdl_id, thumbnail_ts, title in featured_items|slice:":5" %}
<li class="item" data-wdl-id="{{ wdl_id|unlocalize }}">
<a href="{% url 'item-detail' wdl_id=wdl_id %}" title="{{ title }}">
<img class="img-rounded" width="308" height="255" src="{% media_url 'item-thumbnail' wdl_id=wdl_id height=255 width=308 timestamp=thumbnail_ts %}" alt="{{ title }}">
<span class="caption">{{ title }}</span>
</a>
</li>
{% endfor %}
</ul>
{% for wdl_id, thumbnail_ts, title in featured_items|slice:"5:" %}
<span class="pending-item" data-wdl-id="{{ wdl_id|unlocalize }}" data-url="{% url 'item-detail' wdl_id=wdl_id %}" data-title="{{ title }}" data-thumbnail="{% media_url 'item-thumbnail' wdl_id=wdl_id height=255 width=308 timestamp=thumbnail_ts %}"></span>
{% endfor %}
<a hidden class="left carousel-control" href="#featured-items" data-slide="prev">
<span class="fa fa-lg fa-chevron-left"></span>
</a>
<a hidden class="right carousel-control" href="#featured-items" data-slide="next">
<span class="fa fa-lg fa-chevron-right"></span>
</a>
</div>
(function($) {
'use strict';
if (!$(document.body).hasClass('homepage')) {
return;
}
var featuredItemContainer = document.getElementById('featured-items'),
featuredItems = featuredItemContainer.querySelector('.items');
// Remove all child nodes which aren't display items:
for (var i = 0; i < featuredItems.childNodes.length; i++) {
var n = featuredItems.childNodes[i];
if (n.nodeType != 1 || n.className.indexOf('item') < 0) {
featuredItems.removeChild(n);
}
}
var $featuredItemContainer = $(featuredItemContainer);
$featuredItemContainer.on('click', '.carousel-control', function() {
if (this.className.indexOf('left') > -1) {
featuredItems.insertBefore(featuredItems.lastElementChild, featuredItems.firstElementChild);
} else {
featuredItems.insertBefore(featuredItems.firstElementChild, null);
}
return false;
});
if (featuredItemContainer.addEventListener) {
featuredItemContainer.addEventListener(
'wheel',
$.throttle(250, true, function(evt) {
var deltaX = evt.deltaX,
deltaY = evt.deltaY;
if (Math.abs(deltaY) > Math.abs(deltaX)) {
return;
}
if (deltaX > 0) {
$('.carousel-control.right').trigger('click');
} else {
$('.carousel-control.left').trigger('click');
}
evt.preventDefault();
return false;
}),
{passive: true}
);
}
$(window).on('keydown', function(evt) {
switch (evt.which) {
case 37: // left arrow
$('.carousel-control.left').trigger('click');
return false;
case 39: // right arrow
$('.carousel-control.right').trigger('click');
return false;
}
});
$featuredItemContainer.find('.carousel-control').removeAttr('hidden');
var pendingItems = [],
$itemTemplate = $featuredItemContainer.find('.item[data-wdl-id]').first(),
existingItems = $featuredItemContainer.find('.item[data-wdl-id]').map(function(idx, elem) {
return parseInt($(elem).data('wdl-id'), 10);
}).get();
function animateCarousel() {
if (featuredItemContainer.querySelector(':hover') !== null) {
return;
}
var newFirstItem;
if (pendingItems.length > 0) {
newFirstItem = pendingItems.pop();
} else {
newFirstItem = featuredItems.lastElementChild;
}
featuredItems.insertBefore(newFirstItem, featuredItems.firstElementChild);
}
window.setInterval(animateCarousel, 8000);
function loadMoreFeaturedItems() {
var jsonURL = $featuredItemContainer.data('json-url');
if (!jsonURL) {
return;
}
$.getJSON(jsonURL, {
count: existingItems.length * 3
}).success(function(items) {
// Add new items to the queue in random order:
WDL.arrayShuffle(items);
for (var i = 0; i < items.length; i++) {
var item = items[i];
addFeaturedItem(item['wdl-id'], item.url, item.thumbnail, item.title);
}
});
}
function addFeaturedItem(itemId, itemUrl, thumbnailUrl, title) {
if (existingItems.indexOf(itemId) >= 0) {
return;
}
var $item = $itemTemplate.clone();
$item.data('wdl-id', itemId).attr('data-wdl-id', itemId);
$item.find('a').attr({
href: itemUrl,
title: title
});
// Note: seemingly-useless src='' toggle is required for IE8 support:
$item.find('img').attr('src', '').attr({
src: thumbnailUrl,
alt: title
});
$item.find('.caption').text(title);
// Delay adding the item to the list until the thumbnail has loaded:
$item.find('img').on('load', function() {
pendingItems.push($item.get(0));
});
}
$featuredItemContainer.find('.pending-item').each(function(idx, elem) {
var $elem = $(elem),
data = $elem.data();
addFeaturedItem(data.wdlId, data.url, data.thumbnail, data.title);
$elem.remove();
});
$(window).on('load', function() {
window.setTimeout(loadMoreFeaturedItems, 20 * 1000);
});
})(jQuery);
#featured-items-container {
padding-right: 8px;
padding-bottom: 2px;
padding-left: 8px;
}
#featured-items {
position: relative;
height: $featured-items-height;
.items {
position: relative;
overflow: hidden;
height: 100%;
margin: 0 1.5em;
}
.item {
$image-border-width: 1px;
@include transition(all 0.4s ease-out);
// Attempt to request hardware acceleration:
@include backface-visibility(hidden);
position: absolute;
top: 0;
left: 0;
width: #{308px + 2 * $image-border-width};
padding: 0;
will-change: transform;
&:lang(ar) {
right: 0;
left: auto;
}
&:first-child {
padding-left: 0;
}
// We'll slide all of the items off the screen and backfill the first few to be visible:
@include translateX(900%);
&:nth-child(1) { @include translateX(0%); }
&:nth-child(2) { @include translateX(100%); }
&:nth-child(3) { @include translateX(200%); }
&:nth-child(4) { @include translateX(300%); }
&:nth-child(5) { @include translateX(400%); }
&:nth-child(6) { @include translateX(500%); }
&:nth-child(7) { @include translateX(600%); }
&:nth-child(8) { @include translateX(700%); }
&:nth-child(9) { @include translateX(800%); }
&:lang(ar) {
@include translateX(-900%);
&:nth-child(1) { @include translateX(0%); }
&:nth-child(2) { @include translateX(-100%); }
&:nth-child(3) { @include translateX(-200%); }
&:nth-child(4) { @include translateX(-300%); }
&:nth-child(5) { @include translateX(-400%); }
&:nth-child(6) { @include translateX(-500%); }
&:nth-child(7) { @include translateX(-600%); }
&:nth-child(8) { @include translateX(-700%); }
&:nth-child(9) { @include translateX(-800%); }
}
a {
display: block;
color: $text-color;
&:hover img {
border-color: $btn-default-border;
}
img {
display: block; // Prevent inline display of errored images in Firefox
width: #{308px + 2 * $image-border-width};
height: #{255px + 2 * $image-border-width};
border: solid transparent $image-border-width;
}
.caption {
display: block;
overflow: hidden;
max-width: 96%;
margin: auto;
text-align: center;
white-space: nowrap;
text-overflow: ellipsis;
font-weight: bold;
}
}
}
.carousel-control {
width: 25px;
color: #000;
background: none !important;
filter: none;
&:hover {
@include opacity(1);
color: $btn-primary-bg;
}
.fa {
position: absolute;
top: floor(($featured-items-height / 2) - ($line-height-computed / 2));
top: calc(50% - #{$line-height-computed / 2});
}
&.left .fa {
left: 0;
// Intentionally override this because bootstrap-rtl already flips the element:
&.fa-chevron-left:lang(ar)::before {
content: $fa-var-chevron-left;
}
}
&.right .fa {
right: 0;
// Intentionally override this because bootstrap-rtl already flips the element:
&.fa-chevron-right:lang(ar)::before {
content: $fa-var-chevron-right;
}
}
}
.no-csstransforms & {
// Since IE8 supports neither CSS transforms nor nth-child selectors, we'll just use a
// basic inline list display:
.items {
text-align: center;
}
.item {
position: relative;
display: inline-block;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment