<template>
  <view>
    <view class="k-carousel" :id="elId">
      <view
        class="k-carousel-container"
        :class="{ 'is-indicator': showIndicator }"
        @touchstart="touchStart"
        @touchmove="touchMove"
        @touchend="touchEnd"
        ref="container"
        :style="[swiperStyle]"
      >
        <!-- #ifdef MP-WEIXIN -->
        <view
          class="k-carousel-slide"
          @click="itemClick(item)"
          v-for="(item, index) in list"
          :item="item"
          :key="index"
          selectedColor="#465CFF"
          :style="{
            paddingRight: spaceBetween + 'px',
            width: totalWidth + 'px',
          }"
        >
          <image :src="item.image" mode="widthFix"></image>
          <view
            v-if="item.title"
            class="k-carousel-item-title"
            :class="{ ellipsis: ellipsis }"
            :style="{ right: spaceBetween + 'px' }"
            >{{ item.title }}
          </view>
        </view>
        <!-- #endif -->
        <!-- #ifdef H5 || APP-PLUS -->
        <view
          class="k-carousel-slide"
          @click="itemClick(item)"
          :style="{ paddingRight: spaceBetween + 'px', width: width + '%' }"
          v-for="(item, index) in list"
          :item="item"
          :key="index"
          selectedColor="#465CFF"
        >
          <image :src="item.image" mode="widthFix"></image>
          <view
            v-if="item.title"
            class="k-carousel-item-title"
            :class="{ ellipsis: ellipsis }"
            :style="{ right: spaceBetween + 'px' }"
            >{{ item.title }}
          </view>
        </view>
        <!-- #endif -->
      </view>
      <slot name="indicator"></slot>
      <view class="indicator" v-if="showIndicator">
        <slot name="indicator" v-if="showIndicator && slideCount > 1">
          <view
            v-for="(item, index) in slideCount"
            class="indicator-item"
            :class="{ active: index === currentIndex }"
            :key="index"
            @click="handleIndicatorClick(index)"
          ></view>
        </slot>
      </view>
    </view>
  </view>
</template>

<script lang="ts">
import Vue, { VueConstructor } from 'vue';
let observer: any = null;

export default (Vue as VueConstructor<Vue>).extend({
  name: 'KCarousel',
  props: {
    onClick: {
      type: Function,
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      default: () => {},
    },
    onChange: {
      type: Function,
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      default: () => {},
    },
    selectColor: {
      type: String,
      default: '',
    },
    interval: {
      type: Number,
      default: 3000,
    },
    list: {
      type: Array,
      default: () => [],
    },
    animDuration: {
      type: Number,
      default: 300,
    },
    moveRatio: {
      type: Number,
      default: 0.25,
    },
    showIndicator: {
      type: Boolean,
      default: true,
    },
    width: {
      type: Number,
      default: 100,
    },
    spaceBetween: {
      type: Number,
      default: 0,
    },
    autoplay: {
      type: Boolean,
      default: true,
    },
    ellipsis: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      slideCount: 0, // 元素个数
      totalWidth: 0, // swiper的宽度
      swiperStyle: {
        transition: '',
        transform: '',
      }, // swiper样式
      currentIndex: 0, // 当前的index
      scrolling: false, // 是否正在滚动
      playTimer: {} as NodeJS.Timeout,
      distance: 0,
      startX: 0,
      currentX: 0,
      elementWidth: 0,
      elId: `k-carousel_${Math.ceil(Math.random() * 10e5).toString(36)}`,
      appear: false,
      flag: true,
    };
  },
  watch: {
    appear(newVal) {
      // eslint-disable-next-line no-console
      // console.log('appear', newVal);
      if (newVal && this.autoplay) {
        this.startTimer();
      } else {
        this.stopTimer();
      }
    },
  },
  mounted() {
    this.handleDom();
    // if (this.appear && this.autoplay) this.startTimer();

    if (this.autoplay) {
      const threshold = 0.5;
      this.$nextTick(() => {
        const ob = uni.createIntersectionObserver(this, {
          thresholds: [threshold],
          initialRatio: threshold,
        });
        ob.relativeToViewport({
          bottom: 0,
        }).observe(`#${this.elId}`, (res) => {
          // eslint-disable-next-line no-console
          // console.log(res);
          if (res.intersectionRatio > threshold) {
            this.appear = true;
          } else {
            this.appear = false;
          }
        });
      });
    }
  },
  beforeDestroy() {
    if (observer) {
      observer.disconnect();
    }
  },
  methods: {
    itemClick(item: any) {
      this.$emit('onClick', item);
    },
    itemChange(item: any) {
      this.$emit('onChange', item);
    },
    // 定时器操作
    startTimer() {
      if (this.playTimer) {
        this.stopTimer();
      }
      this.playTimer = setInterval(() => {
        if (this.currentIndex < this.slideCount - 1) {
          this.currentIndex++;
          if (this.currentIndex === this.slideCount - 1) {
            this.scrollContent(
              -(
                this.currentIndex * this.elementWidth -
                this.slideCount * (this.elementWidth - this.totalWidth)
              ) + this.spaceBetween
            );
            return;
          }
          this.scrollContent(-this.currentIndex * this.totalWidth);
        } else {
          this.currentIndex = 0;
          this.scrollContent(0);
        }
      }, this.interval);
    },
    stopTimer() {
      clearInterval(this.playTimer);
    },
    // 滚动到正确的位置
    scrollContent(currentPosition: number) {
      // 0.设置正在滚动
      this.scrolling = true;
      // 1.开始滚动动画
      this.$set(this.swiperStyle, 'transition', this.animDuration + 'ms');
      this.setTransform(currentPosition);
      // 2.判断滚动到的位置
      this.checkPosition();
      // 4.滚动完成
      this.scrolling = false;
    },
    // 校验正确的位置
    checkPosition() {
      setTimeout(() => {
        // 1.校验正确的位置
        this.$set(this.swiperStyle, 'transition', '0ms');
      }, this.animDuration);
    },
    // 设置滚动的位置
    setTransform(position: number) {
      if (this.flag) this.itemChange(this.list[this.currentIndex]);
      this.$set(
        this.swiperStyle,
        'transform',
        `translate3d(${position}px, 0, 0)`
      );
    },
    handleDom() {
      this.slideCount = this.list.length;
      const query = uni.createSelectorQuery().in(this);
      query
        .select('.k-carousel-container')
        .boundingClientRect((data) => {
          this.elementWidth = data.width || 0;
          this.totalWidth = this.elementWidth * (this.width / 100);
        })
        .exec();
      this.setTransform(0);
    },
    // 拖动事件的处理
    touchStart(e: any) {
      this.flag = false;
      // 1.如果正在滚动, 不可以拖动
      if (this.scrolling) return;
      // 2.停止定时器
      this.stopTimer();
      // 3.保存开始滚动的位置
      this.startX = e.touches[0].pageX;
    },
    touchMove(e: any) {
      // 1.计算出用户拖动的距离
      this.currentX = e.touches[0].pageX;
      this.distance = this.currentX - this.startX;
      let currentPosition;
      if (this.currentIndex === this.slideCount - 1) {
        currentPosition =
          -(
            this.currentIndex * this.elementWidth -
            this.slideCount * (this.elementWidth - this.totalWidth)
          ) + this.spaceBetween;
      } else {
        currentPosition = -this.currentIndex * this.totalWidth;
      }
      let moveDistance = this.distance + currentPosition;
      this.setTransform(moveDistance);
    },
    touchEnd() {
      this.flag = true;
      // 1.获取移动的距离
      let currentMove = Math.abs(this.distance);
      // 2.判断最终的距离
      if (this.distance === 0) {
        return;
      } else if (
        this.distance > 0 &&
        currentMove > this.totalWidth * this.moveRatio
      ) {
        // 右边移动超过0.5
        if (this.currentIndex === 0) {
          this.$set(
            this.swiperStyle,
            'transform',
            `translate3d(${this.currentIndex * this.totalWidth}px, 0, 0)`
          );
        } else {
          this.currentIndex--;
        }
      } else if (
        this.distance < 0 &&
        currentMove > this.totalWidth * this.moveRatio
      ) {
        // 向左移动超过0.5
        if (this.currentIndex === this.slideCount - 1) {
          this.$set(
            this.swiperStyle,
            'transform',
            `translate3d(${-(
              this.currentIndex * this.elementWidth -
              this.slideCount * (this.elementWidth - this.totalWidth) -
              this.spaceBetween
            )}px, 0, 0)`
          );
        } else {
          this.currentIndex++;
        }
      }
      // 3.移动到正确的位置
      if (this.currentIndex === this.slideCount - 1) {
        this.scrollContent(
          -(
            this.currentIndex * this.elementWidth -
            this.slideCount * (this.elementWidth - this.totalWidth)
          ) + this.spaceBetween
        );
      } else {
        this.scrollContent(-this.currentIndex * this.totalWidth);
      }
      // 4.移动完成后重新开启定时器
      if (this.autoplay) this.startTimer();
    },
    handleIndicatorClick(index: number) {
      // 1.移除定时器
      this.stopTimer();
      this.currentIndex = index;
      if (this.currentIndex === this.slideCount - 1) {
        this.scrollContent(
          -(
            this.currentIndex * this.elementWidth -
            this.slideCount * (this.elementWidth - this.totalWidth)
          ) + this.spaceBetween
        );
      } else {
        this.scrollContent(-this.currentIndex * this.totalWidth);
      }
      if (this.autoplay) this.startTimer();
    },
  },
});
</script>

<style scoped lang="scss">
.kui-wrap {
  // padding-bottom: 64rpx;
  box-sizing: border-box;
  color: var(--k-color-neutral-03, #999);
}

.theme-change-container {
  display: flex;
  align-items: center;
}

.k-carousel {
  overflow: hidden;
  position: relative;
  height: auto;
  width: 100%;
}

.k-carousel-container {
  display: flex;
}

.indicator {
  display: flex;
  justify-content: center;
  position: absolute;
  width: 100%;
  bottom: 13px;
  height: 5px;

  .indicator-item {
    box-sizing: border-box;
    width: 10px;
    height: 5px;
    /* border-radius: 4px; */
    background: var(--k-color-primary-03, #666);
    line-height: 5px;
    border-radius: 5px;
    text-align: center;
    font-size: 12px;
    margin: 0 5px;

    &.active {
      width: 20px;
      /* stylelint-disable-next-line declaration-no-important */
      background: var(--k-color-primary-01, #064c8a) !important;
    }
  }
}

.is-indicator {
  .k-carousel-slide {
    padding-bottom: 30px;
  }
}

.k-carousel-slide {
  flex-shrink: 0;
  position: relative;
  padding-bottom: 0;
  box-sizing: border-box;

  image {
    display: block;
    width: 100%;
    height: auto;
  }
}

.is-indicator {
  .k-carousel-item-title {
    bottom: 30px;
  }
}

.k-carousel-item-title {
  position: absolute;
  left: 0;
  bottom: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 9;
  color: #fff;
  padding: 1vh 2vw;
  font-size: 12px;

  &.ellipsis {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
}
</style>
