Pure js - General carousel, card carousel, stacked carousel

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 - Demo

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

Card carousel - Demo

Card carousel - source code

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

Demonstration
Source code

<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

Tags: Javascript Mobile REST iOS Android

Posted on Tue, 12 Nov 2019 06:17:57 -0500 by justinjkiss