前几天在掘金上看到一个开源项目(PyUI)在招人,一个基于vue.js的UI库,想着反正页闲着,就硬着头皮加入了(其实我都还没学vue.js)
两天速成了一下,学了点皮毛,就开始撸代码了,组织给我分配的是carousel跑马灯的组件

边学边做,一天半撸完了,效果完全参照iview的跑马灯效果(任务要求)
其实原本只要拿iview的代码过来改一改就好了,但是感觉这样页学不到什么,硬着头皮自己撸了

第一次写vue.js感觉在项目任务的督促下学的还是很快的

carousel组件构成

跑马灯的过渡效果主要用了<transition>组件
我的主要思想是将carousel插槽内的代码块提取出来,用一个<li>列表放置在<transition>内,然后实现轮播效果

html部分

<template>
  <div :class="classes">
    <div class="py-carousel__slide-lists" ref="carousel_list">
      <slot></slot>
    </div>
    <button :class="arrowClasses" class="lefts" @click="change(currentIndex-1)">
    </button>
    <button :class="arrowClasses" class="rights" @click="change(currentIndex+1)">
    </button>
    <transition-group tag="div"  name="list" class="py-carousel__slide-current">
        <li v-for="(list,index) in slideList.length"
          class="py-carousel__slide-current-item"
          :key="index"
          v-show="index===currentIndex"
          @mouseenter="hover(1)"
          @mouseleave="hover(2)">
            <div v-html="slideList[index].outerHTML"></div>
        </li>
    </transition-group>
    <div :class="'py-carousel__positions-'+position" class="py-carousel__positions">
        <span v-for="(item,index) in slideList.length" :key="index"
        :class="positionClass(index)"
        @mouseover="hoverChange(index)"
        @mouseleave="hover(3)"
        @click="change(index)">
        </span>
    </div>
  </div>
</template>

js部分

我感觉相对于iview的代码,相同的效果我的代码量已经少了好多,当然可能人家的组件比较稳定吧,我的还没经过比较正规的测试

js部分主要就是一些函数鼠标对轮播图的操作,轮播函数是autoplay()go()两个函数主要控制的


<script>
// 组件class前缀
const prefixCls = 'py-carousel';
const Props = {
  arrow: new Set(['hover', 'always', 'never']),
  position: new Set(['inside', 'outside', 'none', 'left', 'right']),
};

export default {
  name: 'py-carousel',
  props: {
    // 初始位置
    value: {
      type: Number,
      default: 0,
    },
    // 轮播速度
    speed: {
      type: Number,
      default: 3000,
    },
    // 初始自动轮播
    autoloop: {
      type: Boolean,
      default: true,
    },
    // 自动轮播
    loop: {
      type: Boolean,
      default: true,
    },
    // 鼠标移至界面时是否暂停
    hoverstop: {
      type: Boolean,
      default: true,
    },
    // 两侧箭头显示时机
    arrow: {
      type: String,
      default: 'hover',
      validator (value) {
        return Props.arrow.has(value);
      },
    },
    // 底部指示器位置
    position: {
      type: String,
      default: 'outside',
      validator (value) {
        return Props.position.has(value);
      },
    },
    // 圆形指示器
    radiusPosition: {
      type: Boolean,
      default: false,
    },
    // 指示器触发方式
    trigger: {
      type: Boolean,
      default: true,
    },
  },
  data () {
    return {
      slideList: [],
      currentIndex: 0,
      timer: '',
    };
  },
  computed: {
    classes() {
      return `${prefixCls}`;
    },
    arrowClasses () {
      return [
        `py-carousel__arrow`,
        `py-carousel__arrow-${this.arrow}`,
      ];
    },
  },
  methods: {
    go() {
      this.timer = setInterval(() => {
        this.autoPlay();
      }, this.speed);
    },
    stop() {
      clearInterval(this.timer);
      this.timer = null;
    },
    hover(n) {
      if (n === 1 && this.hoverstop) this.stop();
      if (n === 2 && this.hoverstop) this.go();
      if (n === 3 && this.trigger) this.go();
    },
    change(index) {
      index = (index + this.slideList.length) % this.slideList.length;
      this.currentIndex = index;
      this.stop();
    },
    hoverChange(index) {
      if (this.trigger) {
        this.change(index);
      }
    },
    autoPlay() {
      if (this.loop) {
        this.currentIndex += 1;
        if (this.currentIndex > this.slideList.length - 1) {
          this.currentIndex = 0;
        }
      }
    },
    positionClass(index) {
      const classs = [];
      if (index === this.currentIndex) classs.push(`active`);
      if (this.radiusPosition) classs.push(`radius`);
      return classs;
    },
  },
  created() {
    this.$nextTick(() => {
      this.slideList = this.$refs.carousel_list.children;
      this.currentIndex = this.value;
      if (this.autoloop) {
        this.timer = setInterval(() => {
          this.autoPlay();
        }, this.speed);
      }
    });
  },
};
</script>

相对来说,代码量挺少了哈

css部分

css部分有个小问题就是,当底部指示器的位置设置在外部时,我是从carousel的高度中拿出5%的高度作为底部指示器的空间,然后carousel的高度是向外自适应的,所以carousel1的父节点必须指定高度,而且要和内部轮播代码框的高度一致

12月8日更新修复了上诉问题

以下为scss代码

@charset "UTF-8";

@include b(carousel) {
  box-sizing: border-box;
  position:relative;
  height: 100%;
  width: 100%;
  display: block;
  -webkit-box-sizing: border-box;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -ms-touch-action: pan-y;
  touch-action: pan-y;

  @include e(slide) {

    @include e(slide-lists) {
      display: none;
    }

    @include e(slide-current) {
      position: relative;
      width: 100%;
      height: 100%;
      display: block;
      overflow: hidden;
      padding:0;
      margin: 0;
      li {
        position: absolute;
        width: 100%;
        height: 100%;
      }
    }

    @include e(slide-current-item) {
      list-style-type:none;
      width: 100%;
      height: 100%;
      margin:0;
      padding:0;
    }
  }

  @include e(arrow) {
    border: none;
    outline: none;
    padding: 0;
    margin: 0;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    cursor: pointer;
    position: absolute;
    top: 50%;
    z-index: 10;
    transform: translateY(-50%);
    background-color: rgba(31, 45, 61, 0.11);
    text-align: center;
    color: aliceblue;
    font-size: 1em;
    font-family: inherit;
    line-height: inherit;
    &.rights {
      right: 16px;
    }
    &.lefts{
      left: 16px;
    }

    &.rights:before {
      content: ">";
    }
    &.lefts:before{
      content: "<";
    }

    @include e(arrow-hover) {
     display: inherit;
     opacity: 0;
    }

    @include e(arrow-always) {
      display: inherit;
    }
    @include e(arrow-never) {
      display: none;
    }

    &-hover > * {
      vertical-align: baseline;
    }

  }

  &:hover &__arrow-hover {
    opacity: 1;
  }

  @include e(positions) {
    display: block;
    z-index: 10;
    height: 16px;
    width: 100%;
    position: relative;
    text-align: center;

    @include e(positions-inside) {
      position: absolute;
      bottom: 8px;
      height: 12px;
    }

    @include e(positions-outside) {
      margin-top: 8px;
      width: 100%;
      position: relative;
    }

    @include e(positions-none) {
      display: none;
    }

    @include e(positions-left) {
      position: absolute;
      bottom: 8px;
      text-align: left;
      padding-left: 18px;
      height: 12px;
    }

    @include e(positions-right) {
      position: absolute;
      bottom: 8px;
      text-align: right;
      padding-right: 18px;
      height: 12px;
    }

    span {
      display: inline-block;
      vertical-align: top;
      text-align: center;
      margin: 0 2px;
      width: 20px;
      height: 3px;
      border-radius: 0;
      cursor: pointer;
      background-color:#898989;
      opacity: 0.5;
      font-size: 12px;
      &.active {
        width: 25px;
        background-color: rgb(82, 87, 74);
        opacity: 0.7;
      }
    }
    .radius {
      width: 12px;
      height: 12px;
      border-radius: 50%;
      border: 1px solid #2b2b28;
      background: rgba(82, 87, 74, 0.1);
      &.active {
        width: 12px;
        background-color: #505042;
        opacity: 0.7;
      }
    }
  }

  .list-enter-to {
    transition: all 0.8s ease;
    transform: translateX(0);
  }

  .list-leave-active {
    transition: all 0.8s ease;
    transform: translateX(-100%)
  }

  .list-enter {
    transform: translateX(100%)
  }

  .list-leave {
    transform: translateX(0)
  }
}

放上GitHub地址

PyUi