vue 图片放大镜效果

插件名称:vue-photo-zoom-pro

https://github.com/Mater1996/vue-photo-zoom-pro

 

效果图

 

 使用:

<template>
  <div class="images">
    <img style="width: 300px;" @click.stop="showBigPicture(url)" :src="url" alt="">

    <!-- 显示大图 -->
    <Modal v-model="pictureModal" width="960" footer-hide :closable="closable" class-name="vertical-center-modal">
      <photo-zoom :url="bigPictureUrl" :bigWidth="200" :scale="2"
        overlayStyle="width: 100%;height: 600px;border-radius: 3px;"></photo-zoom>
    </Modal>
  </div>
</template>
<script>
  import PhotoZoom from '@/components/photo-zoom'
  export default {
    components: { PhotoZoom },
    data() {
      return {
        url:'http://pic1.win4000.com/wallpaper/2019-01-28/5c4ebd381fc8b.jpg',
        bigPictureUrl:'',
        pictureModal: false,
        closable: false,
      }
    },
    methods: {
      showBigPicture(url) {
        this.pictureModal = true;
        this.bigPictureUrl = url;
      },
    },
  }
</script>

代码中使用了iviewUI的Modal, 

 

组件内容:

   ├── components              
   │   ├── photo-zoom  //放大镜组件
   │   │   ├── index.vue         
   │   │   └── vue-photo-zoom-pro.vue 

index.vue:

<template>
  <div style="width: 100%;height: 100%">
    <vue-photo-zoom-pro :width="bigWidth" :url="url" :type="type" :scale="scale" :out-show="showType"
      :overlayStyle="overlayStyle">
    </vue-photo-zoom-pro>
  </div>
</template>

<script>
  import vuePhotoZoomPro from '@/components/photo-zoom/vue-photo-zoom-pro'
  export default {
    name: 'PicZoom',
    components: { vuePhotoZoomPro },
    data() {
      return {
        type: "square",
        showType: false,
      }
    },
    props: {
      url: {
        type: String,
        required: true,
        // default: require('@/assets/vehicle_img/blank_vehicle.jpg') 
      },
      bigWidth: {
        type: Number,
        required: true,
        default: 168
      },
      scale: {
        type: Number,
        required: true,
        default: 2
      },
      overlayStyle: {
        type: String,
        default: 'width:100%;height:100%'
      }
    },
  }
</script>

 

vue-photo-zoom-pro.vue:

<template>
  <div class="pic-img">
    <div class="img-container">
      <img ref="img" @load="imgLoaded" :src="url" :style="overlayStyle" @error="imgerrorfun" />
      <div class="overlay" @mousemove.stop="!moveEvent && mouseMove($event)"
        @mouseout.stop="!leaveEvent && mouseLeave($event)" :style="overlayStyle">
      </div>
      <div v-if="!hideZoom && imgLoadedFlag &&!hideSelector" :class="['img-selector', {'circle': type === 'circle'}]"
        :style="[imgSelectorStyle, imgSelectorSize, imgSelectorPosition, !outShow && imgBg, !outShow && imgBgSize, !outShow && imgBgPosition]">
        <slot></slot>
      </div>
      <div v-if="outShow" v-show="!hideOutShow" :class="['img-out-show', {'base-line': baseline}]"
        :style="[imgOutShowSize, imgOutShowPosition, imgBg, imgBgSize, imgBgPosition]">
        <div v-if="pointer" class="img-selector-point"></div>
      </div>
    </div>
  </div>

</template>

<script>
  export default {
    name: "vue-photo-zoom-pro",
    props: {
      url: {
        type: String,
      },
      highUrl: String,
      width: {
        type: Number,
        default: 168
      },
      type: {
        type: String,
        default: "square",
        validator: function (value) {
          return ["circle", "square"].indexOf(value) !== -1;
        }
      },
      selectorStyle: {
        type: Object,
        default() {
          return {};
        }
      },
      outShowStyle: {},
      scale: {
        type: Number,
        default: 3
      },
      moveEvent: {
        type: [Object, MouseEvent],
        default: null
      },
      leaveEvent: {
        type: [Object, MouseEvent],
        default: null
      },
      hideZoom: {
        type: Boolean,
        default: false
      },
      outShow: {
        type: Boolean,
        default: false
      },
      pointer: {
        type: Boolean,
        default: false
      },
      baseline: {
        type: Boolean,
        default: false
      },
      overlayStyle: {
        type: String,
        default: 'width:100%;height:100%'
      },
    },
    data() {
      return {
        selector: {
          width: this.width,
          top: 0,
          left: 0,
          bgTop: 0,
          bgLeft: 0,
          rightBound: 0,
          bottomBound: 0,
          absoluteLeft: 0,
          absoluteTop: 0
        },
        imgInfo: {},
        $img: null,
        screenWidth: document.body.clientWidth,
        outShowInitTop: 0,
        outShowTop: 0,
        hideOutShow: true,
        imgLoadedFlag: false,
        hideSelector: false,
        timer: null
      };
    },
    watch: {
      moveEvent(e) {
        this.mouseMove(e);
      },
      leaveEvent(e) {
        this.mouseLeave(e);
      },
      url(n) {
        this.imgLoadedFlag = false;
        // let img = require('@/assets/vehicle_img/blank_vehicle.jpg')
        // if(n == img){
        //  this.outShow = false
        // }
      },
      width(n) {
        this.initSelectorProperty(n);
      },
      screenWidth(val) {
        if (!this.timer) {
          this.screenWidth = val;
          this.timer = setTimeout(() => {
            this.imgLoaded();
            clearTimeout(this.timer);
            this.timer = null;
          }, 400);
        }
      }
    },
    computed: {
      addWidth() {
        return !this.outShow ? (this.width / 2) * (1 - this.scale) : 0;
      },
      imgSelectorPosition() {
        let { top, left } = this.selector;
        return {
          top: `${top}px`,
          left: `${left}px`
        };
      },
      imgSelectorSize() {
        let width = this.selector.width;
        return {
          width: `${width}px`,
          height: `${width}px`
        };
      },
      imgSelectorStyle() {
        return this.selectorStyle;
      },
      imgOutShowSize() {
        let {
          scale,
          selector: { width }
        } = this;
        return {
          width: `${width * scale}px`,
          height: `${width * scale}px`
        };
      },
      imgOutShowPosition() {
        return {
          top: `${this.outShowTop}px`,
          right: `${-8}px`
        };
      },
      imgBg() {
        return {
          backgroundImage: `url(${this.highUrl || this.url})`
        };
      },
      imgBgSize() {
        let {
          scale,
          imgInfo: { height, width }
        } = this;
        return {
          backgroundSize: `${width * scale}px ${height * scale}px`
        };
      },
      imgBgPosition() {
        let { bgLeft, bgTop } = this.selector;
        return {
          backgroundPosition: `${bgLeft}px ${bgTop}px`
        };
      },
    },
    mounted() {
      this.$img = this.$refs["img"];
    },
    methods: {
      imgLoaded() {
        let imgInfo = this.$img.getBoundingClientRect();
        if (JSON.stringify(this.imgInfo) != JSON.stringify(imgInfo)) {
          this.imgInfo = imgInfo;
          this.initSelectorProperty(this.width);
          this.resetOutShowInitPosition();
        }
        if (!this.imgLoadedFlag) {
          this.imgLoadedFlag = true;
          this.$emit("created", imgInfo);
        }
      },
      mouseMove(e) {
        if (!this.hideZoom && this.imgLoadedFlag) {
          this.imgLoaded();
          const { pageX, pageY, clientY } = e;
          const { scale, selector, outShow, addWidth, outShowAutoScroll } = this;
          let { outShowInitTop } = this;
          const scrollTop = pageY - clientY;
          const { absoluteLeft, absoluteTop, rightBound, bottomBound } = selector;
          const x = pageX - absoluteLeft; // 选择器的x坐标 相对于图片
          const y = pageY - absoluteTop; // 选择器的y坐标
          if (outShow) {
            if (!outShowInitTop) {
              outShowInitTop = this.outShowInitTop = scrollTop + this.imgInfo.top;
            }
            this.hideOutShow && (this.hideOutShow = false);
            this.outShowTop =
              scrollTop > outShowInitTop ? scrollTop - outShowInitTop : 0;
          }
          this.hideSelector && (this.hideSelector = false);
          selector.top = y > 0 ? (y < bottomBound ? y : bottomBound) : 0;
          selector.left = x > 0 ? (x < rightBound ? x : rightBound) : 0;
          selector.bgLeft = addWidth - x * scale; // 选择器图片的坐标位置
          selector.bgTop = addWidth - y * scale;
        }
      },
      initSelectorProperty(selectorWidth) {
        const selectorHalfWidth = selectorWidth / 2;
        const selector = this.selector;
        const { width, height, left, top } = this.imgInfo;
        const { scrollLeft, scrollTop } = document.documentElement;
        selector.width = selectorWidth;
        selector.rightBound = width - selectorWidth;
        selector.bottomBound = height - selectorWidth;
        selector.absoluteLeft = left + selectorHalfWidth + scrollLeft;
        selector.absoluteTop = top + selectorHalfWidth + scrollTop;
      },
      mouseLeave() {
        this.hideSelector = true;
        if (this.outShow) {
          this.hideOutShow = true;
        }
      },
      reset() {
        Object.assign(this.selector, {
          top: 0,
          left: 0,
          bgLeft: 0,
          bgTop: 0
        });
        this.resetOutShowInitPosition();
      },
      resetOutShowInitPosition() {
        this.outShowInitTop = 0;
      },
      imgerrorfun(e) {
        // let img = require('@/assets/vehicle_img/blank_vehicle.jpg')
        // this.url = img
        // e.target.src = img
        // e.target.onerror= null
      }
    }
  };
</script>

<style scoped>
  .img-container { position: relative; }
  .overlay { cursor: crosshair; position: absolute; top: 0; left: 0; opacity: 0.5; z-index: 3; }
  .img-selector { position: absolute; cursor: crosshair; border: 1px solid rgba(0, 0, 0, 0.1); background-repeat: no-repeat; background-color: rgba(64, 64, 64, 0.6);}
  .img-selector.circle {border-radius: 50%;}
  .img-out-show {z-index: 5000;position: absolute;background-repeat: no-repeat;-webkit-background-size: cover;background-color: white;transform: translate(100%, 0);}
  .img-selector-point {position: absolute;width: 4px;height: 4px;top: 50%;left: 50%;transform: translate(-50%, -50%);background-color: black;}
  .img-out-show.base-line::after {position: absolute;box-sizing: border-box;content: "";width: 1px;top: 0;bottom: 0;left: 50%;transform: translateX(-50%);}
  .img-out-show.base-line::before {position: absolute;box-sizing: border-box;content: "";height: 1px;border: 1px dashed rgba(0, 0, 0, 0.36);left: 0;right: 0;top: 50%;transform: translateY(-50%);}
</style>

 

 

 

posted @ 2019-12-02 10:53  放晴的天空  阅读(1235)  评论(1编辑  收藏  举报