Slider animation based on a Dribbble shot by Andreas Storm: https://dribbble.com/shots/2591468-Paging-with-Principle-App
A Pen by Tobias Reich on CodePen.
| <div class="center"> | |
| <div class="slider" data-pos="0"> | |
| <div class="slider__slides"> | |
| <div class="slider__slide"></div> | |
| <div class="slider__slide"></div> | |
| <div class="slider__slide"></div> | |
| <div class="slider__slide"></div> | |
| </div> | |
| <div class="slider__dots"> | |
| <a href="#" class="slider__indicator"></a> | |
| <a href="#" class="slider__dot" data-pos="0"></a> | |
| <a href="#" class="slider__dot" data-pos="1"></a> | |
| <a href="#" class="slider__dot" data-pos="2"></a> | |
| <a href="#" class="slider__dot" data-pos="3"></a> | |
| </div> | |
| </div> | |
| </div> |
Slider animation based on a Dribbble shot by Andreas Storm: https://dribbble.com/shots/2591468-Paging-with-Principle-App
A Pen by Tobias Reich on CodePen.
| let dots = 4; | |
| let sliderElem = document.querySelector('.slider') | |
| let dotElems = sliderElem.querySelectorAll('.slider__dot') | |
| let indicatorElem = sliderElem.querySelector('.slider__indicator') | |
| Array.prototype.forEach.call(dotElems, (dotElem) => { | |
| dotElem.addEventListener('click', (e) => { | |
| let currentPos = parseInt(sliderElem.getAttribute('data-pos')) | |
| let newPos = parseInt(dotElem.getAttribute('data-pos')) | |
| let newDirection = (newPos > currentPos ? 'right' : 'left') | |
| let currentDirection = (newPos < currentPos ? 'right' : 'left') | |
| indicatorElem.classList.remove(`slider__indicator--${ currentDirection }`) | |
| indicatorElem.classList.add(`slider__indicator--${ newDirection }`) | |
| sliderElem.setAttribute('data-pos', newPos) | |
| }) | |
| }) |
| .slider { | |
| $dots : 4; | |
| $dotSize : 1; | |
| $dotMargin : .5; | |
| $duration : .3s; | |
| $timingEase : cubic-bezier(.51, .92, .24, 1); | |
| $timingBounce : cubic-bezier(.51, .92, .24, 1.15); | |
| position: relative; | |
| width: 100%; | |
| height: 100%; | |
| overflow: hidden; | |
| &__slides { | |
| position: relative; | |
| width: $dots * 100%; | |
| height: 100%; | |
| transition: transform $duration $timingEase; | |
| will-change: transform; | |
| } | |
| @for $i from 0 to $dots { | |
| $slide : 100% / $dots; | |
| $left : $slide * $i; | |
| &[data-pos="#{ $i }"] &__slides { | |
| transform: translateX(-$left); | |
| } | |
| } | |
| &__slide { | |
| float: left; | |
| width: 100% / $dots; | |
| height: 100%; | |
| } | |
| &__dots { | |
| display: flex; | |
| position: absolute; | |
| bottom: 1.5em; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| } | |
| &__dot { | |
| display: block; | |
| margin: 0 #{ $dotMargin }em; | |
| width: #{ $dotSize }em; | |
| height: #{ $dotSize }em; | |
| background: rgba(255, 255, 255, .5); | |
| border-radius: 100px; | |
| } | |
| &__indicator { | |
| @extend .slider__dot; | |
| position: absolute; | |
| background: white; | |
| width: auto; | |
| &--left { | |
| transition: left $duration $timingBounce, right $duration $duration/3 $timingBounce; | |
| } | |
| &--right { | |
| transition: left $duration $duration/3 $timingBounce, right $duration $timingBounce; | |
| } | |
| } | |
| @for $i from 0 to $dots { | |
| $dot : $dotSize + $dotMargin * 2; | |
| $left : $dot * $i; | |
| $right : $dot * ($dots - $i) - $dot; | |
| &[data-pos="#{ $i }"] &__indicator { | |
| left: #{ $left }em; | |
| right: #{ $right }em; | |
| } | |
| } | |
| } | |
| // Custom colors and styling for the demo | |
| .center { | |
| display: flex; | |
| height: 100vh; | |
| justify-content: center; | |
| align-items: center; | |
| background: #333; | |
| } | |
| .slider { | |
| max-width: 600px; | |
| max-height: 400px; | |
| box-shadow: 0 0 20px rgba(0, 0, 0, .8); | |
| &__slide:nth-child(1) { background: #309954; } | |
| &__slide:nth-child(2) { background: #FFBD3C; } | |
| &__slide:nth-child(3) { background: #F8593E; } | |
| &__slide:nth-child(4) { background: #4086FA; } | |
| } |