Record the process of making the plug-in of the carousel
Design sketch
General carousel chart
Card carousel
Stacked carousel
General carousel chart
Functions:
*Can control whether to rotate automatically *Left and right arrows switch from previous to next, throttling treatment *Put the mouse on the arrow, the picture stops rotating automatically, move the mouse away and continue playing *Click the small circle to jump to the corresponding sequence of pictures *The mobile end can switch between left sliding and right sliding
General carousel - source code
Train of thought:
As shown in the figure above, suppose that there are three pictures for rotation, the green area is the display area, and the other parts are hidden
- When rendering the page, two more pictures will be selected. The numbers 1, 2 and 3 in the picture respectively represent the picture number. A, B, C and D respectively represent the status of div in the picture list at different times
- A rectangular box is the effect when the page starts, showing the first picture
- B is the next effect picture, showing the second picture
- Until the first picture is displayed, as shown in C, it is the last picture in the current picture list, so to change the state of C to the state of D, the first picture is displayed, which is equivalent to changing the left value of the picture list div quietly, and the displayed picture does not change or the first picture
- The same idea in the previous picture
Page layout
- Class = "swiper list" this div is a viewport div, the size is the same as that of the picture, and the rest is hidden
- Class = "swiper main" this div is a picture list div, which switches between the previous picture and the next picture by changing the left value
- When switching to the next picture, the left value of the picture list div subtracts the width of one picture from the original value
- When switching the previous picture, the left value of the picture list div adds the width of a picture to the original value
Source code
Previous, next core approach
nowIndex is used to mark the current picture, with an initial value of 0
// Last one prevSlider(aniTIme) { let that = this; if (this.imgArr.length===1) return; this.mainDom.style.transition = `left ${aniTIme / 1000}s` this.mainDom.style.left = `${parseInt(this.mainDom.style.left) + this.moveWidth}px`; // left value change of picture list div if (this.nowIndex === 0) { that.nowIndex = (that.imgArr.length-1); that.setActiveSpot(); setTimeout(function() { that.mainDom.style.transitionProperty = 'none'; // Set this property when changing the left value quietly, or the animation will play that.mainDom.style.left = `${-that.imgArr.length * that.moveWidth}px`; }, aniTIme) // At present, the first picture is displayed, and the last one is no longer available. Quietly change the left value of the picture list page. The picture displayed is still the first picture } else { this.nowIndex--; this.setActiveSpot(); } }, // Next piece nextSlider(aniTIme) { let that = this; if (this.imgArr.length===1) return; this.nowIndex++; this.mainDom.style.transition = `left ${aniTIme / 1000}s` this.mainDom.style.left = `${parseInt(this.mainDom.style.left) - this.moveWidth}px`; if (this.nowIndex === (this.imgArr.length)) { that.nowIndex = 0; that.setActiveSpot(); setTimeout(function() { that.mainDom.style.transitionProperty = 'none'; that.mainDom.style.left = `${-that.moveWidth}px`; }, aniTIme) // At present, the last picture is displayed, and the next one is no longer available. Quietly change the left value of div in the picture list, and the displayed picture is still the last picture } else { this.setActiveSpot(); } }, // Set origin style setActiveSpot: function() { for (let i = 0; i < this.swiperSpotDom.childElementCount; i++) { if (i === Math.abs(this.nowIndex)) { document.getElementsByClassName('spot-item')[i].style.backgroundColor = '#ff5c1f' } else { document.getElementsByClassName('spot-item')[i].style.backgroundColor = '#ccc' } } },
Next button event binding, throttling processing
eventBind() { ... // Next button event binding this.rightBtn.addEventListener('mouseover', function() { clearInterval(that.timer); }) this.rightBtn.addEventListener('mouseout', function() { that.timer = setInterval(that.nextSlider.bind(that, that.aniTIme), that.intervalTime); }) this.rightBtn.addEventListener('click', function() { that.throttle(that.nextSlider, 300, 300); }) ... } // Throttling: time stamp version throttle(handle, delay, val) { var now = Date.now(); if (now - this.prev >= delay) { handle.call(this, val); this.prev = Date.now(); } },
Throttling: event triggered at even interval
The throttling here is a little different from other throttling. You can use your own
Small dot click event binding
eventBind() { ... // Little dot event binding this.swiperSpotDom.addEventListener('mouseover', function() { clearInterval(that.timer); }) this.swiperSpotDom.addEventListener('mouseout', function() { that.timer = setInterval(that.nextSlider.bind(that, that.aniTIme), that.intervalTime); }) this.swiperSpotDom.addEventListener('click', function(e) { e = e || window.event; //This line and the next line are for IE8 and below compatibility var target = e.target || e.srcElement; if (target.tagName.toLowerCase() === "li") { var ret = this.querySelectorAll("li"); let index = Array.prototype.indexOf.call(ret, target); that.nowIndex = index; that.setActiveSpot(); that.mainDom.style.transition = `left .8s` that.mainDom.style.left = `${-(that.nowIndex+1) * that.moveWidth}px`; } }) ... }
Determine which li is clicked, assign the subscript value to nowIndex, change the left value of div (main DOM) in the picture list, and update the origin style
Left and right sliding events of mobile terminal
eventBind() { ... this.mainDom.addEventListener('touchstart', function(e) { clearInterval(that.timer); that.startX = e.changedTouches[0].clientX; that.startY = e.changedTouches[0].clientY; }) this.mainDom.addEventListener('touchmove', function(e) { clearInterval(that.timer); that.endX = e.changedTouches[0].clientX; that.endY = e.changedTouches[0].clientY; }) this.mainDom.addEventListener('touchend', function(e) { if (!that.mainDom.style.transition) { that.mainDom.style.transition = `left ${that.aniTIme / 1000}s` } let angle = that.angle({ X: that.startX, Y: that.startY }, { X: that.endX, Y: that.endY }); if (Math.abs(angle) > 30) return; if (that.endX > that.startX){ // Right slip that.prevSlider(); } else { // Left slide that.nextSlider(); } that.timer = setInterval(that.nextSlider.bind(that, that.aniTIme), that.intervalTime); }) ... }, /** * Calculate sliding angle * @param {Object} start Starting coordinates * @param {Object} end Terminal coordinates */ angle: function (start, end) { let _X = end.X - start.X; let _Y = end.Y - start.Y; //Return angle / Math.atan() returns the arctangent of a number return 360 * Math.atan(_Y / _X) / (2 * Math.PI); }
Left slide calls nextSlider(), right slide calls prevSlider()
Use:
Introduce slider.js
<div class="swiper-list"></div>
let imgArr = [ { url: '#', imgPath: '../i.jpg' }, { url: '#', imgPath: '../o.jpg' }, { url: '#', imgPath: '../q.jpeg' }, { url: '#', imgPath: '../w.jpg' }, { url: '#', imgPath: '../z.png' } ]; // let imgArr = ['i.jpg', 'o.jpg', 'q.jpeg']; // let imgArr = ['i.jpg', 'o.jpg']; // let imgArr = ['i.jpg']; new Swiper({ imgArr: imgArr, // Picture array aniTIme: 1000, // Animation execution time intervalTime: 1000, // How long does the picture stay autoplay: true // Auto play or not }).init();
css
<style> ul{padding: 0; list-style: none;} .swiper-list{ width: 640px; height: 360px; margin: 0 auto; position: relative; overflow: hidden; } .swiper-main { height: 100%; position: relative; overflow: hidden; } .swiper-item{ height: 100%; display: inline; position: absolute; } img { width: 100%; height: 100%; display: block; } .swiper-spot{ width: 100%; height: 15px; display: flex; justify-content: center; align-items: center; position: absolute; bottom: 10px; } .swiper-spot .spot-item{ width: 15px; height: 15px; border-radius: 50%; background-color: #ccc; margin-left: 10px; } .swiper-spot .spot-item:nth-of-type(1) { margin-left: 0; } .leftBtn{ position: absolute; left: 15px; top: 50%; transform: translateY(-50%); width: 30px; height: 30px; } .rightBtn{ position: absolute; right: 15px; top: 50%; transform: translateY(-50%); width: 30px; height: 30px; } </style>
Card carousel
Functions:
*Can control whether to rotate automatically *Left and right arrows switch from previous to next, throttling treatment *Put the mouse on the arrow, the picture stops rotating automatically, move the mouse away and continue playing *The mobile end can switch between left sliding and right sliding
Train of thought:
The idea is the same as that of a regular broadcast chart. However, two pictures are added to the head and the end respectively for transition. When the picture moves to the critical value, the left value of the picture list div changes, showing the same picture, but the left value of the picture list div is different
Code
The default value of nowIndex here is 3. Since two pictures are added to the header, to make the element with subscript 3 be the middle picture without zooming, the rest pictures are zoomed out
/** * obj: * imgArr Picture array * imgWidth image width * aniTime Animation switching time * intervalTime Stay time * scale Image scaling * autoplay Auto play or not * gap Space between pictures */ function Swiper(obj) { this.imgArr = obj.imgArr || []; this.scale = obj.scale || 0.8; // Picture zoom value this.gap = obj.gap; // The space between pictures when the picture is not zoomed // Mobile terminal if ((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) { this.containerWidth = document.body.clientWidth; // Box width of rotation chart }else{ // PC terminal this.containerWidth = 600; // Box width of rotation chart } this.imgWidth = obj.imgWidth; // image width this.aniTime = obj.aniTime || 500; this.intervalTime = this.aniTime + obj.intervalTime || 2000; this.nowIndex = 3; this.imgDoms = document.getElementsByClassName('swiper-slide'); this.mainDom = document.getElementsByClassName('swiper-main')[0]; this.listDoms = document.getElementsByClassName('swiper-list')[0]; this.activeDom = this.imgDoms[0]; this.autoplay = obj.autoplay; this.listDoms.style.width = `${this.containerWidth}px`; this.timer; // Auto play timer this.prev = Date.now(); this.diffLen = (this.containerWidth - this.imgWidth - (this.gap * 2)) / 2; this.clsSuffix = obj.clsSuffix; // Class name suffix }
diffLen value:
prevSlider: function(aniTime) { if (this.imgArr.length ===2) { this.nowIndex = this.nowIndex ? 0 : 1; this.setScale() } else if (this.imgArr.length ===1) { return; } else { this.nowIndex--; this.mainDom.style.transition = `left ${aniTime/1000}s` this.mainDom.style.left = `${parseInt(this.mainDom.style.left)+(this.gap + this.imgWidth)}px`; if (this.nowIndex === 1) { this.setScale() setTimeout(function() { this.nowIndex = (this.imgArr.length+1); this.setScale() this.mainDom.style.transitionProperty = 'none'; this.mainDom.style.left = `${-(parseInt(this.imgDoms[this.nowIndex].style.left) - this.diffLen - this.gap)}px`; }.bind(this), aniTime) } else { this.setScale() } } }, nextSlider: function(aniTime) { if (this.imgArr.length ===2) { this.nowIndex = this.nowIndex ? 0 : 1; this.setScale() } else if (this.imgArr.length ===1) { return; } else { if (this.nowIndex >=2) { this.mainDom.style.transition = `left ${aniTime/1000}s` this.mainDom.style.left = `${parseInt(this.mainDom.style.left)-(this.gap + this.imgWidth)}px`; // this.mainDom.style.left = `${this.gap + this.imgWidth}px`; } if (this.nowIndex === (this.imgArr.length+1)) { this.nowIndex = (this.imgArr.length+2); this.setScale() setTimeout(function() { this.nowIndex = 2; this.setScale() this.mainDom.style.transitionProperty = 'none'; this.mainDom.style.left = `${-(this.imgWidth - this.diffLen)}px`; }.bind(this), aniTime) } else { this.nowIndex++; this.setScale() } } }, setScale: function() { // Stacked if (this.gap < 0) { for (let i = 0; i < this.imgDoms.length; i++) { if (this.imgArr.length ===2) { this.imgDoms[0].style.left = `${(this.containerWidth/4) - (this.imgWidth/2)}px`; this.imgDoms[1].style.left = `${(this.containerWidth/4)*3 - (this.imgWidth/2)}px`; } else if (this.imgArr.length ===1) { this.imgDoms[i].style.left = `${(this.containerWidth/2) - (this.imgWidth/2)}px`; } else { this.imgDoms[i].style.left = `${(i - 1) * (this.imgWidth + this.gap)}px`; } if (i === this.nowIndex) { this.imgDoms[i].style.transform = 'scale(1)'; this.imgDoms[i].style.zIndex = '1001'; } else if (i < this.nowIndex) { this.imgDoms[i].style.transform = `scale(${1 - ((this.nowIndex - i) * 0.2)})`; this.imgDoms[i].style.zIndex = 1000 - ((this.nowIndex - i)); } else if (i > this.nowIndex) { this.imgDoms[i].style.transform = `scale(${1 - ((i - this.nowIndex) * 0.2)})`; this.imgDoms[i].style.zIndex = 1000 - (i - this.nowIndex); } } } else { // Card type for (let i = 0; i < this.imgDoms.length; i++) { if (this.imgArr.length ===2) { this.imgDoms[0].style.left = `${(this.containerWidth/4) - (this.imgWidth/2)}px`; this.imgDoms[1].style.left = `${(this.containerWidth/4)*3 - (this.imgWidth/2)}px`; } else if (this.imgArr.length ===1) { this.imgDoms[i].style.left = `${(this.containerWidth/2) - (this.imgWidth/2)}px`; } else { this.imgDoms[i].style.left = `${(i - 1) * (this.imgWidth + this.gap)}px`; } if (i === this.nowIndex) { this.imgDoms[i].style.transform = 'scale(1)'; } else { this.imgDoms[i].style.transform = `scale(${this.scale})`; } } } },
usage
<div class="swiper-list-card swiper-list"> <div class="swiper-main-card swiper-main"></div> <img id="prev-card" class="btn leftBtn" src="../left.png" alt=""> <img id="next-card" class="btn rightBtn" src="../right.png" alt=""> </div>
// Introduce slider_card.js <script src="./slider_card.js"></script>
let imgArr = [{ url: '#', imgPath: '../i.jpg' }, { url: '#', imgPath: '../o.jpg' }, { url: '#', imgPath: '../q.jpeg' }, { url: '#', imgPath: '../w.jpg' }, { url: '#', imgPath: '../z.png' } ]; // let imgArr = ['i.jpg', 'o.jpg', 'q.jpeg']; // let imgArr = ['i.jpg', 'o.jpg']; // let imgArr = ['i.jpg']; new Swiper({ imgArr: imgArr, // Picture array imgWidth: 200, // image width aniTime: 1000, // Animation switching time intervalTime: 1500, // Stay time scale: 0.8, // Image scaling autoplay: true, // Auto play or not gap: 0, // Space between pictures clsSuffix: '-card' // Element class name suffix }).init();
css
<style> .swiper-list{ height: 200px; position: relative; overflow: hidden; border: 1px solid #eee; padding: 30px 0; } .swiper-main{ height: 100%; position: relative; } .swiper-main img{ height: 100%; display: block; position: absolute; top: 0px; border-radius: 4px; display: inline-block; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); } .btn{ position: absolute; top: 50%; transform: translateY(-50%); width: 30px; height: 30px; z-index: 1002; } .leftBtn{ left: 0px; } .rightBtn{ right: 0px; } </style>
Stackable carousel
The principle of stackable carousel is the same as that of card carousel, except that the gap between pictures is set to a negative value. The usage is the same as that of card carousel
<div class="swiper-list-stack swiper-list"> <div class="swiper-main-stack swiper-main"></div> <img id="prev-stack" class="btn leftBtn" src="../left.png" alt=""> <img id="next-stack" class="btn rightBtn" src="../right.png" alt=""> </div>
// Introduce slider_card.js <script src="./slider_card.js"></script>
new Swiper({ imgArr: imgArr, imgWidth: 320, aniTime: 1000, intervalTime: 1500, scale: 0.8, autoplay: false, gap: -200, clsSuffix: '-stack' }).init();
The reason for adding clsSuffix is that the page acquisition element is obtained by class name, and a suffix is added to distinguish the card type and stack type