Due to the demand to do a photo album of business components, I think it is very good to look at, share with you, you can extract their own, 6 o ‘clock a few, work first!

<template> <div class="img-wrapper"> <el-button icon="h-icon-angle_left" plain class="left to" :disabled="disabledPrev" @click="toRight" ></el-button> <div ref="imagesWrapper" class="images"> <div v-if="noData" class="empty-text"> <slot Name ="empty"> </slot> </div> <div v-show="! noData" class="items" :style="itemsStyle"> <div v-for="(item, index) in data" :key="index" class="item-img" :style="imgSize" :class="{ 'is-active': index === currentIndex }" @click="selected(index)" > <img :title="item[props.title]" :src="item[props.url]" /> <p v-if="item[props.title]" v-ellipsis class="item-date"> {{ item[props.title] }} </p> </div> </div> </div> <el-button icon="h-icon-angle_right" :disabled="disabledNext" plain class="right to" @click="Left" ></el-button> </div> </template>  <script> export default { name: 'ImagesGroup', props: { imgSize: { type: Object, default: () => { return { width: '120px', height: '120px' }; } }, value: { type: Number, default: null }, data: { type: Array, default: () => [] } }, data() { return { props: {title: 'title', url: 'url', key: 'key', type: 'type'}, currentIndex: '', translateX: 0, pageSize: 0}; }, computed: {// offset itemsStyle() {return {transform: 'translateX(${-this.translatex}px)'}; }, / / each width itemWidth () {return parseInt (this) imgSize. Width. The replace (' p ', ')) + 8. }, disabledPrev() {// Disable the previous page button return this.translateX <= 0; }, disabledNext() {return (this.translatex >= (this.data.length-this.pagesize) * this.itemWidth); return (this.translatex >= (this.data.length-this.pagesize) * this.itemWidth); }, noData() { return this.data.length === 0; }, dataLength() { return this.data.length; } }, watch: { value: { immediate: true, handler(val) { this.currentIndex = val; } } }, mounted() { this.pageSize = this.wrapperWidth() / this.itemWidth; }, methods: { toRight() { if (this.translateX < this.pageSize * this.itemWidth) { this.translateX = 0; } else { this.translateX = this.translateX - this.pageSize * this.itemWidth; } }, Left() { this.translateX = this.translateX + this.pageSize * this.itemWidth; const maxTrans = this.itemWidth * (this.dataLength - this.pageSize); if (this.translateX > maxTrans) { this.translateX = maxTrans; } }, selected(index) { const center = this.pageSize >> 1; Const lastCenter = this.datalength -center; // const lastCenter = this.datalength -center; If (index > center && index < lastCenter) {const step = index-center; // Move the click image to the middle this.translatex = this.itemWidth * step; } else { const maxTrans = this.itemWidth * (this.dataLength - this.pageSize); this.translateX = index <= center ? 0 : maxTrans; } if (this.currentIndex ! == index) { this.currentIndex = index; this.$emit('input', index); } }, wrapperWidth() { if (this.$refs.imagesWrapper) { return this.$refs.imagesWrapper.offsetWidth; } return 0; }}}; </script> <style lang="less" scoped> .img-wrapper { display: flex; position: relative; margin: 20px; height: 120px; .to { width: 32px; height: 100%; padding: 0; } .images::before { position: absolute; z-index: 5; top: 0; height: 100%; width: 84px; content: ''; pointer-events: none; background: -webkit-gradient( linear, left top, right top, from(#fff), color-stop(50%, rgba(0, 0, 0, 0)) ); background: -o-linear-gradient(left, #fff, rgba(0, 0, 0, 0) 50%); background: linear-gradient(90deg, #fff, rgba(0, 0, 0, 0) 50%); } .images::after { position: absolute; z-index: 5; top: 0; height: 100%; right: 0; width: 84px; content: ''; pointer-events: none; background: -webkit-gradient( linear, right top, left top, from(#fff), color-stop(50%, rgba(0, 0, 0, 0)) ); background: -o-linear-gradient(right, #fff, rgba(0, 0, 0, 0) 50%); background: linear-gradient(270deg, #fff, rgba(0, 0, 0, 0) 50%); } .images { position: relative; overflow: hidden; width: 100%; height: 100%; margin: 0 2px; .empty-text { color: rgb(158, 158, 158); height: 100%; display: flex; align-items: center; justify-content: center; } .items { position: absolute; top: 0; left: 0; display: flex; // width: 6000px; height: 100%; align-items: center; 0.25 s help ease the transition: transform; .item-img { display: inline-block; box-sizing: border-box; position: relative; margin-right: 8px; border: 2px solid rgba(0, 0, 0, 0); cursor: pointer; .item-date { bottom: 0px; position: absolute; width: 100%; height: 24px; Background: rgba(0, 0, 0, 0.2); text-align: center; line-height: 24px; color: white; } img { width: 100%; height: 100%; } } .item-img:hover::after { // border-color: #409EFF; opacity: 0; } .item-img::after { position: absolute; top: 0; left: 0; width: 100%; height: 100%; content: ''; Opacity: 0.2; pointer-events: none; 0.3 s ease - its - the transition: opacity; 0.3 s ease - o - the transition: opacity; 0.3 s help ease the transition: opacity; background-color: #fff; } .is-active { border-color: #409eff; } .is-active:after { opacity: 0; } } } } </style>