# 内聚旋转效果轮播图

很多灵感来自:ElementUI-Carousel

比如: main 组件和 item 组件之间绑定、z-index 轮播图层叠展示等。。

# 思路

  1. 由 item 自己控制在哪个位置显示,通过 translateX 移动位置,scale 放大缩小,使用户能够聚焦于激活的 item。
  2. 比如 activeIndex 激活的项目,处于正中间,z-index 为 3 最大,translateX 为 0,scale 为 1。默认所有 item 都处于正中间,通过 flex 布局居中。
  3. 每个 item 都需要判断当前 index 和 activeIndex 所处位置, 如果为 -1 则表示应该移动到 activeItem 的左边。如果为 1 则表示应该移动到 activeItem 的右边。否者移动到正中间隐藏,z-index 为 1,scale 为 0.83。

# 代码例子

# 父组件

<template>
  <div class="carousel">
    <div class="wrapper">
      <slot></slot>
    </div>
    <div class="action">
      <button @click="prev">prev</button>
      <button @click="next">next</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Carousel',
  props: {
    itemWidth: {
      type: String,
      default: '400px',
    },
    itemHeight: {
      type: String,
      default: '200px',
    },
  },
  provide() {
    return {
      width: this.itemWidth,
      height: this.itemHeight,
    };
  },
  data() {
    return {
      activeIndex: 0,
      items: [],
    };
  },
  methods: {
    updatePositions() {
      this.items.forEach((child) => child.updatePosition());
    },
    prev() {
      this.activeIndex = this.decreaseIndex(
        this.activeIndex,
        this.items.length
      );
      this.updatePositions();
    },
    next() {
      this.activeIndex = this.increaseIndex(
        this.activeIndex,
        this.items.length
      );
      this.updatePositions();
    },
    increaseIndex(index, max) {
      return (index + 1) % max;
    },
    decreaseIndex(index, max) {
      const sw = index % max;
      const rever = (max - sw) % max;
      return (max - rever - 1) % max;
    },
    updateItems() {
      this.items = this.$children.filter(
        (comp) => comp.$options.name === 'CarouselItem'
      );
    },
  },
  mounted() {
    this.updateItems();
    this.updatePositions();
  },
  updated() {
    this.updateItems();
    this.updatePositions();
  },
};
</script>

<style scoped>
.carousel {
  position: relative;
}

.wrapper {
  width: 100%;
  height: 100%;
  position: relative;
  border: 1px solid red;
  display: flex;
  justify-content: center;
  align-items: center;
}

.action {
  position: absolute;
  width: 100%;
  display: flex;
  justify-content: center;
}
</style>

# 子组件

<template>
  <div
    class="carousel-item"
    ref="itemRef"
    :style="{ width: this.width, height: this.height }"
  >
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'CarouselItem',
  inject: ['height', 'width'],
  methods: {
    updatePosition() {
      const [direct, distance] = this.calcTranslate();
      switch (direct) {
        case 0:
          this.setTranslate(0, 1, 3);
          break;
        case -1:
          this.setTranslate(-distance, 0.83, 2);
          break;
        case 1:
          this.setTranslate(distance, 0.83, 2);
          break;
        default:
          this.setTranslate(0, 0.83, 1);
          break;
      }
    },
    calcTranslate() {
      const activeIndex = this.$parent.activeIndex;
      const index = this.$parent.items.indexOf(this);
      const length = this.$parent.items.length;
      const selfWidth = this.$refs.itemRef.offsetWidth;
      const containerWidth = this.$parent.$el.offsetWidth;

      const direct = this.calcDirection(activeIndex, index, length);
      const distance = Math.round(containerWidth / 2 - selfWidth / 2);
      return [direct, distance];
    },
    calcDirection(activeIndex, index, length) {
      if (activeIndex === index) {
        return 0;
      } else if (this.increaseIndex(activeIndex, length) === index) {
        return 1;
      } else if (this.decreaseIndex(activeIndex, length) === index) {
        return -1;
      } else {
        return 2;
      }
    },
    setTranslate(distance, scale, zIndex) {
      this.$refs.itemRef.style.transform = `translateX(${distance}px) scale(${scale})`;
      this.$refs.itemRef.style['z-index'] = zIndex;
    },
    increaseIndex(index, max) {
      return (index + 1) % max;
    },
    decreaseIndex(index, max) {
      const sw = index % max;
      const rever = (max - sw) % max;
      return (max - rever - 1) % max;
    },
  },
  created() {
    this.$parent && this.$parent.updatePositions();
  },
};
</script>

<style scoped>
.carousel-item {
  position: absolute;
  transition: all 0.5s;
  cursor: pointer;
}
</style>

上次更新: 7/29/2020, 10:46:06 AM