SOURCE

console 命令行工具 X clear

                    
>
console
let colors = ['red', 'blue', 'green', 'yellow', 'orange', 'pink', 'purple', 'skyblue']
colors.push(...colors)
Vue.component('card-slider', {
    template: "#card-slider",
    props: ['data', 'keys', 'sizes'],
    data() {
        return {
            list: [1, 2, 3, 4, 5, 6, 7, 8,9,10,11,12,13,14,15,16],
            active: 0,
            o_sizes: {
                width: 200,
                height: 200
            },
            o_keys: {
                cover: '',
                array: ''
            },
            animating: false
        }
    },
    computed: {
        buttonBoxStyle() {
            return {
                width: this.o_sizes.width + 'px',
                height: this.o_sizes.height + 'px',
                zIndex: this.list.length + 1
            }
        }
    },
    methods: {
        getOffset(index) {
            if (index < this.active) {
                return this.list.length + index - this.active;
            } else {
                return index - this.active;
            }
        },
        getOpacity(index) {
            let offset = this.getOffset(index);
            let opacityOffset = 0.15;
            if (offset > 4) {
                return 0;
            }
            return 1 - opacityOffset * offset;
        },
        getZIndex(index) {
            return this.list.length - this.getOffset(index) - 1;
        },
        getPosition(index) {
            let offset = 10;
            let _offset = this.getOffset(index);
            return _offset * offset + 'px';
        },
        getTransform(index) {
            let offset = this.getOffset(index);
            let translateOffset = 8;
            let scaleOffset = 0.05;
            return `translateX(${offset * translateOffset}%) scale(${1 - offset * scaleOffset})`;
        },
        getCardStyle(index) {
            let opacity = this.getOpacity(index);
            let zIndex = this.getZIndex(index);
            let left = this.getPosition(index);
            return {
                opacity,
                zIndex,
                background: colors[index],
                width: this.o_sizes.width + 'px',
                height: this.o_sizes.height + 'px',
                transform: this.getTransform(index)
            }
        },
        getData(src, keys) {
            if (typeof keys === 'string' && src != undefined) {
                let e = src;
                keys = keys.split(/[\.\[\]]/).filter(key => {
                    return key.trim() != '';
                }).map(key => {
                    return key.replace(/^['"]|["']$/g, '');
                });
                if (!keys.length) {
                    return;
                }
                for (let i = 0; i < keys.length; ++i) {
                    let key = keys[i];
                    let prop = e[key];
                    e = prop;
                    if (e === null || e === undefined) {
                        return;
                    }
                }
                return e;
            }
        },
        selectiveCopy(target, source) {
            if (!source) {
                return;
            }
            for (let key in target) {
                if (typeof source[key] == typeof target[key]) {
                    target[key] = source[key];
                }
            }
        },
        next() {
            if (!this.animating) {
                this.slideOut(this.active++, () => {
                    this.animating = false;
                });
                this.animating = true;
            }
        },
        prev() {
            if (!this.animating) {
                this.slideIn(this.getPrevPos(this.active--), () => {
                    this.animating = false;
                });
                this.animating = true;
            }
        },
        seek(index) {
            let end = index - this.active;
            let slides = this.$refs.cards;
            let slideBox = this.$refs['card-box'];
            if (end > 0) {
                let container = slideBox.cloneNode(true);
                container.replaceChildren();
                container.style.zIndex = this.list.length - 1;
                slideBox.after(container);
                for (let i = this.active; i < this.active + end; ++i) {
                    let node = slides[i].cloneNode(true);
                    container.appendChild(node);
                }
                container.animate([
                    {
                        left: '-100%',
                        opacity: 1,
                        opacity: 1
                    }
                ], { duration: 200 }).onfinish = () => {
                    container.remove();
                }
            } else if (end < 0) {
                let count = 0;
                for (let i = this.active - 1; i >= this.active + end; --i) {
                    slides[i].animate([
                        {
                            left: '-200%',
                            zIndex: this.list.length + count,
                            opacity: 1
                        },
                        {
                            left: 0,
                            zIndex: this.list.length + count,
                            opacity: 1
                        }
                    ],{duration: 300})
                    count++;
                }
            } else {
                this.animating = false;
            }
            this.active = index;
        },
        slideIn(index, callback) {
            this.$refs.cards[index].animate([
                {
                    transform: 'translateX(-100%) scale(1)',
                    zIndex: this.list.length,
                    opacity: 1
                },
                {
                    transform: 'translateX(0%) scale(1)',
                    zIndex: this.list.length,
                    opacity: 1
                }
            ], { duration: 150 }).onfinish = () => {
                if (typeof callback === 'function') {
                    callback();
                }
            }
        },
        slideOut(index, callback) {
            let slideBox = this.$refs['card-box'];
            let slides = this.$refs.cards;
            let elt = slides[index].cloneNode(true);
            elt.animate([
                {
                    transform: 'translateX(-100%) scale(1)',
                    zIndex: this.list.length,
                    opacity: 1
                }
            ], { duration: 150 }).onfinish = () => {
                if (typeof callback == 'function') {
                    callback();
                }
                elt.remove();
            }
            slideBox.appendChild(elt);
        },
        getNextPos(current) {
            current++;
            let max = this.list.length - 1;
            if (current > max) {
                current = 0;
            }
            return current;
        },
        getPrevPos(current) {
            current--;
            let max = this.list.length - 1;
            if (current < 0) {
                current = max;
            }
            return current;
        }
    },
    watch: {
        sizes: {
            immediate: true,
            handler() {
                this.selectiveCopy(this.o_sizes, this.sizes);
            }
        },
        keys: {
            immediate: true,
            handler() {
                this.selectiveCopy(this.o_keys, this.keys);
            }
        },
        active(newValue) {
            let max = this.list.length - 1;
            let min = 0;
            if (newValue > max) {
                this.active = min;
            } else if (newValue < min) {
                this.active = max;
            }
        }
    }
});
let vm = new Vue({
    el: "#root",
    data: {

    }
})
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=, initial-scale=">
	<meta http-equiv="X-UA-Compatible" content="">
	<title></title>
</head>

<body>
	<div id="root">
		<card-slider></card-slider>
	</div>
	<template id="card-slider">
		<div class="card-slider">
			<div class="card-box" ref="card-box">
                <div class="card" ref="cards" :style="getCardStyle(index)" v-for="(item,index) in list" :key="index">{{item}}</div>
            </div>
            <div class="button-box" :style="buttonBoxStyle">
                <div class="arrow-left" @click="prev">&lt;</div>
                <div class="arrow-right" @click="next">&gt;</div>
                <div class="pagination">
                    <div @click="seek(index)" :class="[active == index ? 'active' : '']" v-for="(item,index) in list" :key="index"></div>
                </div>
            </div>
		</div>
	</template>
</body>

</html>
* {
    margin: 0;
    padding: 0;
}
#root {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background: #eee;
}
.card-slider {
    margin-left: 100px;
    position: relative;
    width: calc(200px + 10px * 3);;
    height: 200px;
    overflow: hidden;
}
.card-slider .card-box {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
}
.card-slider .button-box {
    position: absolute;
    left: 0;
    top: 0;
    pointer-events: none;
}
.card-slider .button-box .arrow-left,
.card-slider .button-box .arrow-right {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    font-size: 22px;
    color: #eee;
    font-family: "幼圆";
    font-weight: 600;
    cursor: pointer;
    pointer-events: all;
    user-select: none;
}
.card-slider .button-box .arrow-left {
    left: 5px;
}
.card-slider .button-box .arrow-right {
    right: 5px;
}
.card-slider .button-box .pagination {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    bottom: 10px;
    pointer-events: all;
    width: max-content;
}
.card-slider .button-box .pagination > div {
    display: inline-block;
    width: 6px;
    height: 6px;
    background: #fff;
    border-radius: 3px;
    margin: 0 3px;
    cursor: pointer;
    transition: width 0.2s;
}
.card-slider .button-box .pagination > div.active {
    width: 20px;
}
.card-slider .card {
    position: absolute;
    left: 0;
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 0 5px rgba(0, 0, 0, .3);
    background: #fff;
    transition: transform 150ms,opacity 150ms;
}

本项目引用的自定义外部资源