<main id="slide">
<section class="slides">
<article data-slide="0" class="slide slide--beauty slide--active">
<svg class="svg" viewBox="0 0 1500 750">
<clipPath id="clip-00">
<path d="">
<animate
dur=""
repeatCount=".5"
attributeName="d"
restart="whenNotActive"
values=""
keySplines=""
calcMode="spline"
fill="freeze">
</path>
</clipPath>
<image clip-path="url(#clip-00)" height="100%" width="100%" xlink:href="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/beauty.jpg" />
</svg>
</article>
<div class="bg-slide bg-slide--beauty bg-slide--active"></div>
<article data-slide="1" class="slide slide--girl">
<svg class="svg" viewBox="0 0 1500 750">
<clipPath id="clip-01">
<path d="">
<animate
dur=""
repeatCount=".5"
attributeName="d"
restart="whenNotActive"
values=""
keySplines=""
calcMode="spline"
fill="freeze">
</path>
</clipPath>
<image clip-path="url(#clip-01)" height="100%" width="100%" xlink:href="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/girl.jpg" />
</svg>
</article>
<div class="bg-slide bg-slide--girl"></div>
<article data-slide="2" class="slide slide--model">
<svg class="svg" viewBox="0 0 1500 750">
<clipPath id="clip-02">
<path d="">
<animate
dur=""
repeatCount=".5"
attributeName="d"
restart="whenNotActive"
values=""
keySplines=""
calcMode="spline"
fill="freeze">
</path>
</clipPath>
<image clip-path="url(#clip-02)" height="100%" width="100%" xlink:href="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/model.jpg" />
</svg>
</article>
<div class="bg-slide bg-slide--model"></div>
<article data-slide="3" class="slide slide--snoe">
<svg class="svg" viewBox="0 0 1500 750">
<clipPath id="clip-03">
<path d="">
<animate
dur=""
repeatCount=".5"
attributeName="d"
restart="whenNotActive"
values=""
keySplines=""
calcMode="spline"
fill="freeze">
</path>
</clipPath>
<image clip-path="url(#clip-03)" height="100%" width="100%" xlink:href="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/snow.jpg" />
</svg>
</article>
<div class="bg-slide bg-slide--snow"></div>
<article data-slide="4" class="slide slide--woman">
<svg class="svg" viewBox="0 0 1500 750">
<clipPath id="clip-04">
<path d="">
<animate
dur=""
repeatCount=".5"
attributeName="d"
restart="whenNotActive"
values=""
keySplines=""
calcMode="spline"
fill="freeze">
</path>
</clipPath>
<image clip-path="url(#clip-04)" height="100%" width="100%" xlink:href="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/woman.jpg" />
</svg>
</article>
<div class="bg-slide bg-slide--woman"></div>
</section>
<nav class="nav">
<button class="slide__left btn" data-goto="3" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H6M12 5l-7 7 7 7"/></svg>
</button>
<ul class="slide__nav">
<li><button class="btn btn--active js-nav" data-goto="0" type="button"><span class="visually-hidden">Slide </span>1</button></li>
<li><button class="btn js-nav" data-goto="1" type="button"><span class="visually-hidden">Slide </span>2</button></li>
<li><button class="btn js-nav" data-goto="2" type="button"><span class="visually-hidden">Slide </span>3</button></li>
<li><button class="btn js-nav" data-goto="3" type="button"><span class="visually-hidden">Slide </span>4</button></li>
<li><button class="btn js-nav" data-goto="4" type="button"><span class="visually-hidden">Slide </span>5</button></li>
</ul>
<button class="slide__right btn" data-goto="1" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h13M12 5l7 7-7 7"/></svg>
</button>
</nav>
</main>
<div class="open-modal">
<button class="js-open-modal" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"></line><line x1="4" y1="10" x2="4" y2="3"></line><line x1="12" y1="21" x2="12" y2="12"></line><line x1="12" y1="8" x2="12" y2="3"></line><line x1="20" y1="21" x2="20" y2="16"></line><line x1="20" y1="12" x2="20" y2="3"></line><line x1="1" y1="14" x2="7" y2="14"></line><line x1="9" y1="8" x2="15" y2="8"></line><line x1="17" y1="16" x2="23" y2="16"></line></svg>
</button>
<button class="js-close-modal" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
<div class="modal">
<div class="modal__content">
<section class="section section--active">
<p class="section__title">Quadratic bottom</p>
<img src="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/quadratic-bottom.png" alt="">
<footer class="dur">
<label class="label-dur">
Tiempo animación (ms)
<input class="btn-dur js-btn-dur" type="number" min="100" max="30000" value="1800">
</label>
<button class="btn-save" data-setting="setting01" type="button">Usar</button>
</footer>
</section>
<section class="section">
<p class="section__title">Quadratic left</p>
<img src="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/quadratic-left.png" alt="">
<footer class="dur">
<label class="label-dur">
Tiempo animación (ms)
<input class="btn-dur js-btn-dur" type="number" min="100" max="30000" value="2000">
</label>
<button class="btn-save" data-setting="setting02" type="button">Usar</button>
</footer>
</section>
<section class="section">
<p class="section__title">Curve</p>
<img src="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/curve.png" alt="">
<footer class="dur">
<label class="label-dur">
Tiempo animación (ms)
<input class="btn-dur js-btn-dur" type="number" min="100" max="30000" value="2300">
</label>
<button class="btn-save" data-setting="setting03" type="button">Usar</button>
</footer>
</section>
<section class="section">
<p class="section__title">Morph</p>
<img src="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/morph.png" alt="">
<footer class="dur">
<label class="label-dur">
Tiempo animación (ms)
<input class="btn-dur js-btn-dur" type="number" min="100" max="30000" value="2500">
</label>
<button class="btn-save" data-setting="setting04" type="button">Usar</button>
</footer>
</section>
<section class="section">
<p class="section__title">Rhombus</p>
<img src="https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/rhombus.png" alt="">
<footer class="dur">
<label class="label-dur">
Tiempo animación (ms)
<input class="btn-dur js-btn-dur" type="number" min="100" max="30000" value="1500">
</label>
<button class="btn-save" data-setting="setting05" type="button">Usar</button>
</footer>
</section>
</div>
</div>
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400&display=swap');
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
display: block;
}
* {
padding: 0;
margin: 0;
&, &::after, *::before {
box-sizing: border-box;
}
}
ul,
figure {
margin-block-start: 0;
margin-block-end: 0;
margin-inline-start: 0;
margin-inline-end: 0;
}
ol, ul {
list-style: none;
}
button,
[type="button"],
[type="reset"],
[type="submit"] {
cursor: pointer;
border: 0;
padding: 0;
background-color: transparent;
-webkit-appearance: button;
}
html {
font-family: 'Open Sans', sans-serif;
font-weight: 400;
line-height: 1.5;
scroll-behavior: smooth;
-webkit-tap-highlight-color: transparent;
}
.visually-hidden {
position: absolute;
left: -10000px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
}
.slides {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
grid-gap: 0;
}
.slide {
grid-area: 1 / 1 / 1 / 1;
visibility: hidden;
&--active {
visibility: visible;
}
&__nav {
display: flex;
justify-content: center;
z-index: 2;
margin: 0 .7em;
li {
display: flex;
justify-content: center;
align-items: center;
}
}
&__left,
&__right {
svg {
width: 1.2em;
height: 1.2em;
pointer-events: none;
}
}
.svg {
display: block;
max-height: calc(100vh - 2.5em);
margin: auto;
}
}
.bg-slide {
grid-area: 1 / 1 / 1 / 1;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
visibility: hidden;
&--active {
visibility: visible;
}
&--beauty {
background-image: url(https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/beauty.jpg);
}
&--girl {
background-image: url(https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/girl.jpg);
}
&--model {
background-image: url(https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/model.jpg);
}
&--snow {
background-image: url(https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/snow.jpg);
}
&--woman {
background-image: url(https://raw.githubusercontent.com/ivanalbizu/Slider-SVG-Animate-Path/master/src/img/woman.jpg);
}
}
.nav {
display: flex;
justify-content: center;
}
.btn {
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, .9);
color: #fff;
border: 1px solid rgba(0, 0, 0, .9);
width: 2em;
height: 2em;
margin: 0.5em .2em;
transition: all .7s ease-in 0s;
&--active {
background-color: #fff;
color: rgba(0, 0, 0, 0.9);
transition: all .2s ease-in 0s;
pointer-events: none;
}
}
// Not necessary. Use to show example paths animations
.open-modal {
position: fixed;
top: 10px;
right: 18px;
background-color: rgba(0, 0, 0, .9);
border: 1px solid #fff;
padding: .3em;
z-index: 10;
display: flex;
button {
display: flex;
}
.js-close-modal {
display: none;
}
}
.modal {
position: fixed;
right: 0;
width: 350px;
top: 0;
bottom: 0;
z-index: -9;
visibility: hidden;
img {
max-width: 100%;
}
&__content {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
overflow-y: auto;
background-color: rgba(0, 0, 0, .85);
padding: 2.5em 1.5em;
height: 100%;
transform: translateY(-10px);
transition: transform .3s ease;
.section {
color: #fff;
padding: .7em 1em 1em;
border: 1px dashed #fff;
margin-bottom: 2em;
&__title {
font-size: .8em;
letter-spacing: .2px;
text-align: center;
margin-bottom: .7em;
text-transform: uppercase;
}
&--active {
border-style: solid;
}
}
}
.dur {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-top: .5em;
}
.btn-save {
color: #fff;
border: 1px solid #fff;
padding: .4em 2.2em;
text-transform: uppercase;
}
.btn-dur {
border: 0;
padding: .45em;
margin-top: .4em;
}
.label-dur {
font-size: .7em;
}
}
.modal-active {
margin-right: 340px;
.open-modal {
.js-close-modal {
display: flex;
}
.js-open-modal {
display: none;
}
}
.modal {
z-index: 9;
visibility: visible;
&__content {
transform: translateY(0);
transition: transform .3s ease;
}
}
}
View Compiled
// SVG Animations tuto
// https://codeburst.io/svg-morphing-the-easy-way-and-the-hard-way-c117a620b65f
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate
// SVG paths builder
// https://codepen.io/anthonydugois/full/mewdyZ
// Timing functions:
// http://franzheidl.github.io/keysplines/
// Settings
const setting01 = {
dur: 1800,
paths: [
"M 0 750 Q 150 750 200 750 Q 250 750 300 750 Q 350 750 400 750 C 500 750 750 750 1500 750 L 1500 750 L 0 750 Z",
"M 0 300 Q 150 100 200 300 Q 250 600 300 300 Q 350 50 400 300 C 500 750 750 50 1500 300 L 1500 750 L 0 750 Z",
"M 0 0 Q 150 0 200 0 Q 250 0 300 0 Q 350 0 400 0 C 500 0 750 0 1500 0 L 1500 750 L 0 750 Z"
],
keySplines: [
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39"
]
}
const setting02 = {
dur: 2000,
paths: [
"M 0 0 Q 0 100 0 300 Q 0 550 0 600 Q 0 650 0 750 L 0 750 L 0 0 Z",
"M 50 0 Q 900 100 450 300 Q 0 550 500 600 Q 900 650 50 750 L 0 750 L 0 0 Z",
"M 700 0 Q 1450 100 950 300 Q 400 500 800 600 Q 1050 650 950 750 L 0 750 L 0 0 Z",
"M 1100 0 Q 1450 100 1250 300 Q 900 550 1150 600 Q 1350 650 1500 750 L 0 750 L 0 0 Z",
"M 1500 0 Q 1500 100 1500 300 Q 1500 550 1500 600 Q 1500 650 1500 750 L 0 750 L 0 0 Z"
],
keySplines: [
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39"
]
}
const setting03 = {
dur: 2300,
paths: [
"M 650 750 Q 1150 500 1100 0 L 1100 0 Q 1150 500 650 750 Z",
"M 650 750 Q 1150 500 1100 0 L 1150 0 Q 1200 500 750 750 Z",
"M 200 750 Q 800 500 800 0 L 1500 0 Q 1500 500 1200 750 Z",
"M 0 750 Q 200 500 200 0 L 1500 0 Q 1500 500 1500 750 Z",
"M 0 750 Q 0 500 0 0 L 1500 0 Q 1500 500 1500 750 Z"
],
keySplines: [
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39"
]
}
const setting04 = {
dur: 2500,
paths: [
"M 650 500 Q 650 500 650 500 Q 650 500 650 500 Q 650 500 650 500 C 650 500 650 500 650 500",
"M 500 550 Q 400 400 500 450 Q 700 550 550 350 Q 500 250 700 400 C 800 450 550 650 500 550",
"M 400 600 Q 100 350 300 400 Q 700 550 500 350 Q 250 100 800 350 C 1050 500 550 700 400 600",
"M 250 750 Q 0 750 200 500 Q 300 400 200 250 Q 0 0 1050 150 C 1500 200 1450 750 250 750",
"M 1500 750 Q 0 750 0 750 Q 0 400 0 0 Q 0 0 1500 0 C 1500 200 1500 750 1500 750"
],
keySplines: [
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39"
]
}
const setting05 = {
dur: 1500,
paths: [
"M 750 375 Q 525 375 450 375 Q 525 375 750 375 Q 975 375 1050 375 Q 975 375 750 375",
"M 750 375 Q 525 450 450 375 Q 525 300 750 375 Q 975 300 1050 375 Q 975 450 750 375",
"M 750 600 Q 525 450 450 375 Q 525 300 750 150 Q 975 300 1050 375 Q 975 450 750 600",
"M 750 750 Q 150 600 0 375 Q 150 150 750 0 Q 1350 150 1500 375 Q 1350 600 750 750",
"M 1500 750 Q 150 750 0 750 Q 0 0 0 0 Q 1500 0 1500 0 Q 1500 750 1500 750"
],
keySplines: [
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39",
"0.57 0.63 0.51 0.39"
]
}
class Slides {
constructor(el, setting) {
this.DOM = { el: el }
this.DOM.left = this.DOM.el.querySelector('.slide__left')
this.DOM.right = this.DOM.el.querySelector('.slide__right')
this.DOM.slides = this.DOM.el.querySelectorAll('.slide')
this.DOM.slidesCount = this.DOM.slides.length
this.DOM.slidesBg = this.DOM.el.querySelectorAll('.bg-slide')
this.DOM.slidesNavBtns = this.DOM.el.querySelectorAll('.slide__nav .js-nav')
this.DOM.gotoBtns = this.DOM.el.querySelectorAll('[data-goto]')
this.isAnimating = false
this.touchStartX = 0
this.init(setting)
this._addEventListeners()
}
init(setting) {
this.DOM.slides.forEach(slide => {
const path = slide.querySelector('clipPath path')
const animate = path.querySelector('animate')
path.setAttributeNS(null, 'd', setting.paths[0])
animate.setAttributeNS(null, 'values', arrayMirrorToString(setting.paths))
animate.setAttributeNS(null, 'keySplines', arrayMirrorToString(setting.keySplines))
animate.setAttributeNS(null, 'dur', `${setting.dur}ms`)
})
}
_addEventListeners() {
this.DOM.gotoBtns.forEach(nav => {
nav.addEventListener('click', event => {
if (this.isAnimating) return
const goto = event.target.getAttribute('data-goto')
const current = this.DOM.el.querySelector('.slide--active').getAttribute('data-slide')
this.navigate(current, goto)
})
})
this.DOM.slides.forEach(slide => {
slide.nextElementSibling.addEventListener('touchstart', this.handleTouchStart.bind(this), false)
slide.nextElementSibling.addEventListener('touchend', this.handleTouchEnd.bind(this), false)
})
}
navigate(from, to) {
this.isAnimating = true
this.DOM.slidesNavBtns.forEach(nav => nav.classList.remove('btn--active'))
this.DOM.left.setAttribute('data-goto', `${to == 0 ? this.DOM.slidesCount - 1 : +to-1}`)
this.DOM.right.setAttribute('data-goto', `${to == this.DOM.slidesCount - 1 ? 0 : +to+1}`)
const currentSlide = this.DOM.slides[from]
const nextSlide = this.DOM.slides[to]
const currentBg = this.DOM.slidesBg[from]
const nextBg = this.DOM.slidesBg[to]
const animate = nextSlide.querySelector('animate')
animate.beginElement()
setTimeout(() => {
nextSlide.style.zIndex = 1
nextSlide.classList.add('slide--active')
animate.addEventListener('endEvent', () => {
this.DOM.slidesNavBtns[to].classList.add('btn--active')
nextSlide.style.zIndex = -1
currentSlide.classList.remove('slide--active')
currentBg.classList.remove('bg-slide--active')
nextBg.classList.add('bg-slide--active')
this.isAnimating = false
})
}, 1)
}
handleTouchStart(event) {
this.touchStartX = event.touches[0].pageX
}
handleTouchEnd(event) {
const moveX = event.changedTouches[event.changedTouches.length-1].pageX - this.touchStartX
if (moveX < -10) this.DOM.right.click()
else if (moveX > 10) this.DOM.left.click()
}
}
const arrayMirrorToString = arr => {
let newArr = [...arr].reverse()
if (newArr.length % 2 != 0) newArr.shift()
return arr.concat(newArr).join(';')
}
document.addEventListener('DOMContentLoaded', () => {
const slide = new Slides(document.querySelector('#slide'), setting01)
// Not necessary. Use to show example paths animations
document.querySelectorAll('[data-setting]').forEach(setting => {
setting.addEventListener('click', event => {
const target = event.target;
const section = target.closest('.section')
const dur = section.querySelector('.js-btn-dur').value
if (dur > 30000) return alert ('Maximum value of 30 seconds for transition')
document.querySelectorAll('[data-setting]').forEach(section => section.closest('.section').classList.remove('section--active'))
section.classList.add('section--active')
let settingObjet = {}
switch (target.getAttribute('data-setting')) {
case 'setting01':
setting01.dur = dur
settingObjet = setting01
break;
case 'setting02':
setting02.dur = dur
settingObjet = setting02
break;
case 'setting03':
setting03.dur = dur
settingObjet = setting03
break;
case 'setting04':
setting04.dur = dur
settingObjet = setting04
break;
case 'setting05':
setting05.dur = dur
settingObjet = setting05
break;
default:
break;
}
slide.init(settingObjet)
})
})
document.querySelector('.js-open-modal').addEventListener('click', () => {
document.body.classList.add('modal-active')
})
document.querySelector('.js-close-modal').addEventListener('click', () => {
document.body.classList.remove('modal-active')
})
})
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.