vue 放大镜(简易)

目录

zoom组件

<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, default: require('@/assets/images/default-zoom.jpg') }, // 图片地址
    highUrl: String, // 更清晰的图片,若不提供会采用 url
    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 }, // 当需要在外部监听移动事件时,请通过该字段传入事件(必须包含 pageX,pageY,clientY),这将禁用内部移动监听
    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%' },
  },
  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` };
    },
  },
  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
    };
  },
  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.οnerrοr= 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);
      }
    }
  },
  mounted() {
    this.$img = this.$refs["img"];
  },
};
</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;
    border: 1px dashed rgba(0, 0, 0, 0.36);
    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>
<template>
  <div class='wrapper'>
    <div class='box'>
      <!-- <pic-zoom
        :url="storageUrl"
        :bigWidth="150"
        :scale="3"
        overlayStyle="width: 100%;height: 400px;border-radius: 3px;"></pic-zoom> -->
      <pic-zoom
        :width="150"
        :url="url"
        :scale="3"
        type="square"
        :pointer="true"
        :out-show="true"
        overlayStyle="width: 100%;height: 100%;"></pic-zoom>
    </div>
  </div>
</template>

<script>
import PicZoom from '@/components/PicZoom'
export default {
  components: { PicZoom },
  data() {
    return {
      url: 'https://bpic.588ku.com/illus_water_img/18/08/18/472f21d353b64c95b9994be6ed3b7274.jpg!/watermark/url/L3dhdGVyL3dhdGVyX2JhY2tfNDAwXzIwMC5wbmc=/repeat/true',
    }
  },
}
</script>

<style lang='scss' scoped>
.box {
  width: 400px;
  height: 400px;
}
</style>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值