0

I have a simple slider I've put together using the newish CSS Scroll Snap module.

Now I'm stuck with how to show the user what page they're on. In Javascript one typically does this like this or via bootstrap like this... but I'm scratching my head how to detect and display the index of the current visible slide given that it's only a CSS implementation thus far. Any ideas? Or do I have to revert to a js slider?

payload =  [{
 "url": "https://unsplash.it/801?random",
 "filter": "nashville"
}, {"url": "https://unsplash.it/802?random",
 "filter": "aden"
}, {
 "url": "https://unsplash.it/803?random",
 "filter": "mayfair"
}, {
 "url": "https://unsplash.it/804?random",
 "filter": "lofi"
}, {
 "url": "https://unsplash.it/805?random",
 "filter": "kelvin"
}, {
 "url": "https://unsplash.it/806?random",
 "filter": "mayfair"
}]

const init = function(){
 var slider = document.getElementById("slider");
 for (let i = 0; i < payload.length; i++){
  slider.innerHTML += "<section><figure class='" + payload[i].filter + "'><img src='" + payload[i].url + "' /> </figure></section>"; 
 }
 
 //cssScrollSnapPolyfill()
}
init();
img {
  display: inline-block;
  height: 98vh;
  -o-object-fit: cover;
     object-fit: cover;
  width: 100vw;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  overflow-y: hidden;
}

.slider {
  font-family: sans-serif;
  -ms-scroll-snap-type: x mandatory;
      scroll-snap-type: x mandatory;
  display: flex;
  -webkit-overflow-scrolling: touch;
  overflow-x: scroll;
}

section {
  min-width: 100vw;
  height: 100vh;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  text-align: center;
  position: relative;
}

.pagination {
  display: inline-block;
  position: fixed;
  background: rgba(33, 33, 33, 0.9);
  color: #fff;
  text-align: center;
  border-radius: 13px;
  z-index: 4;
  width: 70px;
  top: 20px;
  right: 20px;
}

span {
  display: inline-block;
  line-height: 28px;
  vertical-align: middle;
}
<div class="slider" id="slider">
 <div class='pagination'> <span>1 / 5</span></div>
</div>
zelusp
  • 3,500
  • 3
  • 31
  • 65

3 Answers3

1

How about using the current scroll of the css scroll snap container and dividing it by the width of a slide?

  • That's a pretty decent idea! I'll start looking into how to do that. Any chance you could provide some code? or css property names I should look into? – zelusp Nov 12 '19 at 21:55
  • I can use `var el = document.querySelector('.slider'); console.log(el.scrollLeft, el.scrollTop);` to get the scroll position but I need a way to fire this every time a scroll snaps.... – zelusp Nov 12 '19 at 21:59
  • 1
    So, you need to use `el.addEventListener('scroll', function() { // here check the scroll position })`. The callback function will be fired every time you scroll. – Andrew Dashkov Nov 12 '19 at 22:02
1

This answer is just an implementation of what has been suggested before by Francesco Manicardi. You could do something like this:

const payload = [{
 "url": "https://unsplash.it/801?random",
 "filter": "nashville"
}, {
 "url": "https://unsplash.it/802?random",
 "filter": "aden"
}, {
 "url": "https://unsplash.it/803?random",
 "filter": "mayfair"
}, {
 "url": "https://unsplash.it/804?random",
 "filter": "lofi"
}, {
 "url": "https://unsplash.it/805?random",
 "filter": "kelvin"
}, {
 "url": "https://unsplash.it/806?random",
 "filter": "mayfair"
}];

const slider = document.getElementById("slider");
const pagination = document.querySelector('.pagination')

const init = function () {
 for (let i = 0; i < payload.length; i++) {
  slider.innerHTML += "<section><figure class='" + payload[i].filter + "'><img src='" + payload[i].url + "' /> </figure></section>";
 }

 initPagination();
}

init();


function initPagination() {
 pagination.innerHTML = `<span>1 / ${payload.length}<span>`;

 slider.addEventListener('scroll', function () {
  pagination.innerHTML = `<span>${getCurrentPageNumber()} / ${payload.length}<span>`;
 });
}

function getCurrentPageNumber() {
 const imgWidth = getSliderWidth() / payload.length;
 const imgShift = imgWidth * 0.5; // when to consider that the page has changed

 return Math.floor((slider.scrollLeft + imgShift) / imgWidth) + 1;
}

function getSliderWidth() {
 let width = 0;

 Array.from(slider.children).forEach(child => {
  const img = child.querySelector('img');

  if (img) {
   width += img.width;
  }
 });

 return width;
}
img {
 display: inline-block;
 height: 98vh;
 -o-object-fit: cover;
 object-fit: cover;
 width: 100vw;
}

* {
 box-sizing: border-box;
 margin: 0;
 padding: 0;
}

body {
 overflow-y: hidden;
}

.slider {
 font-family: sans-serif;
 -ms-scroll-snap-type: x mandatory;
 scroll-snap-type: x mandatory;
 display: flex;
 -webkit-overflow-scrolling: touch;
 overflow-x: scroll;
}

section {
 min-width: 100vw;
 height: 100vh;
 scroll-snap-align: start;
 scroll-snap-stop: always;
 text-align: center;
 position: relative;
}

.pagination {
 display: inline-block;
 position: fixed;
 background: rgba(33, 33, 33, 0.9);
 color: #fff;
 text-align: center;
 border-radius: 13px;
 z-index: 4;
 width: 70px;
 top: 20px;
 right: 20px;
}

span {
 display: inline-block;
 line-height: 28px;
 vertical-align: middle;
}
<div class="slider" id="slider"></div>
<div class='pagination'><span>1 / 5</span></div>

But in this solution we assume, that all the images will be the same width.

Andrew Dashkov
  • 301
  • 1
  • 8
1

Simplifying parts from @Francesco and @Andrew and adding a debounce function, we get this:

payload =  [{
 "url": "https://s3.amazonaws.com/appforest_uf/f1573502006658x593350952181959600/IMG_7552.JPG",
 "filter": "nashville"
}, {"url": "https://source.unsplash.com/featured/?dinner",
 "filter": "aden"
}, {
 "url": "https://source.unsplash.com/featured/?dinner/1",
 "filter": "mayfair"
}, {
 "url": "https://source.unsplash.com/featured/?dinner/2",
 "filter": "lofi"
}, {
 "url": "https://source.unsplash.com/featured/?dinner/3",
 "filter": "kelvin"
}, {
 "url": "https://source.unsplash.com/featured/?lunch/4",
 "filter": "mayfair"
}]

 
const init = function(){
 const slider = document.querySelector('.slider');
 const pagination = document.querySelector('.pagination')
 pagination.innerHTML = `<span>1 / ${payload.length}<span>`;
 for (let i = 0; i < payload.length; i++){
  slider.innerHTML += "<section><figure class='" + payload[i].filter + "'><img src='" + payload[i].url + "' /> </figure></section>";
 }
}
init();

slider.addEventListener('scroll', _.debounce(function() { 
 //console.log(Math.round(slider.scrollLeft / slider.offsetWidth )+1);
 const pagination = document.querySelector('.pagination') 
 pagination.innerHTML = `<span>${Math.round(slider.scrollLeft / slider.offsetWidth )+1} / ${payload.length}<span>`;
}, 200)) //Lower this debounce number and note how the number of scroll events fired increases... We'd like to maintain just one scroll event.
img {
  display: inline-block;
  height: 70vh;
  -o-object-fit: cover;
     object-fit: cover;
  width: 100vw;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  overflow-y: hidden;
}

.slider {
  font-family: sans-serif;
  -ms-scroll-snap-type: x mandatory;
      scroll-snap-type: x mandatory;
  display: flex;
  -webkit-overflow-scrolling: touch;
  overflow-x: scroll;
}

section {
  min-width: 100vw;
  height: 100vh;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  text-align: center;
  position: relative;
}

.pagination {
  display: inline-block;
  position: fixed;
  background: rgba(33, 33, 33, 0.9);
  color: #fff;
  text-align: center;
  border-radius: 13px;
  z-index: 4;
  width: 70px;
  top: 20px;
  right: 20px;
}

span {
  display: inline-block;
  line-height: 28px;
  vertical-align: middle;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

<div class="slider" id="slider">
 <div class='pagination'>
 </div>
</div>
zelusp
  • 3,500
  • 3
  • 31
  • 65