<template>
  <div class="scroll-select-list" ref="container">
    <div class="scroll-select-list__content" ref="content">
      <div v-for="item in list" class="scroll-select-list__item" :key="item[0]">{{ item[1] }}</div>
    </div>
    <div class="scroll-select-list__mask"></div>
    <div class="scroll-select-list__indicator"></div>
  </div>
</template>

<script>
/* global Scroller, scroller_render */
/**
 * 通过页面注入方式引入依赖模块和数据，包括：
 * - Scroller
 * - scroller_render
 */
if (!window.Scroller || !window.scroller_render) {
  throw new Error('组件 SelectPopup 无法初始化，依赖 Scroller、scroller_render！')
}

const ITEM_HEIGHT = 34 // 项目高度 34px

function initScroller(container, content, onchange) {
  const rect = container.getBoundingClientRect()
  const scroller = new Scroller(scroller_render(content), {
    scrollingX: false,
    snapping: true,
    scrollingComplete: debounce(onScrollingComplete, 100)// 异步处理更精准些，滚动完成后可能还没有到位
  })
  scroller.setSnapSize(ITEM_HEIGHT, ITEM_HEIGHT)
  scroller.setPosition(rect.left + container.clientLeft, rect.top + container.clientTop)
  scroller.setDimensions(container.clientWidth, container.clientHeight, content.offsetWidth, content.offsetHeight)

  // 新增方法

  scroller.$update = debounce((itemIndex) => {
    // 每次更新时 content 高度可能改变，重新获取
    scroller.setPosition(rect.left + container.clientLeft, rect.top + container.clientTop)
    scroller.setDimensions(container.clientWidth, container.clientHeight, content.offsetWidth, content.offsetHeight)
    // 更新位置，确保数据更新后位置同步，使用 Scroller 私有方法直接移动到位，避免再次触发动画效果
    scroller.__publish(0, itemIndex * ITEM_HEIGHT, 1)
  })

  scroller.$destroy = () => {
    scroller.__callback = null // 移除 render 回调引用
    container.removeEventListener('touchstart', touchstartHandler)
    container.removeEventListener('touchmove', touchmoveHandler)
    container.removeEventListener('touchend', touchendHandler)
  }

  container.addEventListener('touchstart', touchstartHandler, false)
  container.addEventListener('touchmove', touchmoveHandler, false)
  container.addEventListener('touchend', touchendHandler, false)

  function touchstartHandler(e) {
    scroller.doTouchStart(e.touches, e.timeStamp)
    e.preventDefault()
  }

  function touchmoveHandler(e) {
    scroller.doTouchMove(e.touches, e.timeStamp)
  }

  function touchendHandler(e) {
    scroller.doTouchEnd(e.timeStamp)
  }

  function onScrollingComplete() {
    onchange( Math.floor(scroller.__scrollTop / ITEM_HEIGHT) )
  }

  return scroller
}

export default {
  props: {
    list: Array, // [[key, value], [key, value], ...]
    value: Number // 选中项的索引号
  },
  data() {
    return {
      selected: this.value || 0 // 选中项的索引序号
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.scroller = initScroller(this.$refs.container, this.$refs.content, (i) => this.onChange(i))
      this.update()
    })
  },
  updated() {
    this.update()
  },
  beforeDestroy() {
    this.scroller && this.scroller.$destroy()
  },
  watch: {
    value(index) {
      if (this.selected === index) return
      this.selected = index
      this.update()
    },
    list() {
      this.update()
    }
  },
  methods: {
    onChange(index) {
      const list = this.list
      // 尽量匹配到选中项
      if (index < 0) index = 0
      else if (index >= list.length) index = list.length - 1
      this.selected = index
      this.$emit('input', index)
    },
    update() {
      this.$nextTick(() => this.scroller.$update(this.selected))
    }
  }
}

function debounce(func, wait = 0) {
  let timer = null
  let args = null
  // 注意：这里不要用 () => {}，否则 arguments 对应的是 debounce 函数，而非新生成的函数
  return function () {
    args = [].slice.call(arguments, 0)
    if (timer) return
    timer = setTimeout(() => {
      timer = null
      func(...args)
    }, wait)
  }
}
</script>

<style lang="less">
// 固定高度的列表，如果需要调整高度，mask 等样式也需要进行调整
@LIST_HEIGHT: 240px;
@ITEM_HEIGHT: 34px;
@MASK_TOP: (@LIST_HEIGHT - @ITEM_HEIGHT) / 2;

.scroll-select-list {
  position: relative;
  height: @LIST_HEIGHT;
  overflow: hidden;

  &__content {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    padding: @MASK_TOP 0; /* 上下留出空间可以滚动 */
  }
  &__mask {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-image: linear-gradient(to bottom, rgba(255,255,255,0.95), rgba(255,255,255,0.6)), linear-gradient(to top, rgba(255,255,255,0.95), rgba(255,255,255,0.6));
    background-position: top, bottom;
    background-size: 100% @MASK_TOP;
    background-repeat: no-repeat;
    transform: translateZ(0px);
  }
  &__indicator {
    position: absolute;
    left: 0;
    top: @MASK_TOP;
    width: 100%;
    height: @ITEM_HEIGHT;
    background-image: linear-gradient(to bottom, #d0d0d0, #d0d0d0, transparent, transparent), linear-gradient(to top, #d0d0d0, #d0d0d0, transparent, transparent);
    background-position: top, bottom;
    background-size: 100% 1px;
    background-repeat: no-repeat;
  }
  &__item {
    height: @ITEM_HEIGHT;
    font-size: 16px;
    line-height: @ITEM_HEIGHT;
    text-align: center;
    color: #000;
    -webkit-line-clamp: 1;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}
</style>
