<template>
  <div
    class="sv-previewer-wrapper"
    v-if="visible"
    ref="rootRef"
    @touchstart="handleTouchStart"
    @touchmove="handleTouchMove"
    @touchend="handleTouchEnd"
    v-event:doubleTap="handleDoubleTap"
    v-event:singleTap="close"
    v-event:pinchStart="handlePinchStart"
    v-event:pinchMove="handlePinchMove"
    v-event:pinchEnd="handlePinchEnd">
    <div class="sv-previewer-container" ref="containerRef">
      <template v-for="(image, $index) in data">
        <section class="sv-previewer-section" :style="sectionStyle">
          <img
            v-if="zoomImageIndex === $index"
            class="image"
            :src="image"
            :style="[imagesInitStyle[$index], imageZoomStyleByCompute]"
            ref="imageRef"
          />
          <img
            v-else
            class="image"
            :src="image"
            :style="imagesInitStyle[$index]"
            ref="imageRef"
          />
        </section>
      </template>
    </div>

    <div class="sv-previewer-indicator" :class="{'bottom': indicatorPosition === 'bottom', 'top': indicatorPosition === 'top'}">
      <span class="current">
      {{currentIndex + 1}}
      </span>
      <span class="total">
        /{{data.length}}
      </span>
    </div>
  </div>
</template>

<script type="text/ecmascript-6">
  export default {
    name: 'svPreviewer',
    data () {
      return {
        aaa: 0,
        visible: this.value,
        touchHandler: {
          startX: 0,
          currentX: 0,
          endX: 0,
          translateX: 0,
          threshold: 100, // 阈值
          startTime: 0,
          currentTime: 0,
          endTime: 0,
          hasOneTap: false // 是否有过一次点击
        },
        currentIndex: 0,
        clientWidth: document.documentElement.clientWidth,
        clientHeight: document.documentElement.clientHeight,
        isAllowSwitch: true, // 是否允许切换到上一张或者下一张
        hasZoomInImage: false, // 图片是否被放大
        isAllowMove: true, // 是否允许移动 被放大的图片移动到边缘时，不能再移动
        zoomImageIndex: 0, // 被放大的图片的索引
        fingerNum: 1, // 手指个数，判断执行单指事件还是双指事件
        sectionStyle: {
          width: `${document.documentElement.clientWidth}px`
        },
        zoomHandler: {
          startX: 0,
          currentX: 0,
          endX: 0,
          startY: 0,
          currentY: 0,
          endY: 0,
          transformOrigin: {
            x: 'center',
            y: 'center'
          },
          zoomRatio: 1
        },
        pinchHandler: {
          startTouches: [],
          moveTouches: [],
          endTouches: [],
          prevImageWidth: 0,
          prevImageHeight: 0,
          currentImageWidth: 0,
          currentImageHeight: 0
        },
        imagesInitStyle: [],
        imageZoomStyle: {
          translateX: 0,
          translateY: 0,
          scale: 1,
          duration: 0
        }
      }
    },
    props: {
      value: {
        type: Boolean,
        default: false
      },
      data: {
        type: Array,
        default: []
      },
      indicatorPosition: {
        type: String,
        default: 'bottom'
      }
    },
    watch: {
      visible (val) {
        this.$emit('input', val)
      },
      value (val) {
        this.visible = val
        !val && this.reset()

        if (val) {
          this.$nextTick(() => {
            this.computeImageSize()
          })
        }
      },
      currentIndex () {
        this.resetZoomImage()
      }
    },
    computed: {
      imageZoomStyleByCompute () {
        let style = this.imageZoomStyle

        let styleByCompute = {
          transform: `translate3d(${style.translateX}px, ${style.translateY}px, 0) scale(${style.scale})`,
          transition: `transform ${style.duration}s linear`,
          transformOrigin: `${this.zoomHandler.transformOrigin.x} ${this.zoomHandler.transformOrigin.y}`
        }
        return styleByCompute
      }
    },
    methods: {
      _getTime () {
        return Date.now()
      },
      handleTouchStart (event) {
        this.fingerNum = event.touches.length

        if (this.fingerNum >= 2) {
          return
        }
        this.touchHandler.startTime = this._getTime()
        this.touchHandler.startX = event.touches[0].pageX

        if (this.hasZoomInImage) { // 如果当前的图片是放大状态，则移动图片
          this.zoomHandler.startX = event.touches[0].pageX
          this.zoomHandler.startY = event.touches[0].pageY
        }
      },
      handleTouchMove (event) {
        this.fingerNum = event.touches.length

        if (this.fingerNum >= 2) {
          return
        }
        // 如果有被放大的图片，则移动时需要判断用户是在移动被放大的图片，还是在切换上一张或者下一张
//        let duration = 0.3
        if (this.hasZoomInImage) {
          this.isAllowSwitch = false
          this.zoomHandler.currentX = event.touches[0].pageX
          this.zoomHandler.currentY = event.touches[0].pageY

          let imageClientRect = event.target.getBoundingClientRect()

          let moveDistanceX = Math.abs(this.zoomHandler.currentX - this.zoomHandler.startX)
          let moveDistanceY = Math.abs(this.zoomHandler.currentY - this.zoomHandler.startY)

          let translateX = this.imageZoomStyle.translateX
          let translateY = this.imageZoomStyle.translateY

          if (moveDistanceX > moveDistanceY) { // x轴的位移大于y轴位移时，判定为左右移动
            if (imageClientRect.width < this.clientWidth) { // 如果放大后的图片宽度小于屏幕的宽度，不允许左右滑动，而是打开可以上下切换的开关
              this.isAllowSwitch = true
            } else {
              translateX += (this.zoomHandler.currentX - this.zoomHandler.startX) / 10 // 除以10的目的是降低灵敏度

              if (imageClientRect.left >= 0) { // 已经到达左边缘
                if (this.zoomHandler.currentX > this.zoomHandler.startX) { // 屏蔽继续向左滑动，开启上下切换开关
                  translateX = (imageClientRect.width - this.clientWidth) / 2
                  this.isAllowSwitch = true
                }
              }

              if (imageClientRect.right <= this.clientWidth) { // 已经到达右边缘
                if (this.zoomHandler.currentX < this.zoomHandler.startX) { // 屏蔽继续向右滑动，开启上下切换开关
                  translateX = -(imageClientRect.width - this.clientWidth) / 2
                  this.isAllowSwitch = true
                }
              }
            }
          }

          // y轴的位移大于x轴位移时，判定为上下移动
          if (moveDistanceX < moveDistanceY) {
            if (imageClientRect.height < this.clientHeight) { // 如果放大后的图片高度小于屏幕的高度，则不允许上下移动，而是打开
              console.log('什么都不干')
            } else {
              translateY += (this.zoomHandler.currentY - this.zoomHandler.startY) / 10 // 除以10的目的是降低灵敏度

              if (imageClientRect.top >= 0) { // 已经到达上边缘
                if (this.zoomHandler.currentY > this.zoomHandler.startY) { // 屏蔽继续向下滑动
//                  this.isAllowMove = false
                  translateY = (imageClientRect.height - this.clientHeight) / 2
                  console.log(imageClientRect.height, translateY)
                }
              }

              if (imageClientRect.bottom <= this.clientHeight) { // 已经到达下边缘
                if (this.zoomHandler.currentY < this.zoomHandler.startY) { // 屏蔽继续向上滑动
                  translateY = -(imageClientRect.height - this.clientHeight) / 2
                }
              }
            }
          }

          if (this.isAllowMove) {
            this.moveImage(translateX, translateY)
          }
        }
        if (this.isAllowSwitch) {
          let currentX = event.touches[0].pageX
          let currentTime = new Date().getTime()
          let {startX} = this.touchHandler

          if (currentX < this.touchHandler.startX) { // 向左滑动
            if (this.currentIndex < this.data.length - 1) {
              // 计算当前激活的图片index
              this.currentIndex = Math.floor(Math.abs(this.touchHandler.translateX) / this.clientWidth)
              this.touchHandler.translateX += (currentX - startX) / 10
            } else {
              this.currentIndex = this.data.length - 1
              this.touchHandler.translateX += (currentX - startX) / ((currentTime - this.touchHandler.startTime) / 10)
            }
          } else { // 向右滑动
            if (this.touchHandler.translateX < 0) {
              // 计算当前激活的图片index
              this.currentIndex = Math.ceil(Math.abs(this.touchHandler.translateX) / this.clientWidth)

              this.touchHandler.translateX += (currentX - startX) / 10
            } else {
              this.currentIndex = 0
              this.touchHandler.translateX += (currentX - startX) / ((currentTime - this.touchHandler.startTime) / 10)
            }
          }

          this.slide(this.touchHandler.translateX)
        }
      },
      handleTouchEnd (event) {
        if (this.fingerNum >= 2) {
          return
        }

        if (!this.hasZoomInImage) { // 没有被放大的图片时，一律允许切换
          this.isAllowSwitch = true
        }
        if (!this.isAllowSwitch) return
        let touch = event.changedTouches[0]

        let endX = touch.pageX
        let slideX = endX - this.touchHandler.startX
        let duration = 0

        let zoomImageIndex
        if (endX < this.touchHandler.startX) { // 向左滑动
          if (!this.hasZoomInImage) { // 没有被放大的图片时，一律允许切换
            this.isAllowSwitch = true
          }
          if (!this.isAllowSwitch) return

          if (Math.abs(slideX) > this.touchHandler.threshold) {
            zoomImageIndex = this.currentIndex
            this.currentIndex = this.currentIndex < this.data.length - 1 ? ++this.currentIndex : this.data.length - 1
            duration = 0.3
          }
        } else { // 向右滑动
          if (Math.abs(slideX) > this.touchHandler.threshold) {
            zoomImageIndex = this.currentIndex
            this.currentIndex = this.currentIndex > 0 ? --this.currentIndex : 0
            duration = 0.3
          }
        }

        if (this.zoomImageIndex !== this.currentIndex && this.hasZoomInImage) {
          let zoomOutTimeout = window.setTimeout(() => {
            this.zoomImage(1, 0.1, zoomImageIndex)
            window.clearTimeout(zoomOutTimeout)
          }, 500)
        }
        this.touchHandler.translateX = -this.currentIndex * this.clientWidth
        this.slide(this.touchHandler.translateX, duration)
      },
      slide (distance, duration = 0.2, timing = 'linear') {
        this.$refs.containerRef.style.transform = `translate3d(${distance}px, 0, 0)`
        if (duration > 0) {
          this.$refs.containerRef.style.transition = `transform ${duration}s ${timing}`
        }
      },
      // 缩放图片
      zoomImage (zoomRatio, duration = 0.1, zoomImageIndex) {
        this.zoomImageIndex = zoomImageIndex || this.currentIndex
        this.imageZoomStyle.duration = duration

        if (zoomRatio > 1) {
          this.hasZoomInImage = true
          this.imageZoomStyle.scale = zoomRatio
        } else {
          this.hasZoomInImage = false
          this.imageZoomStyle.scale = zoomRatio
          this.imageZoomStyle.translateX = this.imageZoomStyle.translateY = 0

          this.resetZoomImage()
        }
      },
      resetZoomImage () {
        this.zoomHandler.startX = 0
        this.zoomHandler.currentX = 0
        this.zoomHandler.endX = 0
        this.zoomHandler.startY = 0
        this.zoomHandler.currentY = 0
        this.zoomHandler.endY = 0
        this.imageZoomStyle.translateX = 0
        this.imageZoomStyle.translateY = 0

        this.pinchHandler.prevImageWidth = this.pinchHandler.prevImageHeight = this.pinchHandler.currentImageWidth = this.pinchHandler.currentImageHeight = 0
      },
      moveImage (translateX, translateY, duration = 0) {
        this.imageZoomStyle.translateX = translateX
        this.imageZoomStyle.translateY = translateY
        this.imageZoomStyle.duration = duration
      },
      /**
       * 计算图片的尺寸
       * 主要为width和height
       * 通过图片的宽高比判断图片为横图还是竖图还是方图
       * width/height > 1 横图
       * width/height < 1 竖图
       * width/height = 1 方图
       */
      computeImageSize () {
        let image = null
        let imageInitStyle = null
        this.$refs.imageRef.forEach(($image, index) => {
          image = new Image()
          /* eslint-disable func-call-spacing */
          image.src = $image.src;
          /* eslint-enable  func-call-spacing */
          /* eslint-disable no-unexpected-multiline */
          ((image, index) => {
            image.onload = () => {
              imageInitStyle = null
              if (image.width < this.clientWidth && image.height < this.clientHeight) {
                imageInitStyle = null
              } else {
                if (image.width / image.height > 1) {
                  imageInitStyle = {
                    width: '100%',
                    maxHeight: '100%'
                  }
                } else if (image.width / image.height < 1) {
                  imageInitStyle = {
                    maxWidth: '100%',
                    height: '100%'
                  }
                }
              }

              this.$set(this.imagesInitStyle, index, imageInitStyle)
            }
          })(image, index)
          /* eslint-enable  no-unexpected-multiline */
        })
      },
      close () {
        this.visible = false
      },
      reset () {
        this.touchHandler.startX = 0
        this.touchHandler.currentX = 0
        this.touchHandler.endX = 0
        this.touchHandler.translateX = 0

        this.currentIndex = 0
      },
      handleDoubleTap (event) {
        let touch = event.changedTouches[0]

        this.setTransformOriginByPoint(touch.pageX, touch.pageY)
        this.zoomImage(this.hasZoomInImage ? 1 : 2)
      },
      setTransformOriginByPoint (x, y) {
        let $image = this.$refs.imageRef[this.currentIndex]
        let imageClientRect = $image.getBoundingClientRect()

        let originX, originY
        if (x < imageClientRect.left) {
          originX = 0
        } else if (x > imageClientRect.left + imageClientRect.width) {
          originX = $image.offsetWidth
        } else {
          originX = x - ($image.offsetLeft - this.clientWidth * this.currentIndex)
        }

        if (y < imageClientRect.top) {
          originY = 0
        } else if (y > imageClientRect.top + imageClientRect.height) {
          originY = $image.offsetHeight
        } else {
          originY = y - $image.offsetTop
        }

        this.zoomHandler.transformOrigin.x = typeof originX === 'number' ? originX + 'px' : originX
        this.zoomHandler.transformOrigin.y = typeof originY === 'number' ? originY + 'px' : originY
      },
      handlePinchStart (event) {
        let $image = this.$refs.imageRef[this.currentIndex]
        let imageClientRect = $image.getBoundingClientRect()
        this.pinchHandler.prevImageWidth = this.pinchHandler.prevImageWidth || imageClientRect.width
        this.pinchHandler.prevImageHeight = this.pinchHandler.prevImageHeight || imageClientRect.height
        this.pinchHandler.startTouches = event.touches
      },
      handlePinchMove (event) {
        let $image = this.$refs.imageRef[this.currentIndex]
        let imageClientRect = $image.getBoundingClientRect()
        this.pinchHandler.moveTouches = event.touches

        let startTouches = this.pinchHandler.startTouches
        let moveTouches = this.pinchHandler.moveTouches

//        let startPointDistance = this.getDistance(startTouches[0].clientX, startTouches[0].clientY, startTouches[1].clientX, startTouches[1].clientY)
//        let movePointDistance = this.getDistance(moveTouches[0].clientX, moveTouches[0].clientY, moveTouches[1].clientX, moveTouches[1].clientY)

        let startDistanceX = Math.abs(startTouches[0].clientX - startTouches[1].clientX)
        let currentDistanceX = Math.abs(moveTouches[0].clientX - moveTouches[1].clientX)
        let startDistanceY = Math.abs(startTouches[0].clientY - startTouches[1].clientY)
        let currentDistanceY = Math.abs(moveTouches[0].clientY - moveTouches[1].clientY)

        let scaleX
        let scaleY

        scaleX = (this.pinchHandler.prevImageWidth + (currentDistanceX - startDistanceX) * (this.pinchHandler.prevImageWidth * 0.01)) / imageClientRect.width
        scaleY = (this.pinchHandler.prevImageHeight + (currentDistanceY - startDistanceY) * (this.pinchHandler.prevImageHeight * 0.01)) / imageClientRect.height

        window.setTimeout(() => {
          this.setTransformOriginByPoint((startTouches[0].clientX + startTouches[1].clientX) / 2, (startTouches[0].clientY + startTouches[1].clientY) / 2)

          this.aaa = this.pinchHandler.prevImageHeight
          let zoomRatio = Math.max(scaleX, scaleY).toFixed(2)

          if (zoomRatio < 0.8) {
            zoomRatio = 0.6
            this.zoomImage(zoomRatio, 0.6)
          } else if (zoomRatio > 7) {
            zoomRatio = 9
            this.zoomImage(zoomRatio, 0.6)
          } else {
            this.zoomImage(zoomRatio, 0.2)
          }
          this.zoomHandler.zoomRatio = zoomRatio
          this.pinchHandler.currentImageWidth = zoomRatio * imageClientRect.width
          this.pinchHandler.currentImageHeight = zoomRatio * imageClientRect.height
        })
      },
      handlePinchEnd (event) {
        let $image = this.$refs.imageRef[this.currentIndex]
        let imageClientRect = $image.getBoundingClientRect()

        if (this.zoomHandler.zoomRatio < 1) {
          this.zoomHandler.zoomRatio = 1
          window.setTimeout(() => {
            this.zoomImage(1, 0.2)
          }, 200)
        } else if (this.zoomHandler.zoomRatio > 7) {
          this.zoomHandler.zoomRatio = 7
          window.setTimeout(() => {
            this.zoomImage(7, 0.2)
          }, 200)
        }

        this.pinchHandler.prevImageWidth = this.pinchHandler.currentImageWidth = this.zoomHandler.zoomRatio * imageClientRect.width
        this.pinchHandler.prevImageHeight = this.pinchHandler.currentImageHeight = this.zoomHandler.zoomRatio * imageClientRect.height
      },
      getDistance (x1, y1, x2, y2) {
        return Math.sqrt((Math.abs(x2 - x1) * Math.abs(x2 - x1)) + (Math.abs(y2 - y1) * Math.abs(y2 - y1)))
      }
    },
    created () {
      this.value && (this.visible = true)
      document.querySelector('body').addEventListener('contextmenu', () => {
        window.event.returnValue = false
        return false
      })
    }
  }
</script>

<style lang="scss" rel="stylesheet/scss">
  @import "../../styles/base";
  .sv-previewer-wrapper{
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 999;
    background: rgba(0, 0, 0, .9);
    display: flex;
    align-items: center;
    .sv-previewer-container{
      display: flex;
      flex-flow: nowrap;
      height: 100%;
      animation: fade-in-out .5s linear;
      @keyframes fade-in-out {
        from {
          transform: translate(0, -200px);
          opacity: 0;
        }
        to {
          transform: translate(0, 0);
          opacity: 1;
        }
      }
      .sv-previewer-section{
        display: flex;
        justify-content: center;
        align-items: center;
        overflow: hidden;
        .image{
        }
      }
    }
    .sv-previewer-indicator{
      display: flex;
      align-items: center;
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      color: $color-light;
      &.bottom{
        bottom: pxToRem(80);
      }
      &.top{
        top: pxToRem(80);
      }
      .current{
        font-size: pxToRem(50);
      }
      .total{
        font-size: pxToRem(40);
      }
    }
  }
</style>
