Slider - Vanilla JS

  • horizontal Slidert

  • vertical Slider

Slider auf JavaScript Basis habe ich schon einige geschrieben, nun aber versuche ich es mit einem anderen Ansatz, für das Sliden möchte ich das über CSS gestaltete Scrollen nutzen, dadurch ist die Umsetzung der Touch- und Wheel-Events nicht erforderlich, da es im Standard schon funktioniert.

Als Methoden zur Animierung sollen dann zusätzlich zum Wischen und Mousewheel/Mouspad (2-Finger), noch Pfeile (vorwärts/rückwärts), Bubbes (Punkte zur Steuerung und Anzeige aktiver Slide) oder Thumbnails (Bilder zur Steuerung und Anzeige aktiver Slide) und eine Autoplay Funktion (optional mit Play & Pause Button).

Das Sliden soll in horzontaler aber auch vertikaler Richtung funktionieren und Hintergrund-Bilder unterstützen.

Für ältere Browser wie den Internet Explorer habe ich noch den smooth scroll über requestAnimationFrame realisiert.

HTML:
flex-slider.htm HTML (1,55 kByte) 19.07.2021 13:49
<!DOCTYPE html >
<html lang="de">
  <head>
    <meta charset="utf-8" />
    <title>Slider</title>
    <link rel="stylesheet" href="../tabsheets/tabsheet.css" />
  </head>
  <body>
    <ul class="tabsheets">
      <li class="active">
        <h4 class="sheet-header">horizontal Slidert</h4>
        <div class="flex-slider" data-options="thumbs arrows autoplay play_pause"><ul             
          <li data-backgroundimage="/media/IMG_6867.jpg"></li>
          <li data-backgroundimage="/media/IMG_6868.jpg"></li>
          <li data-backgroundimage="/media/IMG_6869.jpg"></li>
          <li data-backgroundimage="/media/IMG_6870.jpg"></li>
          <li data-backgroundimage="/media/IMG_6871.jpg"></li>
          <li data-backgroundimage="/media/IMG_6872.jpg"></li>
        </div>
      </li>
    </ul>
  </body>
  <li>
    <h4 class="sheet-header">vertical Slider</h4>
    <div class="flex-slider vertical" data-options="thumbs arrows autoplay">
      <ul>
        <li data-backgroundimage="/media/IMG_6867.jpg"></li>
        <li data-backgroundimage="/media/IMG_6868.jpg"></li>
        <li data-backgroundimage="/media/IMG_6869.jpg"></li>
        <li data-backgroundimage="/media/IMG_6870.jpg"></li>
        <li data-backgroundimage="/media/IMG_6871.jpg"></li>
        <li data-backgroundimage="/media/IMG_6872.jpg"></li>
      </ul>
    </div>
  </li>
  <script src="../tabsheets/tabsheet.js"></script>
  <script src="../../scripts/viewport.js"></script>
  <script src="flex-slider.js"></script>
</html>
JavaScript:
flex-slider.js JavaScript (13,23 kByte) 19.07.2021 14:44
// coding: utf-8
/*! Created by: Udo Schmal | https://www.gocher.me/ */
(function() {
  'use strict';

  function Slider(el) {
    var slider = el; // slider div
    var slides = el.querySelector('ul'); // slides ul list
    if (!slides.classList.contains('slides')) {
      slides.classList.add('slides');
    }
    var items = slides.children; // slides li items
    var controls = null; // bubbles/thumbs ul list
    var controlItems = []; // bubbles/thumbs li items
    var prev = null; // previous div button
    var next = null; // next div button
    var play = null; // play and pause div button
    var active = 0; // active slide position counter

    // load stylesheet
    if (!document.getElementById('flex-slider.css')) {
      var style = document.createElement("link");
      style.type = 'text/css';
      style.href = '/code/flex-slider/flex-slider.css';
      style.id = 'flex-slider.css';
      style.rel = 'stylesheet';
      document.head.appendChild(style);
    }
    // get settings from dom
    var direction = el.classList.contains('vertical') ? 'vertical' : 'horizontal';
    if ((direction == 'horizontal') && !el.classList.contains('horizontal')) {
      el.classList.add('horizontal');
    }
    var thumbs = false, bubbles = false, autoplay = false, arrows = false, playpause = false;
    if (el.dataset.options) {
      thumbs = el.dataset.options.indexOf('thumbs') !== -1;
      bubbles = el.dataset.options.indexOf('bubbles') !== -1;
      autoplay = el.dataset.options.indexOf('autoplay') !== -1;
      arrows = el.dataset.options.indexOf('arrows') !== -1;
      playpause = el.dataset.options.indexOf('play_pause') !== -1;
    }
    var interval = 3000;
    if (autoplay) {
      if (el.dataset.interval) {
        interval = parseInt(el.dataset.interval, 10);
        if (isNaN(interval)) {
          interval = 3000;
        }
      }
    }
    // set background image if no viewport support
    for(let i = 0; i < items.length; i++) {
      let li = document.createElement('li');
      if (items[i].dataset.backgroundimage) {
        items[i].style.backgroundImage = "url('" + items[i].dataset.backgroundimage + "')";
      }
    }
    // scroll into parent view
    if ('scrollBehaviors' in document.documentElement.style) {
      Element.prototype.scrollIntoParentView = function () {
        this.parentNode.scrollTo({top: this.offsetTop, left: this.offsetLeft, behavior: 'smooth'});
      };
    } else {
      // old browser smooth scroll support
      window.requestAnimFrame = (function () {
        return window.requestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          function(callback){
            window.setTimeout(callback, 1000 / 60);
          };
      })();
      Element.prototype.scrollIntoParentView = function () {
        var top = this.offsetTop, left = this.offsetLeft, el = this.parentNode, attr;
        if (top == 0 && left == 0) {
          attr = (el.scrollLeft == 0 ? 'scrollTop' : 'scrollLeft');
        } else {
          attr = (top == 0 ? 'scrollLeft' : 'scrollTop');
        }
        var from = el[attr] || 0, to = (attr == 'scrollLeft' ? left : top);
        if (from == to) { return; }
        var start = Date.now();
        var tick = function () {
          var t = Math.min(1, ((Date.now() - start) / 1000));
          var eased =  t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1;
          var val = (eased * (to - from)) + from;
          el[attr] = Math.floor(val);
          if (t<1) {
            window.requestAnimFrame(tick);
          }
        };
        tick();
      };
      // old browser touch support
      var touch = {
        fingerCount: 0,
        startX: 0,
        startY: 0,
        curX: 0,
        curY: 0,
        deltaX: 0,
        deltaY: 0,
        horzDiff: 0,
        vertDiff: 0,
        swipeLength: 0,
        swipeAngle: 0,
        swipeDirection: 0
      };
      function determineSwipeDirection (event) {
        var X = touch.startX - touch.curX;
        var Y = touch.curY - touch.startY;
        touch.swipeLength = Math.round(Math.sqrt(Math.pow(X,2) + Math.pow(Y,2))); //the distance - rounded - in pixels
        var r = Math.atan2(Y, X); //angle in radians (Cartesian system)
        touch.swipeAngle = Math.round(r * 180 / Math.PI); //angle in degrees
        if (touch.swipeAngle < 0) {
          touch.swipeAngle = 360 - Math.abs(touch.swipeAngle);
        };
        if ((touch.swipeAngle <= 45) && (touch.swipeAngle >= 0)) {
          touch.swipeDirection = 'left';
        } else if ((touch.swipeAngle <= 360) && (touch.swipeAngle >= 315)) {
          touch.swipeDirection = 'left';
        } else if ((touch.swipeAngle >= 135) && (touch.swipeAngle <= 225)) {
          touch.swipeDirection = 'right';
        } else if ((touch.swipeAngle > 45) && (touch.swipeAngle < 135)) {
          touch.swipeDirection = 'down';
        } else {
          touch.swipeDirection = 'up';
        }
        if ((direction == 'horizontal' && touch.swipeDirection === 'left') ||
            (direction == 'vertical' && touch.swipeDirection === 'up')) {
          event.preventDefault();
          if (active < items.length-1) {
            items[++active].scrollIntoParentView();
          }
        } else if ((direction == 'horizontal' && touch.swipeDirection === 'right') ||
                   (direction == 'vertical' && touch.swipeDirection === 'down')) {
          event.preventDefault();
          if (active > 0) {
            items[--active].scrollIntoParentView();
          }
        }
      }
      function touchStart (event) {
        // get the total number of fingers touching the screen
        touch.fingerCount = event.touches.length;
        // since we're looking for a swipe (single finger) and not a gesture (multiple fingers),
        // check that only one finger was used
        if (touch.fingerCount === 1) {
          // get the coordinates of the touch
          touch.startX = event.touches[0].pageX;
          touch.startY = event.touches[0].pageY;
        } else {
          // more than one finger touched so cancel
          touchCancel(event);
        }
      }
      function touchEnd (event) {
        event.preventDefault();
        // check to see if more than one finger was used and that there is an ending coordinate
        if (touch.fingerCount === 1 && touch.curX !== 0) {
          // use the Distance Formula to determine the length of the swipe
          touch.swipeLength = Math.round(Math.sqrt(Math.pow(touch.curX - touch.startX,2) + Math.pow(touch.curY - touch.startY,2)));
          // if the user swiped more than the minimum length, perform the appropriate action
          if (touch.swipeLength >= 72) { // the shortest distance the user may swipe
            determineSwipeDirection(event);
          }
        }
        touchCancel(event); // reset the variables
      }
      function touchMove (event) {
        //event.preventDefault();
        if (event.touches.length === 1) {
          touch.curX = event.touches[0].pageX;
          touch.curY = event.touches[0].pageY;
          //determineSwipeDirection(event);
        } else {
          touchCancel(event);
        }
      }
      function touchCancel (event) {
        // reset the variables back to default values
        touch.fingerCount = 0;
        touch.startX = 0;
        touch.startY = 0;
        touch.curX = 0;
        touch.curY = 0;
        touch.deltaX = 0;
        touch.deltaY = 0;
        touch.horzDiff = 0;
        touch.vertDiff = 0;
        touch.swipeLength = 0;
        touch.swipeAngle = null;
        touch.swipeDirection = null;
      }
      slides.addEventListener('touchstart', touchStart);
      slides.addEventListener('touchend', touchEnd);
      slides.addEventListener('touchmove', touchMove);
      slides.addEventListener('touchcancel', touchCancel);
    }

    // slide and snap
    function startScroll() {
      for (let i = 0; i < items.length; i++) {
        items[i].classList.remove('active');
        if (controls) {
          controlItems[i].classList.remove('active');
        }
      }
    }
    function stopScroll() {
      scrollTimer = null;
      for (let i = 0; i < items.length; i++) {
        if ((Math.abs(slides.scrollTop - items[i].offsetTop) < 5) &&
            (Math.abs(slides.scrollLeft - items[i].offsetLeft) < 5)) {
          if (controls) {
            controlItems[i].classList.add('active');
          }
          items[i].classList.add('active');
          active = i;
        }
      }
      if (prev) {
        if (active == 0) {
          prev.classList.add('hidden');
        } else {
          prev.classList.remove('hidden');
        }
      }
      if (next) {
        if (active == items.length-1) {
          next.classList.add('hidden');
        } else {
          next.classList.remove('hidden');
        }
      }
    }
    var scrollTimer;
    function sliderScroll(event) {
      if (scrollTimer) {
        clearTimeout(scrollTimer);
      } else {
        startScroll();
      }
      scrollTimer = setTimeout(stopScroll, 20);
    }
    slides.addEventListener('scroll', sliderScroll);

    // auto slide
    var slideshow = null;
    var restartTimer;
    function startSlideshow () {
      if (restartTimer) {
        clearTimeout(restartTimer);
      }
      if (!slideshow) {
        slideshow = window.setInterval(function () {
          if (active+1 < items.length) {
            items[++active].scrollIntoParentView();
          } else {
            items[0].scrollIntoParentView();
          }
        }, interval);
      }
    }
    function stopSlideShow () {
      if (slideshow) {
        window.clearInterval(slideshow);
        slideshow = null;
      }
    }
    function pauseSlideshow () {
      if (slideshow) {
        window.clearInterval(slideshow);
        slideshow = null;
        restartTimer = setTimeout(startSlideshow, 5000);
      };
    }
    function toggleSlideshow () {
      if (play.classList.contains('play')) {
        startSlideshow();
        play.classList.remove('play');
        play.classList.add('pause');
      } else {
        stopSlideShow();
        play.classList.remove('pause');
        play.classList.add('play');
      }
    }
    if (autoplay) {
      startSlideshow();
      // mousepad, wheel scroll
      el.addEventListener('wheel', pauseSlideshow, true);
      // touch screen
      el.addEventListener('touch', pauseSlideshow, true);
      el.addEventListener('touchstart', pauseSlideshow, true);
      el.addEventListener('touchend', pauseSlideshow, true);
      el.addEventListener('touchmove', pauseSlideshow, true);
      el.addEventListener('touchcancel', pauseSlideshow, true);
    }
    // play and pause button
    if (playpause) {
      play = document.createElement('div');
      play.className = autoplay ? 'pause' : 'play';
      play.addEventListener('click', toggleSlideshow);
      el.appendChild(play);
    }

    // control bubbles
    if (bubbles || thumbs) {
      controls = document.createElement('ul');
      controls.classList.add('controls');
      controls.classList.add(thumbs ? 'thumbs' : 'bubbles');
      el.appendChild(controls);
      function controlClick(event) {
        if (autoplay) {
          pauseSlideshow();
        }
        this.slide.scrollIntoParentView();
      }
      for(let i = 0; i < items.length; i++) {
        let li = document.createElement('li');
        li.slide = items[i];
        li.setAttribute('tabindex', '0');
        if (thumbs) {
          let file = null;
          if (items[i].dataset.backgroundimage) {
            file = items[i].dataset.backgroundimage;
          } else {
            let img = items[i].querySelector('img');
            if (img) {
              if (img.src && (img.src != 'data:,')) {
                file = img.src;
              } else if (img.dataset.src) {
                file = img.dataset.src;
              }
            }
          }
          if (file) {
            // load thumbnails
            //file = file.substr(0, file.lastIndexOf('.')) + '~120' + file.substr(file.lastIndexOf('.'));
            li.style.backgroundImage = "url('" + file + "')";
          }
        }
        li.addEventListener('click', controlClick);
        controls.appendChild(li);
      }
      controlItems = controls.children;
      controlItems[active].classList.add('active');
    }

    // previous and next button
    if (arrows) {
      prev = document.createElement('div');
      prev.className = 'prev';
      function prevClick() {
        if (autoplay) {
          pauseSlideshow();
        }
        items[--active].scrollIntoParentView();
      }
      prev.addEventListener('click', prevClick);
      el.appendChild(prev);

      next = document.createElement('div');
      next.className = 'next';
      function nextClick() {
        if (autoplay) {
          pauseSlideshow();
        }
        items[++active].scrollIntoParentView();
      }
      next.addEventListener('click', nextClick);
      el.appendChild(next);
    }

  }
  var els = document.querySelectorAll('.flex-slider'), cnt = els.length, i;
  if (els.length > 0) {
    for (i=0; i < cnt; i++) {
      new Slider(els[i]);
    }
    // accessibility: handle tabindex enter keydown event
    document.addEventListener('keydown', function(event) {
      if (event.key === "Enter") {
        if (event.target.parentNode && event.target.parentNode.classList.contains('controls')) {
          event.target.click();
        }
      }
    });
  }
})();
StyleSheet:
flex-slider.css StyleSheet (4,08 kByte) 19.07.2021 15:34
.flex-slider {
  position: relative;
  overflow: hidden;
  width: 100%;
  height: 0;
  min-height: 1px;
  padding-top: 44%;
}
.flex-slider > .slides {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: 0;
  list-style-type: none;
  scroll-behavior: smooth;
  /*overflow-scrolling: auto;*/
  /* hide IE scrollbar */
  -ms-overflow-style: none;
}
/* hide WebKit scrollbar */
.flex-slider > .slides::-webkit-scrollbar {
  display: none;
}
.flex-slider.horizontal > .slides {
  bottom: -12px; /* scrollbar fix */
  display: flex;
  scroll-snap-type: x mandatory;
  -ms-scroll-snap-type: x mandatory;
  overflow-x: scroll;
  overflow-y: hidden;
}
.flex-slider.vertical > .slides {
  right: -12px; /* scrollbar fix */
  scroll-snap-type: y mandatory;
  -ms-scroll-snap-type: y mandatory;
  overflow-y: scroll;
  overflow-x: hidden;
}
.flex-slider .slides > li {
  position: relative;
  margin: 0;
  padding: 0;
  scroll-snap-align: start;
  /* for background-image */
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
}
.flex-slider.horizontal .slides > li {
  width: 100%;
  height: auto;
  display: inline-block;
  flex-shrink: 0;
}
.flex-slider.vertical .slides > li {
  height: 100%;
  width: auto;
}
.flex-slider > .slides > li > img {
  width: 100%;
  height: auto;
}

.flex-slider > .controls {
  right: 0;
}
.flex-slider.horizontal > .controls {
  position: absolute;
  left: 0;
  bottom: 15px;
  text-align: center;
}
.flex-slider.vertical > .controls {
  position: absolute;
  top: 50%;
  right: 25px;
  transform: translate(0, -50%);
  display: block;
}
.flex-slider.vertical > .controls::after {
  display: table;
  content: '';
  clear: both;
}
.flex-slider.horizontal > .controls.thumbs {
  height: 10%;
}
.flex-slider.vertical > .controls.thumbs {
  width: 10%;
}
.flex-slider > .controls > li {
  display: inline-block;
  outline: none;
  background-color: #fff;
  opacity: 0.8;
  cursor: pointer;
}
.flex-slider.horizontal > .controls > li {
  display: inline-block;
  margin: 0 5px;
}
.flex-slider.vertical > .controls > li {
  display: block;
  margin: 5px 0;
}

.flex-slider > .controls.bubbles > li {
  border: 1px solid #000;
  transition: border 500ms ease-out;
  width: 0.85em;
  height: 0.85em;
  border-radius: 50%;
}
.flex-slider > .controls.thumbs > li {
  border: 2px solid #000;
  transition: border 500ms ease-out;
}
.flex-slider > .controls.bubbles > li.active {
  background-color: #000;
}
.flex-slider > .controls.thumbs > li.active {
  border-color: #fff;
}
.flex-slider > .controls.thumbs > li {
  position: relative;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
}
.flex-slider.horizontal > .controls.thumbs > li {
  width: 10%;
  height: 100%;
}
.flex-slider.vertical > .controls.thumbs > li {
  padding-top: 44%;
  width: 100%;
}

.flex-slider > .prev, .flex-slider > .next, .flex-slider > .play, .flex-slider > .pause {
  cursor: pointer;
  position: absolute;
}
.flex-slider.horizontal > .prev, .flex-slider.horizontal > .next {
  top: 50%;
  transform: translate(0, -50%);
}
.flex-slider.vertical > .prev, .flex-slider.vertical > .next {
  left: 50%;
  transform: translate(-50%, 0);
}
.flex-slider.horizontal > .prev {
  left: 15px;
}
.flex-slider.horizontal > .next {
  right: 15px;
}
.flex-slider.vertical > .prev {
  top: 15px;
}
.flex-slider.vertical > .next {
  bottom: 15px;
}
.flex-slider > .prev.hidden, .flex-slider > .next.hidden {
  display: none;
}
.flex-slider > .prev::after, .flex-slider > .next::after, .flex-slider > .play::after, .flex-slider > .pause::after {
  display: inline-block;
  color: #fff;
  font-size: 2em;
}
.flex-slider.horizontal > .prev::after {
  content: '\140a';
}
.flex-slider.horizontal > .next::after {
  content: '\1405';
}
.flex-slider.vertical > .prev::after {
  content: '\1403';
}
.flex-slider.vertical > .next::after {
  content: '\1401';
}

.flex-slider > .play, .flex-slider > .pause {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.flex-slider:hover > .play::after {
  content: '\23EF';
}
.flex-slider:hover > .pause::after {
  content: '\23f8';
}

Author: , published: , last modified:

Kontakt

Udo Schmal

Udo Schmal
Softwareentwickler
Olvengraben 41
47608 Geldern
Nordrhein-Westfalen
Germany





+49 2831 9776557
+49 1575 0663676
+49 2831 1328709
SMS
WhatsApp

Instagram Profile
vCard 3.0

Service Infos

CMS Info Product Name:
UDOs Webserver
Version:
0.5.0.101
Description:
All in one Webserver
Copyright:
Udo Schmal
Compilation:
Sun, 18. Jul 2021 17:42:24
Development Info Compiler:
Free Pascal FPC 3.3.1
compiled for:
OS:Linux, CPU:x86_64
System Info OS:
Ubuntu 20.04.2 LTS (Focal Fossa)
Hardware Info Model:
Hewlett-Packard HP Pavilion dv7 Notebook PC
CPU Name:
Intel(R) Core(TM) i5-2450M CPU @ 2.50GHz
CPU Type:
x86_64, 1 physical CPU(s), 2 Core(s), 4 logical CPU(s), 1121.995 MHz