<template>
  <div>
    <div id="imgPreviewContainer" ref="imgPreviewContainer"><slot></slot></div>
    <div ref="imgPreviewMask">
      <Transition name="fade">
        <div v-if="showPreview" class="image_preview_mask"></div>
      </Transition>
      <Transition name="fade">
        <div class="image_preview_wrap" v-if="showPreview">
          <div
            class="image_preview"
            ref="imagePreview"
            @click.stop="handleOperation('close')"
          >
            <img
              :class="imgClasses"
              :style="imageStyle"
              :src="currentSrc"
              @click.stop
              @load="setImgScale"
              @error="imgLoadError"
              @mousedown.stop.prevent="handleMousedown"
            />
            <div class="image_preview_operations">
              <div class="img_name">
                {{ currentImgName }}
                <span v-if="originalSize.width && originalSize.height"
                  >（{{ originalSize.width }} *
                  {{ originalSize.height }}）</span
                >
              </div>
              <div class="operations_btn_box">
                <div class="operations_btn_main">
                  <div title="放大" @click.stop="handleOperation('zoomIn')">
                    <svg
                      class="operations_btn_item"
                      viewBox="0 0 1024 1024"
                      version="1.1"
                      xmlns="http://www.w3.org/2000/svg"
                      p-id="7197"
                      width="200"
                      height="200"
                    >
                      <path
                        d="M637 443H519V309c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v134H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h118v134c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V519h118c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
                        p-id="7198"
                        fill="#ffffff"
                      ></path>
                      <path
                        d="M921 867L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
                        p-id="7199"
                        fill="#ffffff"
                      ></path>
                    </svg>
                  </div>
                  <div title="缩小" @click.stop="handleOperation('zoomOut')">
                    <svg
                      class="operations_btn_item"
                      viewBox="0 0 1024 1024"
                      version="1.1"
                      xmlns="http://www.w3.org/2000/svg"
                      p-id="7412"
                      width="200"
                      height="200"
                    >
                      <path
                        d="M637 443H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h312c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
                        p-id="7413"
                        fill="#ffffff"
                      ></path>
                      <path
                        d="M921 867L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
                        p-id="7414"
                        fill="#ffffff"
                      ></path>
                    </svg>
                  </div>
                  <div
                    title="向左旋转"
                    @click.stop="handleOperation('rotateLeft')"
                  >
                    <svg
                      class="operations_btn_item"
                      viewBox="0 0 1024 1024"
                      version="1.1"
                      xmlns="http://www.w3.org/2000/svg"
                      p-id="13308"
                      width="200"
                      height="200"
                    >
                      <path
                        d="M672 418H144c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H188V494h440v326z m191.3-491.5c-78.8-100.7-196-153.6-314.6-154.2l-0.2-64c0-6.5-7.6-10.1-12.6-6.1l-128 101c-4 3.1-3.9 9.1 0 12.3L492 318.6c5.1 4 12.7 0.4 12.6-6.1v-63.9c12.9 0.1 25.9 0.9 38.8 2.5 42.1 5.2 82.1 18.2 119 38.7 38.1 21.2 71.2 49.7 98.4 84.3 27.1 34.7 46.7 73.7 58.1 115.8 11 40.7 14 82.7 8.9 124.8-0.7 5.4-1.4 10.8-2.4 16.1h74.9c14.8-103.6-11.3-213-81-302.3z"
                        p-id="13309"
                        fill="#ffffff"
                      ></path>
                    </svg>
                  </div>
                  <div
                    title="向右旋转"
                    @click.stop="handleOperation('rotateRight')"
                  >
                    <svg
                      class="operations_btn_item"
                      viewBox="0 0 1024 1024"
                      version="1.1"
                      xmlns="http://www.w3.org/2000/svg"
                      p-id="13521"
                      width="200"
                      height="200"
                    >
                      <path
                        d="M480.5 251.2c13-1.6 25.9-2.4 38.8-2.5v63.9c0 6.5 7.5 10.1 12.6 6.1L660 217.6c4-3.2 4-9.2 0-12.3l-128-101c-5.1-4-12.6-0.4-12.6 6.1l-0.2 64c-118.6 0.5-235.8 53.4-314.6 154.2-69.6 89.2-95.7 198.6-81.1 302.4h74.9c-0.9-5.3-1.7-10.7-2.4-16.1-5.1-42.1-2.1-84.1 8.9-124.8 11.4-42.2 31-81.1 58.1-115.8 27.2-34.7 60.3-63.2 98.4-84.3 37-20.6 76.9-33.6 119.1-38.8zM880 418H352c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H396V494h440v326z"
                        p-id="13522"
                        fill="#ffffff"
                      ></path>
                    </svg>
                  </div>
                  <div
                    v-if="images && images.length > 1"
                    title="上一张"
                    @click.stop="handleOperation('previous')"
                  >
                    <Icon class="operations_btn_item" type="ios-arrow-back" />
                  </div>
                  <div
                    v-if="images && images.length > 1"
                    title="下一张"
                    @click.stop="handleOperation('next')"
                  >
                    <Icon
                      class="operations_btn_item"
                      type="ios-arrow-forward"
                    />
                  </div>
                  <div title="关闭" @click.stop="handleOperation('close')">
                    <Icon class="operations_btn_item" type="md-close" />
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </Transition>
    </div>
  </div>
</template>

<script>
import KeyCode from "@/utils/keyCode";
import { on, off } from "@/utils/dom";
export default {
  props: {
    images: {
      type: Array,
      default: function () {
        return [];
      }
    }
  },
  data() {
    return {
      // 图片原始尺寸
      originalSize: {
        width: 0,
        height: 0
      },
      isAddScale: false,
      scale: 1,
      degree: 0,
      translate: { x: 0, y: 0 },
      startX: 0,
      startY: 0,
      transition: true,
      showPreview: false,
      currentSrc: "",
      previewIndex: 0,
      changeTimer: null
    };
  },
  computed: {
    currentImgName() {
      let _url = this.currentSrc;
      if (!_url || _url.startsWith("data:image/")) {
        return "";
      }
      return _url.slice(_url.lastIndexOf("/") + 1);
    },
    imgClasses() {
      return [
        "image_preview_image",
        {
          ["image_preview_transition"]: this.transition
        }
      ];
    },
    imageStyle() {
      let translateX = this.translate.x / this.scale;
      let translateY = this.translate.y / this.scale;
      const mod = this.degree % 360;
      if ([90, -270].includes(mod)) {
        [translateX, translateY] = [translateY, -translateX];
      }
      if ([180, -180].includes(mod)) {
        [translateX, translateY] = [-translateX, -translateY];
      }
      if ([270, -90].includes(mod)) {
        [translateX, translateY] = [-translateY, translateX];
      }
      return {
        transform: `scale(${this.scale}) rotate(${this.degree}deg) translate(${translateX}px, ${translateY}px)`
      };
    }
  },
  mounted() {
    on(document, "keydown", this.handleKeydown);
    on(document, "keyup", this.handleKeyup);
    on(document, "wheel", this.handleWheel);
    on(this.$refs.imgPreviewContainer, "click", this.containerImgsClick);
    this.$nextTick(() => {
      const body = document.querySelector("body");
      if (body.append) {
        body.append(this.$refs.imgPreviewMask);
      } else {
        body.appendChild(this.$refs.imgPreviewMask);
      }
    });
  },
  beforeDestroy() {
    off(document, "keydown", this.handleKeydown);
    off(document, "keyup", this.handleKeyup);
    off(document, "wheel", this.handleWheel);
    off(this.$refs.imgPreviewContainer, "click", this.containerImgsClick);
    if (this.changeTimer) {
      clearTimeout(this.changeTimer);
      this.changeTimer = null;
    }
  },
  methods: {
    // 图片加载失败
    imgLoadError() {
      this.originalSize.width = 0;
      this.originalSize.height = 0;
    },
    // 图片加载完成
    setImgScale(event) {
      let imgEl = event.target;
      let imgWith = imgEl.offsetWidth;
      this.originalSize.width = imgWith;
      let imgHeight = imgEl.offsetHeight;
      this.originalSize.height = imgHeight;
      let _maskW = this.$refs.imagePreview.offsetWidth;
      let _maskH = this.$refs.imagePreview.offsetHeight;
      let _wScale = 1;
      let _hScale = 1;
      if (imgWith > _maskW) {
        _wScale = _maskW / imgWith;
      }
      if (imgHeight > _maskH) {
        _hScale = _maskH / imgHeight;
      }
      let scale = _wScale > _hScale ? _hScale : _wScale;
      if (this.changeTimer) {
        clearTimeout(this.changeTimer);
        this.changeTimer = null;
      }
      this.changeTimer = setTimeout(() => {
        this.resetStyle(scale);
      }, 100);
    },
    containerImgsClick(event) {
      this.$refs.imgPreviewContainer
        .getElementsByTagName("img")
        .forEach((imgDom, imgIndex) => {
          imgDom.setAttribute("preview_index", imgIndex);
        });
      let previewIndex = event.target.getAttribute("preview_index");
      let selectUrl = event.target.src;
      if (selectUrl) {
        selectUrl = decodeURI(selectUrl);
        if (this.images.includes(selectUrl)) {
          this.currentSrc = selectUrl;
          this.previewIndex = Number(previewIndex);
          this.showPreview = true;
        }
      }
      event.stopPropagation();
    },
    resetStyle(scale = 1) {
      this.scale = scale;
      // 就近纠正，防止切换图片后旋转多圈
      let _degree = this.degree % 360;
      if (_degree > 0) {
        if (_degree >= 180) {
          this.degree = this.degree + 360 - _degree;
        } else {
          this.degree = this.degree - _degree;
        }
      } else if (_degree < 0) {
        if (_degree >= -180) {
          this.degree = this.degree - (360 + _degree);
        } else {
          this.degree = this.degree - _degree;
        }
      }
      this.translate.x = 0;
      this.translate.y = 0;
    },
    handleOperation(operationType) {
      if (operationType === "zoomIn" && this.scale < 6) this.scale += 0.25;
      if (operationType === "zoomOut" && this.scale > 0.25) this.scale -= 0.25;
      if (operationType === "rotateLeft") this.degree -= 90;
      if (operationType === "rotateRight") this.degree += 90;
      if (operationType === "previous") {
        if (this.previewIndex === 0) {
          this.previewIndex = this.images.length - 1;
        } else {
          this.previewIndex -= 1;
        }
        this.currentSrc = this.images[this.previewIndex];
        if (this.isAddScale) {
          this.scale += 0.02;
        } else {
          this.scale -= 0.02;
        }
        this.isAddScale = !this.isAddScale;
      }
      if (operationType === "next") {
        if (this.previewIndex + 1 === this.images.length) {
          this.previewIndex = 0;
        } else {
          this.previewIndex += 1;
        }
        this.currentSrc = this.images[this.previewIndex];
        if (this.isAddScale) {
          this.scale += 0.02;
        } else {
          this.scale -= 0.02;
        }
        this.isAddScale = !this.isAddScale;
      }
      if (operationType === "close") {
        this.showPreview = false;
        this.resetStyle();
      }
    },
    handleMousedown(event) {
      const { pageX, pageY, which } = event;
      if (which !== 1) return;
      this.startX = pageX;
      this.startY = pageY;
      this.transition = false;
      on(document, "mousemove", this.handleMousemove);
      on(document, "mouseup", this.handleMouseup);
    },
    handleMousemove(event) {
      event.stopPropagation();
      const { pageX, pageY } = event;
      this.translate.x += pageX - this.startX;
      this.translate.y += pageY - this.startY;
      this.startX = pageX;
      this.startY = pageY;
    },
    handleMouseup() {
      this.transition = true;
      off(document, "mousemove", this.handleMousemove);
      off(document, "mouseup", this.handleMouseup);
    },
    handleKeydown(event) {
      if (!this.showPreview) return;
      const { keyCode } = event;
      if (keyCode === KeyCode.LEFT) this.handleOperation("previous");
      if (keyCode === KeyCode.RIGHT) this.handleOperation("next");
      if (keyCode === KeyCode.UP) this.handleOperation("zoomIn");
      if (keyCode === KeyCode.DOWN) this.handleOperation("zoomOut");
    },
    handleKeyup(event) {
      if (!this.showPreview) return;
      const { keyCode } = event;
      if (keyCode === KeyCode.ESC) this.handleOperation("close");
    },
    handleWheel(event) {
      if (!this.showPreview) return;
      const { deltaY } = event;
      this.handleOperation(deltaY < 0 ? "zoomIn" : "zoomOut");
    }
  }
};
</script>

<style scoped lang="less">
#imgPreviewContainer img {
  cursor: pointer;
}
.image_preview_mask {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: rgba(55, 55, 55, 0.6);
  height: 100%;
  z-index: 9999;
}
.image_preview_wrap {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 9999;
  user-select: none;
  overflow: hidden;
  .image_preview {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    .image_preview_image {
      cursor: grab;
    }
    .image_preview_transition {
      transition: transform 0.3s ease;
    }
    .image_preview_operations {
      position: absolute;
      left: 0;
      right: 0;
      bottom: 30px;
      margin: 0 auto;
      text-align: center;
      .img_name {
        margin-bottom: 10px;
        font-size: 12px;
        color: #ccc;
        line-height: 20px;
        white-space: nowrap;
      }
      .operations_btn_box {
        display: flex;
        justify-content: center;
        align-items: center;
        .operations_btn_main {
          display: flex;
          justify-content: center;
          align-items: center;
          background: rgba(55, 55, 55, 0.4);
          border-radius: 4px;
          overflow: hidden;
          .operations_btn_item {
            margin: 0 6px;
            padding: 4px 8px;
            width: 36px;
            height: 40px;
            line-height: 32px;
            font-size: 18px;
            color: #fff;
            text-align: center;
            cursor: pointer;
            transition: opacity 0.1s ease-in-out;
          }
        }
      }
    }
  }
}
</style>
