element ui 中轮播图组件样式修改为三列展示轮播

实现效果
在这里插入图片描述
在使用 Element UI 组件库中的跑马灯组件时,需求是三列卡片轮播的实现。虽然 Element UI 中跑马灯组件提供了 type=‘card’ 属性,去设置轮播为卡片类型,但是样式不是我们所期待的,不想要缩放效果,于是便对跑马灯组件源码进行调整。
在这里插入图片描述
源码主要修改
跑马灯组件主要是 carousel 和 carouselItem 组件。可以单独把源码中的这两个组件文件复制一份进行修改。

其中 carousel 文件不需要修改,只是复制一份即可,在 carouselItem 文件中将 const CARD_SCALE = 0.83 更换为 const CARD_SCALE = 1。接着去修改控制位移的 calcCardTranslate() 函数即可,我这里需要三列轮播,做以下调整,总体上样式还是使用 Element UI 默认样式,只是细节上的调整。

item组件
    calcCardTranslate(index, activeIndex) {
          let parentWidth = this.$parent.$el.offsetWidth;
          //减去左右两边箭头的宽度
            parentWidth = parentWidth - 100;
          if (this.inStage) {
            return parentWidth * ((2 - CARD_SCALE) * (index - activeIndex) + 1) / 3;
          } else if (index < activeIndex) {
          //三张卡 除三  原始有缩放和位移是除四
            return -(1 + CARD_SCALE) * parentWidth / 3;
          } else {
            return (3 + CARD_SCALE) * parentWidth / 3;
          }
        },
  
调用组件
        <Carousel  type="card" height="250px" :autoplay="false" indicator-position="none" class="carousel-box">
          <CarouselItem v-for="(item,index) in cardData" :key="index"  style="width:33%;"   >
            <div class="card-box">
        {{item}}
            </div>
          </CarouselItem>
        </Carousel>

主要是main.vue组件ui和css的调整
main.vue文件

<template>
    <div class="carouselBox">
        <div  class="leftBth" >
            <i class="el-icon-arrow-left "
            v-show=" (loop || activeIndex > 0)"
            @mouseenter="handleButtonEnter('left')"
            @mouseleave="handleButtonLeave"
            @click.stop="throttledArrowClick(activeIndex - 1)"/>
        </div>
        <div
        :class="carouselClasses "
        class="contentBox"
        @mouseenter.stop="handleMouseEnter"
        @mouseleave.stop="handleMouseLeave">
        <div
            class="el-carousel__container"
            :style="{ height: height }">
            <!-- <transition
            v-if="arrowDisplay"
            name="carousel-arrow-left">
            <button
                type="button"
                v-show="(arrow === 'always' || hover) && (loop || activeIndex > 0)"
                @mouseenter="handleButtonEnter('left')"
                @mouseleave="handleButtonLeave"
                @click.stop="throttledArrowClick(activeIndex - 1)"
                class="el-carousel__arrow el-carousel__arrow--left">
                <i class="el-icon-arrow-left"></i>
            </button>
            </transition>
            <transition
            v-if="arrowDisplay"
            name="carousel-arrow-right">
            <button
                type="button"
                v-show="(arrow === 'always' || hover) && (loop || activeIndex < items.length - 1)"
                @mouseenter="handleButtonEnter('right')"
                @mouseleave="handleButtonLeave"
                @click.stop="throttledArrowClick(activeIndex + 1)"
                class="el-carousel__arrow el-carousel__arrow--right">
                <i class="el-icon-arrow-right"></i>
            </button>
            </transition> -->
            <slot></slot>
        </div>
        <ul
            v-if="indicatorPosition !== 'none'"
            :class="indicatorsClasses">
            <li
            v-for="(item, index) in items"
            :key="index"
            :class="[
                'el-carousel__indicator',
                'el-carousel__indicator--' + direction,
                { 'is-active': index === activeIndex }]"
            @mouseenter="throttledIndicatorHover(index)"
            @click.stop="handleIndicatorClick(index)">
            <button class="el-carousel__button">
                <span v-if="hasLabel">{{ item.label }}</span>
            </button>
            </li>
        </ul>
        </div>
        <div class="rightBtn">
            <i  class="el-icon-arrow-right"

            v-show=" (loop || activeIndex < items.length - 1)"
            @mouseenter="handleButtonEnter('right')"
            @mouseleave="handleButtonLeave"
            @click.stop="throttledArrowClick(activeIndex + 1)"/>
        </div>
   </div>
  </template>
  
  <script>
  import throttle from 'throttle-debounce/throttle';
  import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
  
  export default {
    name: 'ElCarousel',
  
    props: {
      initialIndex: {
        type: Number,
        default: 0
      },
      height: String,
      trigger: {
        type: String,
        default: 'hover'
      },
      autoplay: {
        type: Boolean,
        default: true
      },
      interval: {
        type: Number,
        default: 3000
      },
      indicatorPosition: String,
      indicator: {
        type: Boolean,
        default: true
      },
      arrow: {
        type: String,
        default: 'hover'
      },
      type: String,
      loop: {
        type: Boolean,
        default: true
      },
      direction: {
        type: String,
        default: 'horizontal',
        validator(val) {
          return ['horizontal', 'vertical'].indexOf(val) !== -1;
        }
      }
    },
  
    data() {
      return {
        items: [],
        activeIndex: -1,
        containerWidth: 0,
        timer: null,
        hover: false
      };
    },
  
    computed: {
      arrowDisplay() {
        return this.arrow !== 'never' && this.direction !== 'vertical';
      },
  
      hasLabel() {
        return this.items.some(item => item.label.toString().length > 0);
      },
  
      carouselClasses() {
        const classes = ['el-carousel', 'el-carousel--' + this.direction];
        if (this.type === 'card') {
          classes.push('el-carousel--card');
        }
        return classes;
      },
  
      indicatorsClasses() {
        const classes = ['el-carousel__indicators', 'el-carousel__indicators--' + this.direction];
        if (this.hasLabel) {
          classes.push('el-carousel__indicators--labels');
        }
        if (this.indicatorPosition === 'outside' || this.type === 'card') {
          classes.push('el-carousel__indicators--outside');
        }
        return classes;
      }
    },
  
    watch: {
      items(val) {
        if (val.length > 0) this.setActiveItem(this.initialIndex);
      },
  
      activeIndex(val, oldVal) {
        this.resetItemPosition(oldVal);
        if (oldVal > -1) {
          this.$emit('change', val, oldVal);
        }
      },
  
      autoplay(val) {
        val ? this.startTimer() : this.pauseTimer();
      },
  
      loop() {
        this.setActiveItem(this.activeIndex);
      },
  
      interval() {
        this.pauseTimer();
        this.startTimer();
      }
    },
  
    methods: {
      handleMouseEnter() {
        this.hover = true;
        this.pauseTimer();
      },
  
      handleMouseLeave() {
        this.hover = false;
        this.startTimer();
      },
  
      itemInStage(item, index) {
        const length = this.items.length;
        if (index === length - 1 && item.inStage && this.items[0].active ||
          (item.inStage && this.items[index + 1] && this.items[index + 1].active)) {
          return 'left';
        } else if (index === 0 && item.inStage && this.items[length - 1].active ||
          (item.inStage && this.items[index - 1] && this.items[index - 1].active)) {
          return 'right';
        }
        return false;
      },
  
      handleButtonEnter(arrow) {
        if (this.direction === 'vertical') return;
        this.items.forEach((item, index) => {
          if (arrow === this.itemInStage(item, index)) {
            item.hover = true;
          }
        });
      },
  
      handleButtonLeave() {
        if (this.direction === 'vertical') return;
        this.items.forEach(item => {
          item.hover = false;
        });
      },
  
      updateItems() {
        this.items = this.$children.filter(child => child.$options.name === 'ElCarouselItem');
      },
  
      resetItemPosition(oldIndex) {
        this.items.forEach((item, index) => {
          item.translateItem(index, this.activeIndex, oldIndex);
        });
      },
  
      playSlides() {
        if (this.activeIndex < this.items.length - 1) {
          this.activeIndex++;
        } else if (this.loop) {
          this.activeIndex = 0;
        }
      },
  
      pauseTimer() {
        if (this.timer) {
          clearInterval(this.timer);
          this.timer = null;
        }
      },
  
      startTimer() {
        if (this.interval <= 0 || !this.autoplay || this.timer) return;
        this.timer = setInterval(this.playSlides, this.interval);
      },
  
      resetTimer() {
        this.pauseTimer();
        this.startTimer();
      },
  
      setActiveItem(index) {
        if (typeof index === 'string') {
          const filteredItems = this.items.filter(item => item.name === index);
          if (filteredItems.length > 0) {
            index = this.items.indexOf(filteredItems[0]);
          }
        }
        index = Number(index);
        if (isNaN(index) || index !== Math.floor(index)) {
          console.warn('[Element Warn][Carousel]index must be an integer.');
          return;
        }
        let length = this.items.length;
        const oldIndex = this.activeIndex;
        if (index < 0) {
          this.activeIndex = this.loop ? length - 1 : 0;
        } else if (index >= length) {
          this.activeIndex = this.loop ? 0 : length - 1;
        } else {
          this.activeIndex = index;
        }
        if (oldIndex === this.activeIndex) {
          this.resetItemPosition(oldIndex);
        }
        this.resetTimer();
      },
  
      prev() {
        this.setActiveItem(this.activeIndex - 1);
      },
  
      next() {
        this.setActiveItem(this.activeIndex + 1);
      },
  
      handleIndicatorClick(index) {
        this.activeIndex = index;
      },
  
      handleIndicatorHover(index) {
        if (this.trigger === 'hover' && index !== this.activeIndex) {
          this.activeIndex = index;
        }
      }
    },
  
    created() {
      this.throttledArrowClick = throttle(300, true, index => {
        this.setActiveItem(index);
      });
      this.throttledIndicatorHover = throttle(300, index => {
        this.handleIndicatorHover(index);
      });
    },
  
    mounted() {
      this.updateItems();
      this.$nextTick(() => {
        addResizeListener(this.$el, this.resetItemPosition);
        if (this.initialIndex < this.items.length && this.initialIndex >= 0) {
          this.activeIndex = this.initialIndex;
        }
        this.startTimer();
      });
    },
  
    beforeDestroy() {
      if (this.$el) removeResizeListener(this.$el, this.resetItemPosition);
      this.pauseTimer();
    }
  };
  </script>
  <style lang="scss" scoped>
  .leftBth ,.rightBtn{
    width: 50px;
    display: inline-block;
    position: absolute;
    top: 50%;
    transform: translate(0,-50%);
    > i {
        display: flex;
        color: #0F5AA8;
        font-size: 30px;
        font-weight: bolder;
    }
  }
  .leftBth {
    > i {
        float: left;
    }
  }
  .rightBtn {
    > i {
        float: right;
    }
  }
  .contentBox{
    width:calc(100% - 100px);
    // border: 1px solid blue;
    display: inline-block;
    margin-left: 50px;
  }
  .carouselBox{
    width:100%;
    position: relative;
    // border: 1px solid blue;
  }
  ::v-deep .is-active{
   height: 100% !important;
   width:100%; 
  //  border: 3px #0F5AA8 solid;
  //  border-radius: 4px;
   background-image: url(../../../assets/images/lineControl/border.png);
   background-repeat: no-repeat;
   background-position:right top;
   background-size:100% 100%;
   background-color: transparent !important;
   border: none !important;
   
 }
 ::v-deep  .el-carousel__item {
    background-color: #FFFFFF;
    border: 1px solid #EDEDF2 ;
    height: calc(100% - 10px);
    width: 100%;
   
  }
  
  </style>
  

item文件

<template>
    <div
      v-show="ready"
      class="el-carousel__item"
      :class="{
        'is-active': active,
        'el-carousel__item--card': $parent.type === 'card',
        'is-in-stage': inStage,
        'is-hover': hover,
        'is-animating': animating
      }"
      @click="handleItemClick"
      :style="itemStyle">
      <div
        v-if="$parent.type === 'card'"
        v-show="!active"
        class="el-carousel__mask">
      </div>
      <slot></slot>
    </div>
  </template>
  
  <script>
    import { autoprefixer } from 'element-ui/src/utils/util';
    // const CARD_SCALE = 0.83;
    const CARD_SCALE = 1;
    export default {
      name: 'ElCarouselItem',
  
      props: {
        name: String,
        label: {
          type: [String, Number],
          default: ''
        }
      },
  
      data() {
        return {
          hover: false,
          translate: 0,
          scale: 1,
          active: false,
          ready: false,
          inStage: false,
          animating: false
        };
      },
  
      methods: {
        processIndex(index, activeIndex, length) {
          if (activeIndex === 0 && index === length - 1) {
            return -1;
          } else if (activeIndex === length - 1 && index === 0) {
            return length;
          } else if (index < activeIndex - 1 && activeIndex - index >= length / 2) {
            return length + 1;
          } else if (index > activeIndex + 1 && index - activeIndex >= length / 2) {
            return -2;
          }
          return index;
        },
  
        calcCardTranslate(index, activeIndex) {
          let parentWidth = this.$parent.$el.offsetWidth;
          //减去左右两边箭头的宽度
            parentWidth = parentWidth - 100;
          if (this.inStage) {
            return parentWidth * ((2 - CARD_SCALE) * (index - activeIndex) + 1) / 3;
          } else if (index < activeIndex) {
            return -(1 + CARD_SCALE) * parentWidth / 3;
          } else {
            return (3 + CARD_SCALE) * parentWidth / 3;
          }
        },
  
        calcTranslate(index, activeIndex, isVertical) {
          const distance = this.$parent.$el[isVertical ? 'offsetHeight' : 'offsetWidth'];
          return distance * (index - activeIndex);
        },
  
        translateItem(index, activeIndex, oldIndex) {
          const parentType = this.$parent.type;
          const parentDirection = this.parentDirection;
          const length = this.$parent.items.length;
          if (parentType !== 'card' && oldIndex !== undefined) {
            this.animating = index === activeIndex || index === oldIndex;
          }
          if (index !== activeIndex && length > 2 && this.$parent.loop) {
            index = this.processIndex(index, activeIndex, length);
          }
          if (parentType === 'card') {
            if (parentDirection === 'vertical') {
              console.warn('[Element Warn][Carousel]vertical direction is not supported in card mode');
            }
            this.inStage = Math.round(Math.abs(index - activeIndex)) <= 1;
            this.active = index === activeIndex;
            this.translate = this.calcCardTranslate(index, activeIndex);
            this.scale = this.active ? 1 : CARD_SCALE;
          } else {
            this.active = index === activeIndex;
            const isVertical = parentDirection === 'vertical';
            this.translate = this.calcTranslate(index, activeIndex, isVertical);
            this.scale = 1;
          }
          this.ready = true;
        },
  
        handleItemClick() {
          const parent = this.$parent;
          if (parent && parent.type === 'card') {
            const index = parent.items.indexOf(this);
            parent.setActiveItem(index);
          }
        }
      },
  
      computed: {
        parentDirection() {
          return this.$parent.direction;
        },
  
        itemStyle() {
          const translateType = this.parentDirection === 'vertical' ? 'translateY' : 'translateX';
          const value = `${translateType}(${ this.translate }px) scale(${ this.scale })`;
          const style = {
            transform: value
          };
          return autoprefixer(style);
        }
      },
  
      created() {
        this.$parent && this.$parent.updateItems();
      },
  
      destroyed() {
        this.$parent && this.$parent.updateItems();
      }
    };
  </script>
  

选中样式的带箭头的边框是使用的背景图片,尝试使用css伪类完成箭头,但是会形成竖向滚动条(如下图),过度修改elementui的原生样式,所以选择背景图片方式
在这里插入图片描述
需要修改被选中的样式,和统一修改其他卡片的高度才能和图片背景边框对齐

  ::v-deep .is-active{
   height: 100% !important;
   width:100%; 
  //  border: 3px #0F5AA8 solid;
  //  border-radius: 4px;
   background-image: url(../../../assets/images/lineControl/border.png);
   background-repeat: no-repeat;
   background-position:right top;
   background-size:100% 100%;
   background-color: transparent !important;
   border: none !important;
   
 }
 ::v-deep  .el-carousel__item {
    background-color: #FFFFFF;
    border: 1px solid #EDEDF2 ;
    height: calc(100% - 10px);
    width: 100%;
   
  }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值