Last active
October 24, 2018 10:04
-
-
Save steevehook/4e6bade50eaba9e59051c9760f547627 to your computer and use it in GitHub Desktop.
Vanilla carousel with infinite loop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import "../../core/fonts"; | |
@import "../../core/colors"; | |
@import "../../core/mixins"; | |
$number-of-slides-carousel: 4; | |
$number-of-slides-content-images: 3; | |
@mixin activeSlide() { | |
.refresh-content-images__info-cta { | |
a { | |
color: $gold-refresh; | |
} | |
} | |
img { | |
@include breakpoint(xp) { | |
opacity: 0.8; | |
} | |
} | |
} | |
.refresh-content-images { | |
margin-bottom: 20px; | |
@include breakpoint(xp) { | |
margin-bottom: 80px; | |
} | |
&__carousel-wrapper, &__list-wrapper { | |
position: relative; | |
overflow: hidden; | |
} | |
&__container { | |
position: relative; | |
} | |
&__arrow { | |
position: absolute; | |
top: 45%; | |
transform: translateY(-100%); | |
width: 35px; | |
height: 35px; | |
background: $white-dark; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
font-size: 18px; | |
cursor: pointer; | |
z-index: 10; | |
&--left { | |
left: 0; | |
} | |
&--right { | |
right: 0; | |
} | |
&--disabled { | |
display: none; | |
} | |
@at-root .touchevents & { | |
display: none; | |
} | |
} | |
&__title { | |
font-family: $avantgarde-gothic; | |
font-size: 46px; | |
font-weight: 300; | |
text-transform: uppercase; | |
text-align: center; | |
margin-bottom: 40px; | |
@include breakpoint(xp) { | |
font-size: 82px; | |
} | |
} | |
&__list, &__carousel { | |
position: relative; | |
left: 0; | |
display: flex; | |
transition: all .3s ease-in-out; | |
} | |
&__list-item { | |
display: flex; | |
flex-direction: column; | |
box-sizing: border-box; | |
@at-root .touchevents &.active { | |
@include activeSlide | |
} | |
@at-root .no-touchevents &:hover { | |
@include activeSlide | |
} | |
img { | |
display: block; | |
height: auto; | |
pointer-events: none; | |
} | |
} | |
&__info { | |
font-family: $avantgarde-gothic; | |
text-transform: uppercase; | |
color: $gray-refresh; | |
padding: 25px; | |
box-sizing: border-box; | |
@include breakpoint(xp) { | |
padding: 35px; | |
} | |
} | |
&__info-title { | |
font-size: 16px; | |
@include breakpoint(xp) { | |
line-height: 24px; | |
font-size: 24px; | |
} | |
} | |
&__info-cta { | |
font-size: 12px; | |
margin-top: 20px; | |
@include breakpoint(xp) { | |
font-size: 13px; | |
margin-top: 25px; | |
} | |
a { | |
color: lighten($gray-refresh, 20%); | |
&, | |
&:hover { | |
@include breakpoint(xp) { | |
text-decoration: underline; | |
} | |
} | |
} | |
} | |
&__list { | |
.refresh-content-images__list-item { | |
.refresh-content-images__info, | |
img { | |
width: 1024px/$number-of-slides-carousel; | |
@include breakpoint(sm) { | |
width: 1024px/$number-of-slides-content-images; | |
} | |
@include breakpoint(xp) { | |
width: (100vw/$number-of-slides-content-images); | |
} | |
} | |
} | |
} | |
&__list { | |
text-align: center; | |
} | |
&__carousel { | |
.refresh-content-images__list-item { | |
&:nth-child(odd) { | |
background-color: $white-dark; | |
} | |
.refresh-content-images__info, | |
img { | |
width: 1024px/$number-of-slides-carousel; | |
@include breakpoint(xp) { | |
width: (100vw/$number-of-slides-carousel); | |
} | |
} | |
.refresh-content-images__info-title { | |
font-size: 13px; | |
@include breakpoint(xp) { | |
font-size: 16px; | |
} | |
} | |
.refresh-content-images__info-copy { | |
margin-top: 20px; | |
font-family: $helvetica-light; | |
font-size: 12px; | |
text-transform: none; | |
font-weight: 300; | |
@include breakpoint(xp) { | |
font-size: 14px; | |
margin-top: 25px; | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import metadata from './utils/metadata'; | |
import hammer from './utils/hammer'; | |
import debounce from '../tools/debounce'; | |
import {nextHandler, previousHandler, step} from './utils/moves'; | |
import {onResizeThrottler} from '../tools/throttler'; | |
const initializeDimensions = metadata => { | |
resizeTitleHeights(metadata.com.titleList); | |
adjustSlideWidth(metadata); | |
metadata.wrapper.style.height = `${metadata.com.carousel.children[0].offsetHeight}px`; | |
resizeSlidesHeight(metadata); | |
}; | |
const resizeSlidesHeight = ({gallery, wrapper}) => { | |
gallery.forEach(slide => slide.style.height = wrapper.style.height) | |
}; | |
const resizeTitleHeights = titleList => { | |
const titleArray = Array.from(titleList); | |
const titleHeights = titleArray.map(title => title.offsetHeight); | |
const greatestTitleHeight = titleHeights.sort()[titleHeights.length - 1]; | |
titleArray.forEach(title => title.style.height = greatestTitleHeight + 'px'); | |
}; | |
const adjustSlideWidth = metadata => { | |
metadata.slideWidth = window.innerWidth <= 1024 | |
? metadata.gallery[0].children[0].children[0].width | |
: window.innerWidth / metadata.numberOfSlides; | |
step(metadata, 0); | |
}; | |
const addEventListeners = carouselData => { | |
const {com} = carouselData; | |
com.leftArrow.addEventListener('click', debounce(150, () => previousHandler(carouselData))); | |
com.rightArrow.addEventListener('click', debounce(150, () => nextHandler(carouselData))); | |
}; | |
const initialize = metadata => { | |
metadata.com.carousel.children[metadata.active].classList.add('active'); | |
addEventListeners(metadata); | |
hammer(metadata); | |
initializeDimensions(metadata); | |
onResizeThrottler(() => initializeDimensions(metadata), 500); | |
}; | |
export default element => initialize(metadata(element)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default { | |
lazyLoad: async () => { | |
const elList = document.querySelectorAll('.refresh-carousel'); | |
if (elList.length > 0) { | |
const {default: carousel} = await import('./carousel'); | |
elList.forEach(el => carousel(el)); | |
} | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Hammer from 'hammerjs'; | |
import {slide, activePrev, activeNext} from './moves'; | |
export default metadata => { | |
const {com, wrapper} = metadata; | |
const sliderManager = new Hammer.Manager(wrapper); | |
const reposition = e => metadata.position + e.deltaX; | |
sliderManager.add(new Hammer.Pan({ | |
threshold: 10, | |
pointers: 1, | |
direction: Hammer.DIRECTION_HORIZONTAL | |
})); | |
sliderManager.on('panstart', e => { | |
if (e.pointerType !== 'touch') { | |
return; | |
} | |
com.carousel.style.transition = 'none'; | |
}); | |
sliderManager.on('panleft panright', e => { | |
const isHorizontalPos = Math.abs(e.deltaX) > Math.abs(e.deltaY); | |
if (e.pointerType !== 'touch' || !isHorizontalPos) { | |
return; | |
} | |
slide({com, position: reposition(e)}); | |
}); | |
sliderManager.on('panend', e => { | |
let increment = Math.round(Math.abs(e.deltaX / metadata.slideWidth)); | |
metadata.increment = increment === 0 ? 1 : increment; | |
const isHorizontal = Math.abs(e.deltaX) > Math.abs(e.deltaY); | |
if (e.pointerType !== 'touch' || !isHorizontal) { | |
return; | |
} | |
metadata.position = reposition(e); | |
com.carousel.style.transition = 'all .3s ease-in-out'; | |
if (e.direction === Hammer.DIRECTION_LEFT || e.deltaX * 1 < 0) { | |
metadata.position -= (metadata.slideWidth * metadata.increment - Math.abs(e.deltaX)); | |
activeNext(metadata); | |
} else if (e.direction === Hammer.DIRECTION_RIGHT || e.deltaX * 1 > 0) { | |
metadata.position += (metadata.slideWidth * metadata.increment - Math.abs(e.deltaX)); | |
activePrev(metadata); | |
} | |
slide(metadata); | |
metadata.increment = 1; | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const getSrcSet = image => { | |
if (!image || !image.srcset) { | |
return []; | |
} | |
return image.srcset.split(',').map(link => { | |
const [url, ratio] = link.trim().split` `; | |
const splitLink = url.split`/`; | |
return { | |
ratio, | |
width: splitLink[splitLink.length - 1] | |
}; | |
}); | |
}; | |
export default (slide, metadata) => { | |
const {gallery} = metadata; | |
const lastLoadedSlide = metadata.loadedSlides[metadata.loadedSlides.length - 1]; | |
const [gallerySample] = gallery[0].children[0].children; | |
if (slide > gallery.length - 1) { | |
return; | |
} | |
for (let i = lastLoadedSlide; i <= slide; i++) { | |
const [slideElement] = gallery[i].children, | |
[slideImage] = slideElement.children, | |
kalturaEntry = slideImage.dataset.kalturaEntry; | |
const sampleSrc = gallerySample.src.trim().split`/`, | |
sampleSrcWidth = sampleSrc[sampleSrc.length - 1]; | |
if (!slideImage.src) { | |
slideImage.src = `${window.GHD.config.kalturaThumbnailURL}/${kalturaEntry}/width/${sampleSrcWidth}`; | |
slideImage.srcset = getSrcSet(gallerySample).map(v => | |
`${window.GHD.config.kalturaThumbnailURL}/${kalturaEntry}/quality/75/width/${v.width} ${v.ratio}` | |
); | |
slideImage.sizes = gallerySample.sizes; | |
metadata.loadedSlides.push(i); | |
} | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default element => { | |
const wrapper = element.parentElement.parentElement; | |
const position = 0; | |
const numberOfSlides = parseInt(element.dataset.numberOfSlides); | |
const slideWidth = window.innerWidth / numberOfSlides; | |
const loadedSlides = [numberOfSlides]; | |
const com = { | |
carousel: element, | |
titleList: element.querySelectorAll('.refresh-content-images__info-title'), | |
leftArrow: wrapper.querySelector('.refresh-content-images__arrow--left'), | |
rightArrow: wrapper.querySelector('.refresh-content-images__arrow--right') | |
}; | |
const gallery = Array | |
.from(com.carousel.children || []) | |
.filter(e => e.classList.contains('refresh-content-images__list-item')); | |
const active = 0; | |
const stepPos = 0; | |
const increment = 1; | |
return { | |
wrapper, | |
position, | |
numberOfSlides, | |
slideWidth, | |
loadedSlides, | |
com, | |
gallery, | |
active, | |
stepPos, | |
increment | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import lazyLoad from './lazyLoad'; | |
export const slide = ({com, position}) => { | |
com.carousel.style.left = position + 'px'; | |
}; | |
export const step = (metadata, time) => { | |
setTimeout(() => { | |
for (let i = 0; i < metadata.gallery.length; i++) { | |
const index = (metadata.active + i) % metadata.gallery.length; | |
const slide = metadata.com.carousel.children[index]; | |
slide.style.position = 'absolute'; | |
slide.style.left = `${metadata.slideWidth * i + metadata.stepPos}px`; | |
} | |
}, time || 0); | |
}; | |
export const activeNext = metadata => { | |
metadata.stepPos += metadata.slideWidth * metadata.increment; | |
metadata.com.carousel.children[metadata.active].classList.remove('active'); | |
metadata.active = metadata.active + metadata.increment > metadata.com.carousel.children.length - 1 ? 0 : metadata.active + metadata.increment; | |
metadata.com.carousel.children[metadata.active].classList.add('active'); | |
step(metadata, 300); | |
lazyLoad(metadata.active + metadata.numberOfSlides - 1, metadata); | |
}; | |
export const activePrev = metadata => { | |
metadata.stepPos -= metadata.slideWidth * metadata.increment; | |
metadata.com.carousel.children[metadata.active].classList.remove('active'); | |
metadata.active = metadata.active - metadata.increment < 0 ? metadata.com.carousel.children.length - 1 : metadata.active - metadata.increment; | |
metadata.com.carousel.children[metadata.active].classList.add('active'); | |
step(metadata, 0); | |
lazyLoad(metadata.active, metadata); | |
}; | |
export const nextHandler = metadata => { | |
const {com, slideWidth} = metadata; | |
activeNext(metadata); | |
metadata.position -= slideWidth * metadata.increment; | |
slide({com, position: metadata.position}); | |
}; | |
export const previousHandler = metadata => { | |
const {com, slideWidth} = metadata; | |
activePrev(metadata); | |
metadata.position += slideWidth * metadata.increment; | |
slide({com, position: metadata.position}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment