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; } | |
} |