Skip to content

Instantly share code, notes, and snippets.

@jongacnik
Last active December 12, 2019 22:51
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 jongacnik/871984e9de3fc57fb230237fcdf35aaa to your computer and use it in GitHub Desktop.
Save jongacnik/871984e9de3fc57fb230237fcdf35aaa to your computer and use it in GitHub Desktop.
Crossfade Slideshow Example

Flexible simple slideshows

Simple 3-module setup for v flexible slideshows (mainly when crossfading, use Flickity for anything else lol).

generic-cycle

A module, which given an array, returns an instance with some methods for progressing through the array and getting data about indexes and etc

generic-slideshow

A module, which given an element, puts all children into an instance of generic cycle. Returns an instance with methods for moving prev/next. On prev/next, we just add a class of in or out which can be used for fx

crossfade-slideshow

Shows using generic-slideshow in a data-component. Little extra functionality for setting caption/counter also.


Not meant to be plug-and-play (though its v close). Just keeping track of my approach to this for future projects. This example is old-skool dom-thrashy, but I use same ideas when throwing this into a React/Vue build, etc. Main point is just have a simple cycle iterator and let css handle the fade out.

import GenericSlideshow from './generic-slideshow'
const ov = object => Object.keys(object).map(i => object[i])
class CrossfadeSlideshow {
constructor($el, options) {
this.$el = $el
this.$counter = document.querySelector('[data-subcomponent="slider-counter"][data-exports="' + options.exports + '"]')
this.$title = document.querySelector('[data-subcomponent="slider-title"][data-exports="' + options.exports + '"]')
// init slideshow if multiple slides
if ($el.children.length > 1) {
this.slideshow = GenericSlideshow($el, {
onChange: slides => this.onChange(slides)
})
this.slideshow.init()
this.handleClick()
} else if ($el.children.length === 1) {
// otherwise just show the single slide
$el.children[0].classList.add('in')
this.$title.innerHTML = $el.children[0].getAttribute('data-caption')
$el.classList.add('no-slideshow')
}
}
handleClick () {
// on click, progress the slider
this.$el.addEventListener('click', () => {
if (this.slideshow) {
this.slideshow.next()
}
})
}
onChange ($slides) {
this.$title.innerHTML = $slides.current.getAttribute('data-caption')
var data = this.slideshow.data()
this.$counter.innerHTML = `${data.current + 1}/${data.length}`
}
}
export default CrossfadeSlideshow
[data-component="crossfade-slideshow"] {
width: 100%;
height: 100%;
position: relative;
> * {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: none;
background: white;
&.in {
display: block;
opacity: 1;
z-index: 3;
}
&.out {
display: block;
animation: slideshow-fade-out 1s ease forwards;
z-index: 4;
}
}
}
@keyframes slideshow-fade-out {
from { opacity: 1 }
to { opacity: 0 }
}
/**
* Generic cycle module for moving
* through an array
*/
module.exports = function (list, index) {
index = index || 0
var current = () => index
var next = () => (index + 1 >= list.length) ? 0 : index + 1
var prev = () => (index - 1 < 0) ? list.length - 1 : index - 1
var currentItem = () => list[index]
var nextItem = () => list[next()]
var prevItem = () => list[prev()]
function goNext () {
index = next()
return list[index]
}
function goPrev () {
index = prev()
return list[index]
}
return {
current,
next,
prev,
currentItem,
nextItem,
prevItem,
goNext,
goPrev,
length: list.length
}
}
import GenericCycle from './generic-cycle'
/**
* Generic slideshow module
* simply adds previous/current classes
* which can be used to trigger animations
*
* Todo:
* - better onChange/onAfter mechanism.
* - onChange should use animation start callback
* - onAfter should use animation end callback
*/
module.exports = function ($el, opts) {
opts = opts || {}
var slides = [...$el.children]
var slidesCycle = GenericCycle(slides)
function clearClasses () {
slides.forEach($slide => {
$slide.classList.remove('out', 'in')
})
}
function init () {
clearClasses()
if (opts.onChange) {
opts.onChange({
current: slidesCycle.currentItem(),
next: slidesCycle.nextItem(),
prev: slidesCycle.prevItem()
})
}
slidesCycle.currentItem().classList.add('in')
}
function destroy () {
clearClasses()
}
function next () {
clearClasses()
slidesCycle.currentItem().classList.add('out')
slidesCycle.goNext().classList.add('in')
if (opts.onChange) {
opts.onChange({
current: slidesCycle.currentItem(),
next: slidesCycle.nextItem(),
prev: slidesCycle.prevItem()
})
}
}
function prev () {
clearClasses()
slidesCycle.currentItem().classList.add('out')
slidesCycle.goPrev().classList.add('in')
if (opts.onChange) {
opts.onChange({
current: slidesCycle.currentItem(),
next: slidesCycle.nextItem(),
prev: slidesCycle.prevItem()
})
}
}
return {
init: init,
destroy: destroy,
next: next,
prev: prev,
data: () => {
return {
current: slidesCycle.current(),
next: slidesCycle.next(),
prev: slidesCycle.prev(),
length: slidesCycle.length,
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment