图片预览组件 (放大 缩小 旋转 鼠标拖动)

效果图

其中的图片初始化 不需要 如果需要可自行修改一下
**完整代码如下 **

点击查看代码
<template>
  <transition name="zoom">
    <div class="previewImage_wrapper" ref="previewImage_wrapper" @wheel="handleScroll">
      <div class="previewImage_image" ref="previewImage_image" @mousedown="onmousedownHandle">
        <img ref="previewImage_img" 
        :style="{
          top: top + 'px',
          left: left + 'px',
        }"
        @click="initImage(previewImgList )"
       :src="previewImgList || ''" />
      </div>
      <div class="previewImage_toolbar">
        <span class="title">{{ title }} {{ currentIndex }}</span>
        <div class="toolbar">
          <span class="previewImage_btn" @click="shrinkHandle">-</span>
          <span class="previewImage_btn" @click="largeHandle">+</span>
          <span class="previewImage_btn" @click="turnLeftHandle">↺</span>
          <span class="previewImage_btn" @click="initImgHandle">▣</span>
          <span class="previewImage_btn" @click="turnRightHandle">↻</span>
        </div>
      </div>
    </div>
  </transition>
</template>
<script>
export default {
  name: 'picture-view',
  props: {
    previewImgList: {
      // url数组
      type: String,
      default: '',
    },
    currentIndex: {
      // 当前图片索引
      type: String,
      default: 0,
    },
    title: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      imageUrl: '',
      imgW: 0,
      imgH: 0,
      top: 0,
      left: 0,
      mousewheelevt: null,
      imgHandle: {
        // 图片控制
        scale: 1,
        rotate: 0,
      },
    };
  },
  mounted() {
    this.imageUrl = this.previewImgList;
    this.initImage();
    this.mousewheelevt = /Firefox/i.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel';
    this.$refs.previewImage_image.addEventListener(this.mousewheelevt, { passive: true });
  },
  beforeDestroy() {
    this.$refs.previewImage_image.removeEventListener(this.mousewheelevt,  { passive: true });
  },
  methods: {
    /* 获取图片真实高宽 */
    getImgSize(url) {
      return new Promise((resolve, reject) => {
        let imgObj = new Image();
        imgObj.src = url;
        imgObj.onload = () => {
          resolve({ width: imgObj.width, height: imgObj.height });
        };
      });
    },

    /* 初始化图片 */ 
    async initImage() {
      if (!this.imageUrl) {
        return;
      }
      let { width, height } = await this.getImgSize(this.imageUrl);
      // let realWidth = width;
      // let realHeight = height;
      // const picBoxW = this.$refs.previewImage_image.clientWidth;
      // const picBboxH = this.$refs.previewImage_image.clientHeight;
      // const WRatio =  picBoxW / realWidth;
      // const HRatio =  picBboxH / realHeight;
      /* 横图 使用宽度比例 */ 
      // if (realWidth >= realHeight) {
      //   this.imgW =  realWidth * WRatio;
      //   this.imgH = realHeight * WRatio;
      //   this.top = (picBboxH - this.imgH) / 2;
      //   this.left = (picBoxW - this.imgW) / 2;
        
      // /* 竖图 */
      // } else {
      //   this.left = (picBoxW - this.imgW) / 2;
      //   this.imgW =  realWidth * HRatio;
      //   this.imgH = realHeight * HRatio;
      // }
    },
    onmousedownHandle(e) {
      const that = this;
      const element = this.$refs.previewImage_image;
      const imgEle = this.$refs.previewImage_img;
      element.onmousemove = function (el) {
        const ev = el || window.event;
        // 阻止默认事件
        ev.preventDefault();
        that.left += ev.movementX;
        that.top += ev.movementY;
      };
      element.onmouseup = function () {
        element.onmousemove = null;
        element.onmouseup = null;
      };
      if (e.preventDefault) {
        e.preventDefault();
      } else {
        return false;
      }
    },
    // 向左翻转
    async turnLeftHandle() {
      this.imgHandle.rotate = this.imgHandle.rotate - 90;
      await this.$nextTick();
      const element = this.$refs.previewImage_img;
      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`;
    },
    // 向右翻转
    async turnRightHandle() {
      this.imgHandle.rotate = this.imgHandle.rotate + 90;
      await this.$nextTick();
      const element = this.$refs.previewImage_img;
      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`;
    },
    // 初始化还原图片缩放旋转控制
    async initImgHandle() {
      this.imgHandle = {
        scale: 1,
        rotate: 0,
      };
      this.top = 0,
      this.left = 0,
      // this.initImage()
      await this.$nextTick();
      const element = this.$refs.previewImage_img;
      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`;
    },
    // 放大图片
    async largeHandle() {
      this.imgHandle.scale = Number((this.imgHandle.scale + 0.2).toFixed(2)); // 使用toFixed防止小数点精度不准
      const element = this.$refs.previewImage_img;
      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`;
    },
    // 缩小图片
    async shrinkHandle() {
      if (this.imgHandle.scale === 0.2) {
        // 最低缩放到0.2倍
        return;
      }
      this.imgHandle.scale = Number((this.imgHandle.scale - 0.2).toFixed(2)); // 使用toFixed防止小数点精度不准
      const element = this.$refs.previewImage_img;
      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`;
    },
    // 上一张图片
    prevImage() {
      if (this.currentIndex === 0) {
        this.currentIndex = this.previewImgList.length - 1;
      } else {
        this.currentIndex--;
      }
      this.initImgHandle();
    },
    // 下一张图片
    nextImage() {
      if (this.currentIndex === this.previewImgList.length - 1) {
        this.currentIndex = 0;
      } else {
        this.currentIndex++;
      }
      this.initImgHandle();
    },
  },
};
</script>
<style lang="scss" scoped>
.title{
color: #fff;
font-size: 24px;
font-weight: 700;
}
.previewImage_wrapper {
  width: 100%;
  height: 100%;
  .previewImage_image {
    width: 100%;
    height: 100%;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    img {
      position: absolute;
      width: 100%;
      height: 100%;
      object-fit: scale-down;
      transition: transform 0.3s ease;
    }
  }
  .previewImage_navigation {
    &_left {
      position: absolute;
      left: 15px;
      top: 50%;
      transform: translate(0, -50%);
      transition: transform 0.2s ease-out;
    }
    &_right {
      position: absolute;
      right: 15px;
      top: 50%;
      transform: translate(0, -50%);
      transition: transform 0.2s ease-out;
    }
    &_left:hover,
    &_right:hover {
      transform: translate(0, -50%) scale(1.2);
    }
  }
  .previewImage_toolbar {
    position: absolute;
    top: 0px;
    left: 50%;
    width: 100%;
    height: 50px;
    transform: translate(-50%, 0);
    display: flex;
    align-items: center;
    justify-content: space-around;
    background: rgba($color: #606266, $alpha: 0.5);
    .toolbar {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    span {
      margin-right: 10px;
      transition: transform 0.2s ease-out;
      &:hover {
        transform: scale(1.1);
      }
    }
    span:last-child {
      margin-right: 0;
    }
  }
  .previewImage_btn {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    font-weight: 700;
    color: #fff;
    border-radius: 50%;
    cursor: pointer;
    user-select: none;  //for chrome
    -moz-user-select:none;//for firefox
  }
}
.zoom-enter,
.zoom-leave-to {
  // 元素进入和离开时的动作
  transform: scale(0);
}
.zoom-enter-active,
.zoom-leave-active {
  // 元素进入和离开时的过渡动画定义
  transition: transform 0.3s;
}

.slide-enter,
.slide-leave-to {
  // 元素进入和离开时的动作
  transform: translateX(100%);
}
.slide-enter-active,
.slide-leave-active {
  // 元素进入和离开时的过渡动画定义
  transition: transform 0.3s ease-in-out;
}
</style>
posted @ 2023-08-23 15:28  宝贝熊の  阅读(79)  评论(0编辑  收藏  举报