<!--
 * @Author: Billy
 * @Date: 2021-12-20 09:54:19
 * @LastEditors: Billy
 * @LastEditTime: 2022-01-06 13:59:12
 * @Description: 请输入
-->

<template>
  <div class="sliders-container" ref="sliders-container">
    <Draggable
      :class="[index === activeIndex ? 'active' : '']"
      class="slider"
      v-for="(item, index) in sliders"
      ref="sliders"
      :key="index"
      :id="index"
      :inertia="false"
      :moveDirection="'horizontal'"
      :isFrozen="isFrozen"
      :style="{ width: perSliderWidth + 'px' }"
      @drag-start="onDragStart"
      @drag-move="onDragMove"
      @drag-end="onDragEnd"
      @drag-enter="onDragEnter"
      @mousedown.native="onSliderMousedown($event, index)"
    >
      <slot
        v-bind:item="item"
        v-bind:index="index"
        v-bind:isActive="index === activeIndex"
      ></slot>
    </Draggable>
  </div>
</template>
<script>
import Draggable from "./Drag/Draggable.vue";
// import Velocity from "velocity-animate";
export default {
  components: { Draggable },
  props: {
    // 滑块对象的数组
    sliders: {
      type: Array,
      default: function () {
        return [];
      },
    },
    // 当前激活菜单的 index (支持.sync)
    currentActive: { type: Number, default: 0 },
    // 滑块移动方向
    direction: {
      type: String,
      default: "horizontal",
      validator: function (value) {
        return ["horizontal", "vertical"].indexOf(value) !== -1;
      },
    },
    // 滑块滑动的时间
    millisecondOfMove: { type: Number, default: 250 },
    // 每个滑块的宽度(不传则不设定宽度)
    perSliderWidth: { type: Number },
  },
  computed: {
    // 当前被激活(选中)的标签index
    activeIndex: {
      get() {
        return this.currentActive;
      },
      set(val) {
        this.calcCurrSliderKeyPoints(val);
        this.activeSliderWidth = this.slidersInfo[val].width;
        this.$emit("update:currentActive", val);
      },
    },
  },
  data() {
    return {
      slidersInfo: [], // {dom, width, midPoint}
      activeSliderStartPoint: 0, // slider 头部坐标
      activeSliderEndPoint: 0, // slider 尾部坐标
      activeSliderWidth: 0, // slider 的宽度
      indexOfSliderNeedToMoveCurrentlyAndActiveSliderShouldBe: 0,
      isFrozen: false,
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.refreshSlidersInfo();
    });

    // this.calcAllTabsMidPoints();

    // https://stackoverflow.com/questions/11700927/horizontal-scrolling-with-mouse-wheel-in-a-div
    const slidersContainer = this.$refs["sliders-container"];
    slidersContainer.addEventListener(
      "mousewheel",
      (e) => {
        e = window.event || e;
        var delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail));
        slidersContainer.scrollLeft -= delta * 80; // Multiplied by 80
        e.preventDefault();
      },
      false
    );
  },
  methods: {
    DoFrozen() {
      this.isFrozen = true;
    },
    DisFrozen() {
      this.isFrozen = false;
    },

    onDragStart(info) {},
    onDragMove(info) {
      // 移动后的 slider 头部的坐标
      const activeSliderStartPointCurr =
        this.activeSliderStartPoint + info.currentX;
      // 移动后的 slider 尾部的坐标
      const activeSliderEndPointCurr =
        this.activeSliderEndPoint + info.currentX;

      const midPoints = this.slidersInfo.map((ti) => ti.midPoint);
      // 当前激活 slider 之前的 sliders 的中间点集合
      const midPointsBefore = midPoints.slice(0, this.activeIndex);
      // 当前激活 slider 之后的 sliders 的中间点集合
      const midPointsAfter = midPoints.slice(this.activeIndex + 1);

      let index = this.activeIndex;
      // 查找需要移动的 slider 的index
      function findNeedToMoveIndex() {
        if (info.currentX > 0) {
          for (const k in midPointsAfter) {
            const point = midPointsAfter[k];
            if (point > activeSliderEndPointCurr) {
              index += Number(k);
              return;
            }
          }
          index = midPoints.length - 1; // 最后一个 slider
        } else {
          for (const k in midPointsBefore.reverse()) {
            const point = midPointsBefore[k];
            if (point < activeSliderStartPointCurr) {
              index -= Number(k);
              return;
            }
          }
          index = 0; // 第0个 slider
        }
      }

      findNeedToMoveIndex();

      this.indexOfSliderNeedToMoveCurrentlyAndActiveSliderShouldBe = index;

      // console.log("当前需要移动的 slider index :>> ", index);

      if (index !== this.activeIndex) {
        const refSlider = this.$refs["sliders"][index];
        let _index = index;
        if (index > this.activeIndex) {
          // 当需要移动的 slider 在当前激活 slider 的后面时
          refSlider.MoveTo({
            x: -this.activeSliderWidth,
            transitionDuration: this.millisecondOfMove,
            afterMoveTo: () => {},
          });
          // 当需要移动的 slider 的后面的 sliders 需要复原
          while (_index < midPoints.length - 1) {
            _index++;
            this.$refs["sliders"][_index].Rebound({
              transitionDuration: this.millisecondOfMove,
              afterRebound: () => {},
            });
          }
        } else {
          // 当需要移动的 slider 在当前激活 slider 的前面时
          refSlider.MoveTo({
            x: this.activeSliderWidth,
            transitionDuration: this.millisecondOfMove,
            afterMoveTo: () => {},
          });
          // 当需要移动的 slider 的前面的 sliders 需要复原
          while (_index > 0) {
            _index--;
            this.$refs["sliders"][_index].Rebound({
              transitionDuration: this.millisecondOfMove,
              afterRebound: () => {},
            });
          }
        }
      } else {
        // 当需要移动的 slider 就是当前激活的 slider 时
        let _index = 0;
        while (_index < midPoints.length - 1) {
          if (_index !== this.activeIndex) {
            this.$refs["sliders"][_index].Rebound({
              transitionDuration: this.millisecondOfMove,
            });
          }
          _index++;
        }
      }
    },

    onDragEnd(info) {
      const index =
        this.indexOfSliderNeedToMoveCurrentlyAndActiveSliderShouldBe;

      const widths = this.slidersInfo.map((ti) => ti.width);
      let point = 0;
      if (index > this.activeIndex) {
        // 移动到原位置的右边
        for (let i = this.activeIndex + 1; i <= index; i++) {
          point += widths[i];
        }
      } else {
        // 移动到原位置的左边 或保持原位
        for (let i = index; i < this.activeIndex; i++) {
          point -= widths[i];
        }
      }

      const currSlider = this.$refs["sliders"][this.activeIndex];

      this.isFrozen = true;

      currSlider.MoveTo({
        x: point,
        transitionDuration: this.millisecondOfMove,
        zIndex: 1000,

        afterMoveTo: () => {
          this.dragAndDrop(
            this.sliders,
            this.activeIndex,
            this.indexOfSliderNeedToMoveCurrentlyAndActiveSliderShouldBe
          );

          this.$nextTick(() => {
            this.$refs["sliders"].forEach((slider) => {
              slider.Clear();
            });

            this.refreshSlidersInfo();
            // this.calcAllTabsMidPoints();

            this.activeIndex =
              this.indexOfSliderNeedToMoveCurrentlyAndActiveSliderShouldBe;

            this.isFrozen = false;
          });
        },
      });
    },

    onDragEnter(info) {},

    onSliderMousedown(event, index) {
      switch (event.button) {
        case 0: // 左键
          if (!this.isFrozen) this.activeIndex = index;
          break;
        case 1: // 中轮
          break;
        case 2: // 右键
          break;
        default:
          break;
      }
    },

    // handleMoveEnd(Rebound) {
    //   Rebound({});
    // },

    // 更新所有 slider 的信息
    refreshSlidersInfo() {
      const slidersRef = this.$refs["sliders"];
      this.slidersInfo = [];
      // 找出 slider 的宽度
      for (const sliderRef of slidersRef) {
        const dom = sliderRef.$el;
        const width = dom.getBoundingClientRect().width;

        const rectSlider = dom.getBoundingClientRect();
        const rectContainer =
          this.$refs["sliders-container"].getBoundingClientRect();
        const startPoint = rectSlider.left - rectContainer.left;
        const endPoint = rectSlider.right - rectContainer.left;
        const midPoint = startPoint + width / 2;

        this.slidersInfo.push({
          dom,
          width,
          startPoint,
          endPoint,
          midPoint,
        });
      }
    },

    // 计算当前激活的 slider 的头部和尾部相对坐标点
    calcCurrSliderKeyPoints(activeIndex) {
      // ----------下面是旧写法------------------------------------------
      // const widths = this.slidersInfo.map((ti) => ti.width);
      // const widthsLess = widths.slice(0, activeIndex);
      // const widthsMore = widths.slice(0, activeIndex + 1);

      // let sumStartPoint = 0,
      //   sumEndPoint = 0;
      // widthsLess.forEach((el) => (sumStartPoint += el));
      // widthsMore.forEach((el) => (sumEndPoint += el));

      // this.activeSliderStartPoint = sumStartPoint;
      // this.activeSliderEndPoint = sumEndPoint;

      // ----------下面是新写法------------------------------------------
      const doms = this.slidersInfo.map((ti) => ti.dom);
      const rectActive = doms[activeIndex].getBoundingClientRect();
      const rectContainer =
        this.$refs["sliders-container"].getBoundingClientRect();

      const startPoint = rectActive.left - rectContainer.left;
      const endPoint = rectActive.right - rectContainer.left;

      this.activeSliderStartPoint = startPoint;
      this.activeSliderEndPoint = endPoint;
    },

    // 找出 slider 的中间点(弃用)
    calcAllTabsMidPoints() {
      for (const key in this.slidersInfo) {
        if (Object.hasOwnProperty.call(this.slidersInfo, key)) {
          const ti = this.slidersInfo[key];
          if (key - 1 >= 0) {
            ti.midPoint =
              this.slidersInfo[key - 1].midPoint +
              this.slidersInfo[key - 1].width / 2 +
              ti.width / 2;
          } else {
            ti.midPoint = ti.width / 2;
          }
          // ti.title = this.sliders[key].title;
        }
      }
    },

    swap(arr, i1, i2) {
      arr[i1] = arr.splice(i2, 1, arr[i1])[0];
      return arr;
    },

    dragAndDrop(arr, i1, i2) {
      let o = arr.splice(i1, 1)[0];
      arr.splice(i2, 0, o);
      return arr;
    },
  },
};
</script>
<style lang="scss" scoped>
@import "~@/scss/_variables.scss";
.sliders-container {
  width: 100%;
  height: 100%;
  display: flex;
  overflow: auto;

  scrollbar-width: none; /* For Firefox */
  -ms-overflow-style: none; /* For Internet Explorer and Edge */
  &::-webkit-scrollbar {
    /* For Chrome, Safari, and Opera */
    // width: 0px;
    height: 0px;
  }

  .slider {
    // flex-grow: 1;
    flex-shrink: 0;
    user-select: none;
  }
}
</style>