From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
Today’s Blueprint is a simple content slider with depth-like zoom functionality. Each slide has a predefined zoom area that will be used to calculate the appropriate scale value for a fullscreen fill. Once the icon for zooming is clicked, the zoom area as well as the page get scaled, creating the illusion that the viewer is approaching the item. Once the whole page is covered, we show some more details.
Navigating the slider will animate the inner parts of the slide, allowing for an independent control of the image area and the title. We are using CSS transitions and dymanic.js for moving the slide elements. Dymanic.js by Michaël Villar is a JavaScript library to create physics-based animations.
Please note that we are using a couple of modern CSS properties, so only contemporary browsers are supported.
Browser Support:- ChromeSupported
- FirefoxSupported
- Internet ExplorerSupported from version 10+
- SafariSupported
- OperaSupported
The HTML
<!-- Main container --> <div class="container"> <!-- Blueprint header --> <header class="bp-header cf"> <!-- Page title etc. --> </header> <!-- Grid --> <section class="slider"> <div class="slide slide--current" data-content="content-1"> <div class="slide__mover"> <div class="zoomer flex-center"> <img class="zoomer__image" src="images/iphone.png" alt="iPhone" /> <div class="preview"> <img src="images/iphone-content-preview.png" alt="iPhone app preview" /> <div class="zoomer__area zoomer__area--size-2"></div> </div> </div> </div> <h2 class="slide__title"><span>The Classy</span> iPhone 6</h2> </div> <div class="slide" data-content="content-2"> <!-- ... --> </div> <!-- ... --> <nav class="slider__nav"> <button class="button button--nav-prev"> <i class="icon icon--arrow-left"></i> <span class="text-hidden">Previous product</span> </button> <button class="button button--zoom"> <i class="icon icon--zoom"></i> <span class="text-hidden">View details</span> </button> <button class="button button--nav-next"> <i class="icon icon--arrow-right"></i> <span class="text-hidden">Next product</span> </button> </nav> </section> <!-- /slider--> <!-- content --> <section class="content"> <div class="content__item" id="content-1"> <img class="content__item-img rounded-right" src="images/iphone-content.png" alt="Apple Watch Content" /> <div class="content__item-inner"> <h2>The iPhone 6</h2> <h3>Incredible performance for powerful apps</h3> <p>...</p> </div> </div> <div class="content__item" id="content-2"> <!-- ... --> </div> <!-- ... --> <button class="button button--close"> <i class="icon icon--circle-cross"></i> <span class="text-hidden">Close content</span> </button> </section> <!-- /content --> </div> <script src="js/classie.js"></script> <script src="js/dynamics.min.js"></script> <script src="js/main.js"></script>
The CSS
/* Helper classes */ html, body { overflow: hidden; height: 100%; } .container { position: relative; overflow: hidden; overflow-y: scroll; width: 100%; height: 100%; -webkit-overflow-scrolling: touch; } .noscroll .container { overflow-y: hidden; } .slider { position: relative; z-index: 200; width: 100%; margin: 0 auto; padding: 0 0 7em; text-align: center; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-touch-callout: none; -khtml-user-select: none; } .slide { position: absolute; top: 0; visibility: hidden; width: 100%; opacity: 0; } .slide--current { position: relative; z-index: 100; visibility: visible; opacity: 1; } .slide__mover { position: relative; z-index: 100; } .slide__title { font-size: 1.75em; font-weight: normal; margin: 0 auto; padding: 1em 0 0 0; } .slide__title span { font-size: 55%; font-weight: bold; display: block; letter-spacing: 2px; text-transform: uppercase; color: #35303d; } .slider__nav { position: absolute; bottom: 2em; width: 100%; text-align: center; } .button { font-size: 1.31em; position: relative; display: inline-block; overflow: hidden; margin: 0 25px; padding: 0; cursor: pointer; color: #5c5edc; border: none; background: none; } .button:focus { outline: none; } .button:hover { color: #fff; } .text-hidden { position: absolute; top: 200%; } .button--close { font-size: 1.55em; position: absolute; top: 30px; right: 30px; margin: 0; opacity: 0; color: #50505a; -webkit-transition: opacity 0.3s; transition: opacity 0.3s; } .content--open .button--close { opacity: 1; } /* Zoomer */ .zoomer { position: relative; height: 360px; /* this is needed for IE10 so that vertical flexbox centering works */ } .flex-center { display: -webkit-flex; display: -ms-flexbox; display: flex; -webkit-align-items: center; -ms-flex-align: center; align-items: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; } .zoomer__image { display: block; margin: 0; -webkit-flex: none; -ms-flex: none; flex: none; } .zoomer__area, .preview { position: absolute; top: 50%; left: 50%; -webkit-transform: translate3d(-50%,-50%,0); transform: translate3d(-50%,-50%,0); } .zoomer__area:focus { outline: none; } .zoomer__area--size-1 { /* Apple Watch */ width: 96px; height: 118px; } .zoomer__area--size-2 { /* iPhone */ width: 112px; height: 198px; } .zoomer__area--size-3 { /* MacBook */ width: 315px; height: 200px; } .zoomer__area--size-4 { /* iPad */ width: 150px; height: 200px; } .zoomer__area--size-5 { /* iMac */ width: 315px; height: 189px; } .preview { overflow: hidden; background: #18191b; } .preview img { display: block; border-radius: inherit; -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } .zoomer--active .preview img { -webkit-transform: translate3d(100%,0,0); transform: translate3d(100%,0,0); } .rounded { border-radius: 15px; } .rounded-right { border-radius: 0 15px 15px 0; } .preview__content { position: absolute; top: 0; left: 100%; width: 100%; height: 100%; border-radius: inherit; } /* Content */ .content { position: fixed; z-index: 1000; top: 0; left: -100%; overflow: hidden; overflow-y: scroll; width: 100%; height: 100vh; background: #18191b; -webkit-overflow-scrolling: touch; } .content--open { left: 0; } .content__item { position: absolute; top: 0; display: -webkit-flex; display: -ms-flexbox; display: flex; overflow: hidden; height: 0; min-height: 100%; margin: 0 auto; padding: 2em 0; pointer-events: none; opacity: 0; color: #fff; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } .content__item--current { pointer-events: auto; opacity: 1; } .content__item--reset { height: auto; } .content h2 { font-size: 3.5em; font-weight: normal; margin: 0; } .content h3 { font-size: 1.95em; font-weight: normal; margin: 0.25em 0 0.5em; color: #685884; } .content p { font-size: 1.25em; line-height: 1.5; } .content__item-img { display: block; max-width: 40vw; max-height: 80vh; -webkit-transform: translate3d(-120%,0,0); transform: translate3d(-120%,0,0); -webkit-flex: none; -ms-flex: none; flex: none; } .content__item--current .content__item-img { -webkit-transform: translate3d(-10px,0,0); transform: translate3d(-10px,0,0); } .content__item-inner { padding: 0 10vw 0; opacity: 0; -webkit-transform: translate3d(0,50px,0); transform: translate3d(0,50px,0); } .content__item--current .content__item-inner { opacity: 1; -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } /**************************/ /* All synced transitions */ /**************************/ .zoomer { -webkit-transition: -webkit-transform 0.5s; transition: transform 0.5s; -webkit-transition-timing-function: cubic-bezier(0.7,0,0.3,1); transition-timing-function: cubic-bezier(0.7,0,0.3,1); } .zoomer.zoomer--notrans { -webkit-transition: none; transition: none; } .zoomer__image { -webkit-transition: opacity 0.3s 0.3s; transition: opacity 0.3s 0.3s; } .zoomer--active .zoomer__image { opacity: 0; -webkit-transition-delay: 0s; transition-delay: 0s; } .preview img { -webkit-transition: -webkit-transform 0.6s 0.3s; transition: transform 0.6s 0.3s; -webkit-transition-timing-function: cubic-bezier(0.2,1,0.3,1); transition-timing-function: cubic-bezier(0.2,1,0.3,1); } .zoomer--active .preview img { -webkit-transition: -webkit-transform 0.3s; transition: transform 0.3s; } .content { -webkit-transition: left 0s; transition: left 0s; } .content__item { -webkit-transition: opacity 0s; transition: opacity 0s; } .content, .content__item { /* delay for content to disappear and zoomer to start transitioning back to 0 */ -webkit-transition-delay: 0.3s; transition-delay: 0.3s; } .content--open, .content__item--current { -webkit-transition: none; transition: none; } .content__item-img { -webkit-transition: -webkit-transform 0.4s; transition: transform 0.4s; -webkit-transition-timing-function: cubic-bezier(0.7,1,0.8,1); transition-timing-function: cubic-bezier(0.7,1,0.8,1); } .content__item--current .content__item-img { -webkit-transition-timing-function: cubic-bezier(0.2,1,0.3,1); transition-timing-function: cubic-bezier(0.2,1,0.3,1); -webkit-transition-duration: 1s; transition-duration: 1s; } .content__item-inner { -webkit-transition: -webkit-transform 0.6s, opacity 0.3s; transition: transform 0.6s, opacity 0.3s; -webkit-transition-timing-function: cubic-bezier(0.7,1,0.8,1), ease; transition-timing-function: cubic-bezier(0.7,1,0.8,1), ease; } .content__item--current .content__item-inner { -webkit-transition-timing-function: cubic-bezier(0.2,1,0.3,1), ease; transition-timing-function: cubic-bezier(0.2,1,0.3,1), ease; -webkit-transition-duration: 1.7s; transition-duration: 1.7s; } /* Media Queries */ @media screen and (max-width: 50em) { .content__item { display: block; } .content__item-img { max-width: calc(100% - 80px); max-height: 70vh; } .content h2 { font-size: 3em; } .content__item-inner { font-size: 82%; padding: 4em 3em 2em; } }
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
The JavaScript
/** * main.js * http://www.codrops.com * * Licensed under the MIT license. * http://www.opensource.org/licenses/mit-license.php * * Copyright 2015, Codrops * http://www.codrops.com */ ;(function(window) { 'use strict'; var bodyEl = document.body, docElem = window.document.documentElement, support = { transitions: Modernizr.csstransitions }, // transition end event name transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'msTransition': 'MSTransitionEnd', 'transition': 'transitionend' }, transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ], onEndTransition = function( el, callback ) { var onEndCallbackFn = function( ev ) { if( support.transitions ) { if( ev.target != this ) return; this.removeEventListener( transEndEventName, onEndCallbackFn ); } if( callback && typeof callback === 'function' ) { callback.call(this); } }; if( support.transitions ) { el.addEventListener( transEndEventName, onEndCallbackFn ); } else { onEndCallbackFn(); } }, // window sizes win = {width: window.innerWidth, height: window.innerHeight}, // some helper vars to disallow scrolling lockScroll = false, xscroll, yscroll, scrollContainer = document.querySelector('.container'), // the main slider and its items sliderEl = document.querySelector('.slider'), items = [].slice.call(sliderEl.querySelectorAll('.slide')), // total number of items itemsTotal = items.length, // navigation controls/arrows navRightCtrl = sliderEl.querySelector('.button--nav-next'), navLeftCtrl = sliderEl.querySelector('.button--nav-prev'), zoomCtrl = sliderEl.querySelector('.button--zoom'), // the main content element contentEl = document.querySelector('.content'), // close content control closeContentCtrl = contentEl.querySelector('button.button--close'), // index of current item current = 0, // check if an item is "open" isOpen = false, isFirefox = typeof InstallTrigger !== 'undefined', // scale body when zooming into the items, if not Firefox (the performance in Firefox is not very good) bodyScale = isFirefox ? false : 3; // some helper functions: function scrollX() { return window.pageXOffset || docElem.scrollLeft; } function scrollY() { return window.pageYOffset || docElem.scrollTop; } // from http://www.sberry.me/articles/javascript-event-throttling-debouncing function throttle(fn, delay) { var allowSample = true; return function(e) { if (allowSample) { allowSample = false; setTimeout(function() { allowSample = true; }, delay); fn(e); } }; } function init() { initEvents(); } // event binding function initEvents() { // open items zoomCtrl.addEventListener('click', function() { openItem(items[current]); }); // close content closeContentCtrl.addEventListener('click', closeContent); // navigation navRightCtrl.addEventListener('click', function() { navigate('right'); }); navLeftCtrl.addEventListener('click', function() { navigate('left'); }); // window resize window.addEventListener('resize', throttle(function(ev) { // reset window sizes win = {width: window.innerWidth, height: window.innerHeight}; // reset transforms for the items (slider items) items.forEach(function(item, pos) { if( pos === current ) return; var el = item.querySelector('.slide__mover'); dynamics.css(el, { translateX: el.offsetWidth }); }); }, 10)); // keyboard navigation events document.addEventListener( 'keydown', function( ev ) { if( isOpen ) return; var keyCode = ev.keyCode || ev.which; switch (keyCode) { case 37: navigate('left'); break; case 39: navigate('right'); break; } } ); } // opens one item function openItem(item) { if( isOpen ) return; isOpen = true; // the element that will be transformed var zoomer = item.querySelector('.zoomer'); // slide screen preview classie.add(zoomer, 'zoomer--active'); // disallow scroll scrollContainer.addEventListener('scroll', noscroll); // apply transforms applyTransforms(zoomer); // also scale the body so it looks the camera moves to the item. if( bodyScale ) { dynamics.animate(bodyEl, { scale: bodyScale }, { type: dynamics.easeInOut, duration: 500 }); } // after the transition is finished: onEndTransition(zoomer, function() { // reset body transform if( bodyScale ) { dynamics.stop(bodyEl); dynamics.css(bodyEl, { scale: 1 }); // fix for safari (allowing fixed children to keep position) bodyEl.style.WebkitTransform = 'none'; bodyEl.style.transform = 'none'; } // no scrolling classie.add(bodyEl, 'noscroll'); classie.add(contentEl, 'content--open'); var contentItem = document.getElementById(item.getAttribute('data-content')) classie.add(contentItem, 'content__item--current'); classie.add(contentItem, 'content__item--reset'); // reset zoomer transform - back to its original position/transform without a transition classie.add(zoomer, 'zoomer--notrans'); zoomer.style.WebkitTransform = 'translate3d(0,0,0) scale3d(1,1,1)'; zoomer.style.transform = 'translate3d(0,0,0) scale3d(1,1,1)'; }); } // closes the item/content function closeContent() { var contentItem = contentEl.querySelector('.content__item--current'), zoomer = items[current].querySelector('.zoomer'); classie.remove(contentEl, 'content--open'); classie.remove(contentItem, 'content__item--current'); classie.remove(bodyEl, 'noscroll'); if( bodyScale ) { // reset fix for safari (allowing fixed children to keep position) bodyEl.style.WebkitTransform = ''; bodyEl.style.transform = ''; } /* fix for safari flickering */ var nobodyscale = true; applyTransforms(zoomer, nobodyscale); /* fix for safari flickering */ // wait for the inner content to finish the transition onEndTransition(contentItem, function(ev) { classie.remove(this, 'content__item--reset'); // reset scrolling permission lockScroll = false; scrollContainer.removeEventListener('scroll', noscroll); /* fix for safari flickering */ zoomer.style.WebkitTransform = 'translate3d(0,0,0) scale3d(1,1,1)'; zoomer.style.transform = 'translate3d(0,0,0) scale3d(1,1,1)'; /* fix for safari flickering */ // scale up - behind the scenes - the item again (without transition) applyTransforms(zoomer); // animate/scale down the item setTimeout(function() { classie.remove(zoomer, 'zoomer--notrans'); classie.remove(zoomer, 'zoomer--active'); zoomer.style.WebkitTransform = 'translate3d(0,0,0) scale3d(1,1,1)'; zoomer.style.transform = 'translate3d(0,0,0) scale3d(1,1,1)'; }, 25); if( bodyScale ) { dynamics.css(bodyEl, { scale: bodyScale }); dynamics.animate(bodyEl, { scale: 1 }, { type: dynamics.easeInOut, duration: 500 }); } isOpen = false; }); } // applies the necessary transform value to scale the item up function applyTransforms(el, nobodyscale) { // zoomer area and scale value var zoomerArea = el.querySelector('.zoomer__area'), zoomerAreaSize = {width: zoomerArea.offsetWidth, height: zoomerArea.offsetHeight}, zoomerOffset = zoomerArea.getBoundingClientRect(), scaleVal = zoomerAreaSize.width/zoomerAreaSize.height < win.width/win.height ? win.width/zoomerAreaSize.width : win.height/zoomerAreaSize.height; if( bodyScale && !nobodyscale ) { scaleVal /= bodyScale; } // apply transform el.style.WebkitTransform = 'translate3d(' + Number(win.width/2 - (zoomerOffset.left+zoomerAreaSize.width/2)) + 'px,' + Number(win.height/2 - (zoomerOffset.top+zoomerAreaSize.height/2)) + 'px,0) scale3d(' + scaleVal + ',' + scaleVal + ',1)'; el.style.transform = 'translate3d(' + Number(win.width/2 - (zoomerOffset.left+zoomerAreaSize.width/2)) + 'px,' + Number(win.height/2 - (zoomerOffset.top+zoomerAreaSize.height/2)) + 'px,0) scale3d(' + scaleVal + ',' + scaleVal + ',1)'; } // navigate the slider function navigate(dir) { var itemCurrent = items[current], currentEl = itemCurrent.querySelector('.slide__mover'), currentTitleEl = itemCurrent.querySelector('.slide__title'); // update new current value if( dir === 'right' ) { current = current < itemsTotal-1 ? current + 1 : 0; } else { current = current > 0 ? current - 1 : itemsTotal-1; } var itemNext = items[current], nextEl = itemNext.querySelector('.slide__mover'), nextTitleEl = itemNext.querySelector('.slide__title'); // animate the current element out dynamics.animate(currentEl, { opacity: 0, translateX: dir === 'right' ? -1*currentEl.offsetWidth/2 : currentEl.offsetWidth/2, rotateZ: dir === 'right' ? -10 : 10 }, { type: dynamics.spring, duration: 2000, friction: 600, complete: function() { dynamics.css(itemCurrent, { opacity: 0, visibility: 'hidden' }); } }); // animate the current title out dynamics.animate(currentTitleEl, { translateX: dir === 'right' ? -250 : 250, opacity: 0 }, { type: dynamics.bezier, points: [{"x":0,"y":0,"cp":[{"x":0.2,"y":1}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}], duration: 450 }); // set the right properties for the next element to come in dynamics.css(itemNext, { opacity: 1, visibility: 'visible' }); dynamics.css(nextEl, { opacity: 0, translateX: dir === 'right' ? nextEl.offsetWidth/2 : -1*nextEl.offsetWidth/2, rotateZ: dir === 'right' ? 10 : -10 }); // animate the next element in dynamics.animate(nextEl, { opacity: 1, translateX: 0 }, { type: dynamics.spring, duration: 2000, friction: 600, complete: function() { items.forEach(function(item) { classie.remove(item, 'slide--current'); }); classie.add(itemNext, 'slide--current'); } }); // set the right properties for the next title to come in dynamics.css(nextTitleEl, { translateX: dir === 'right' ? 250 : -250, opacity: 0 }); // animate the next title in dynamics.animate(nextTitleEl, { translateX: 0, opacity: 1 }, { type: dynamics.bezier, points: [{"x":0,"y":0,"cp":[{"x":0.2,"y":1}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}], duration: 650 }); } // disallow scrolling (on the scrollContainer) function noscroll() { if(!lockScroll) { lockScroll = true; xscroll = scrollContainer.scrollLeft; yscroll = scrollContainer.scrollTop; } scrollContainer.scrollTop = yscroll; scrollContainer.scrollLeft = xscroll; } init(); })(window);
Super rad.
Awesome Awesome Awesome !! oh god I love itttt
WOW! Love this! Awesome work once again!
That looks brilliant, and it works so smoothly! I’m thinking about using this myself. <3
Mary! It’s amazing. Thanks.
Nice work as always!
Just incredible Mary. Amazing talent right there…!
Thank you for sharing…
This is pretty sweet, oh man. Pondering if I should revamp my portfolio with this.
Supperr Cool
It helped me for my website which is under construction
Awesome as always!!!
Super amazing! How to be super amazing like you?!
Hey Mary Lou, looking silky smooth as ever. But I don’t get why are you sliding the preview to the right (when zooming in) when it slides back in from the left, that contradicts users’ expectations.
Anyways, I know these are just experiments 😉
Marry me Mary Lou!
Thanks so much 😉
Very cool. The code us an open for use? I can use it with my website?
this is really cool
What sells this is the beautiful simplicity! Love it! Nice work.
THIS IS AWESOME. Thank you. I’m in need of some help though.
If someone would have some time to spare please have a look at my site here: http://pastebin.com/gEn2wYpM
Thank you.
Awesome plugin. I was curious if it were possible to run multiple Zoom Sliders on a single page. I tried playing around with it, but only the first instance seems to be working, wereas the second instance, located further down the page; doesn’t do anything. I was wondering if I need to assigned some sort of unique identifier to each in order for the browser to recognize two separate sliders.
Any help would be much appreciated.
Thanks!
MH
Thank you for leaving comments in the JS section! This is invaluable!
You inspire…
Mary me too
You’re a true inspiration and never cease to amaze with your wonderful work.
Amazing design! Speechless! If the close button on the pop up page was made more visible that would help a lot. I had a hard time figuring out how to go back 😛
Great design. Apple should use this one!
Help me auto run slider!
Hi. Did you figure out how to do it?
how can i remove zoom transition ?
Awesome Slider, well done. Does his build include “auto-sliding” through the products, or must the user click the -> ?
Try adding this to the function initEvents() :
setInterval(function() { navigate('right'); }, 3000);
Is there a method to allow touch screen devices?
Have You tried to add pagination based navigation? so 1,2,3 for each slide?
Hello there , I realy liked your slider but , this horizonal timeline in mackbook pro look absolutely amazing is there any chance for tutorial how to make something similar?
I was trying to apply this slider into a particular section of the website i am working. How should i zoom only the section ex. slider feature section. Not the whole page?
Hi, I was trying to use this on a parallax website I am currently working on as one of the main section of the website. However as I zoom in, everything is zooming in. I don’t want to do that. I want to only zoom in that particular section (gallery section). How can I do that? What part on javascript should I edit to overwrite zooming of window but instead zoomin only for example?
Hi Mary,
how to stop sliding(i.e how to clearInterval), when zooming the content.
Thank you for this awesome slider. How to put two sliders for one page?
Thank you for share.
i have a problem ican’t see the nav buttons when put on web the index.html, bunt on my Pc its work perfect.
can you help me please
Hi fiddled and was able to make it work on all three browsers. I have to see how it displays on that beautiful piece of furniture (I wasted money) called iMac.
Manoela: Will you be my Daughter-in-Law?
Is it possible to change the background colour in each slide?
How to make thumbnails?
For some reason, when zoomed, the transition is breaking all of the position:fixed elements on my page and making them “act” position:absolute. Can’t figure this one out. Did anyone else have this problem?
This problem is caused by a transition on the body that’s caused by the slider when you close it,
what I have done to solve it is to remove that transition from the body after the function for closing the slider ends.
so I added this code in the main.js file at the end of closeContent function just before the closing tag
setTimeout(function () {
document.querySelector(‘body’).style.transform = null;
}, 1000)
I know it is not a perfect solution but it worked for me and now the fixed header appears again
check it at http://dawoud.ml
Another way is to set the bodyScale variable in main.js to false on line 63
which cancels the animation on the body
I really like this script! Is there anyway that i can make this script swipable?
Is it possible to use more than one of the Zoom sliders on the same webpage?
I tried integrating this into the page stack navigation layout but the Zoom slider for the second page won’t work. Does that mean theoretically, I have to work it on a separate script file with different ids from the first zoom slider?
Hi ! If you need to use this wonderfull blueprint with navigation history support,
I’ve made some adjustments in javascript code that allow it.
You’ll find this version here : https://github.com/liitfr/ZoomSlider
Hope it’ll be helpfull for some of you, please feel free to suggest some improvements.
Mathias
Good day. I liked the effect very much. Suggest how you can change the code, so that every slide has its own button, which it zumit? Thank you