<!-- Path Slider Container -->
<div class="path-slider">
    <!-- Slider items -->
    <a href="#" class="path-slider__item path-slider__item--1"><div class="item__circle"></div></a>
    <a href="#" class="path-slider__item path-slider__item--2"><div class="item__circle"></div></a>
    <a href="#" class="path-slider__item path-slider__item--3"><div class="item__circle"></div></a>
    <a href="#" class="path-slider__item path-slider__item--4"><div class="item__circle"></div></a>
    <a href="#" class="path-slider__item path-slider__item--5"><div class="item__circle"></div></a>
</div>
html, body {
  width: 100%;
  height: 100%;
}

body {
  overflow: hidden;
}

// This slider will be full screen
// The `background-image` will be set using Javascript
.path-slider {
  position: relative;
  width: 100%;
  height: 100%;
  background-position: center;

  &:after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 220px;
    height: 220px;
    background-color: rgba(0, 0, 0, 0.1);
    transform: translate(-50%, -50%);
    border-radius: 100%;
  }
}

// We also need this extra element (generated with Javascript) to fade the images smoothly
.path-slider__background {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-position: center;
}

svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.path-slider__path {
  stroke-width: 10px;
  stroke: rgba(0, 0, 0, 0.5);
  fill: none;
}

.path-slider__item {
  position: absolute;
  left: -100px;
  top: -100px;
  cursor: pointer;
  z-index: 1;
}

.item__circle {
  display: inline-block;
  width: 160px;
  height: 160px;
  background-position: center;
  border-radius: 100%;
  transform: scale(0.5);
  transition: 0.5s transform;
  border: 20px solid rgba(0, 0, 0, 0.5);
  box-shadow: 0 0 0 50px rgba(255, 255, 255, 0.3);
}

.path-slider__current-item {
  z-index: 2;

  .item__circle {
    transform: scale(1);
  }
}

// Defining images

.path-slider__item--1 .item__circle {
  background-image: url("https://cdn.rawgit.com/lmgonzalves/path-slider/aafa43d3/images/img1.jpg");
}

.path-slider__item--2 .item__circle {
  background-image: url("https://cdn.rawgit.com/lmgonzalves/path-slider/aafa43d3/images/img2.jpg");
}

.path-slider__item--3 .item__circle {
  background-image: url("https://cdn.rawgit.com/lmgonzalves/path-slider/aafa43d3/images/img3.jpg");
}

.path-slider__item--4 .item__circle {
  background-image: url("https://cdn.rawgit.com/lmgonzalves/path-slider/aafa43d3/images/img4.jpg");
}

.path-slider__item--5 .item__circle {
  background-image: url("https://cdn.rawgit.com/lmgonzalves/path-slider/aafa43d3/images/img5.jpg");
}
View Compiled
// Function to get a path (string) similar to sin function. Can accept following options that you can use for customization:
// - width: Width to draw the path
// - height: Height to draw the path
// - addWidth: Additional width to overflow actual width
// - controlSep: Bigger values of this parameter will add more curvature, and vice versa
// - curves: Number of curves (iterations) to draw

function getSinPath(options) {
    var _options = options || {};
    var _width = _options.width || window.innerWidth;
    var _height = _options.height || window.innerHeight;
    var _addWidth = _options.addWidth || 100;
    var _controlSep = _options.controlSep || 50;
    var _curves = _options.curves || 2;

    var x = - _addWidth;
    var y = _height / 2;
    var amplitudeX = (_width + _addWidth * 2) / _curves;     // X distance among curve points
    var amplitudeY = _height / 3;                            // Y distance between points and control points

    var path = [];
    path.push('M', x, y);
    var alternateY = true;
    var controlY;
    for (var i = 0; i < _curves; i++) {
        controlY = alternateY ? y - amplitudeY : y + amplitudeY;
        if (i === 0) {
            path.push('C', x + (amplitudeX / 2 - _controlSep), controlY);
        } else {
            path.push('S');
        }
        path.push(x + (amplitudeX / 2 + _controlSep), controlY);
        path.push(x + amplitudeX, y);
        x += amplitudeX;
        alternateY = !alternateY;
    }

    return path.join(' ');
}


(function () {

    // Creating SVG and path elements and insert to DOM

    var svgNS = 'http://www.w3.org/2000/svg';
    var svgEl = document.createElementNS(svgNS, 'svg');

    var pathEl = document.createElementNS(svgNS, 'path');
    // The `getSinPath` function return the `path` in String format
    pathEl.setAttribute('d', getSinPath());
    pathEl.setAttribute('class', 'path-slider__path');

    svgEl.appendChild(pathEl);
    document.body.appendChild(svgEl);


    // Changing `background-image`
    // Firstly, saving the computed `background` of each item, as these are defined in CSS
    // When item is selected, the `background` is set accordingly

    var items = document.querySelectorAll('.path-slider__item');
    var images = [];
    for (var j = 0; j < items.length; j++) {
        images.push(getComputedStyle(items[j].querySelector('.item__circle')).getPropertyValue('background-image'));
    }

    var imgAnimation;
    var lastIndex;
    var setImage = function (index) {
        if (imgAnimation) {
            imgAnimation.pause();
            sliderContainer.style['background-image'] = images[lastIndex];
            sliderContainerBackground.style['opacity'] = 0;
        }
        lastIndex = index;
        sliderContainerBackground.style['background-image'] = images[index];
        imgAnimation = anime({
            targets: sliderContainerBackground,
            opacity: 1,
            easing: 'linear'
        });
    };


    // Adding the extra element needed to fade the images smoothly
    // Also set the image for the initial current item (the first one)

    var sliderContainer = document.querySelector('.path-slider');
    var sliderContainerBackground = document.createElement('div');
    sliderContainerBackground.setAttribute('class', 'path-slider__background');
    setImage(0);
    sliderContainer.appendChild(sliderContainerBackground);


    // Initializing the slider

    var options = {
        startLength: 'center',
        paddingSeparation: 100,
        easing: 'easeOutCubic',
        begin: function (params) {
            // Item get selected, then set the `background` accordingly
            if (params.selected) {
                setImage(params.index);
            }
        }
    };

    var slider = new PathSlider(pathEl, '.path-slider__item', options);


    // Regenerate the SVG `path` and update items position on `resize` event (responsive behavior)

    window.addEventListener('resize', function() {
        pathEl.setAttribute('d', getSinPath());
        slider.updatePositions();
    });

})();
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/animejs@2.2.0/anime.min.js
  2. https://gitcdn.xyz/repo/lmgonzalves/path-slider/master/dist/path-slider.min.js