Vue 實現(xiàn)圖片放大鏡的效果

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

https://github.com/Mater1996/vue-photo-zoom-pro
demo:https://codepen.io/xbup/project/editor/AjnEgE

原插件有一些小bug硝逢,我修改了一下,代碼如下:


image.png

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/PicZoom/vue-photo-zoom-pro'
 export default {
   name: 'PicZoom',
   components: {  vuePhotoZoomPro },
   data() {
     return {
       type: "square",
       showType: true,
     }
   },
   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:3
     },
     overlayStyle:{
       type: String,
       default: 'width:100%;height:100%'
     }
   },
 }
</script>

<style rel="stylesheet/scss" lang="scss" scoped>

</style>

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,
        default: require('@/assets/vehicle_img/blank_vehicle.jpg')
      },
      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;
    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>

引用

<pic-zoom :url="storageUrl" :bigWidth="150" :scale="3"  overlayStyle=" width: 100%;height: 430px;border-radius: 3px;" ></pic-zoom>

import PicZoom from '@/components/PicZoom'

components: { VehicleInfo ,PicZoom},

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市斩跌,隨后出現(xiàn)的幾起案子晾捏,更是在濱河造成了極大的恐慌炫加,老刑警劉巖歹袁,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗤军,死亡現(xiàn)場離奇詭異残黑,居然都是意外死亡馍佑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門梨水,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拭荤,“玉大人,你說我怎么就攤上這事疫诽【耸溃” “怎么了旦委?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長雏亚。 經常有香客問我缨硝,道長,這世上最難降的妖魔是什么罢低? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任追葡,我火速辦了婚禮,結果婚禮上奕短,老公的妹妹穿的比我還像新娘宜肉。我一直安慰自己,他們只是感情好翎碑,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布谬返。 她就那樣靜靜地躺著,像睡著了一般日杈。 火紅的嫁衣襯著肌膚如雪遣铝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天莉擒,我揣著相機與錄音酿炸,去河邊找鬼。 笑死涨冀,一個胖子當著我的面吹牛填硕,可吹牛的內容都是我干的。 我是一名探鬼主播鹿鳖,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼扁眯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了翅帜?” 一聲冷哼從身側響起姻檀,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涝滴,沒想到半個月后绣版,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡歼疮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年杂抽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腋妙。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡默怨,死狀恐怖,靈堂內的尸體忽然破棺而出骤素,到底是詐尸還是另有隱情匙睹,我是刑警寧澤愚屁,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站痕檬,受9級特大地震影響霎槐,放射性物質發(fā)生泄漏。R本人自食惡果不足惜梦谜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一丘跌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唁桩,春花似錦闭树、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至单山,卻和暖如春碍现,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背米奸。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工昼接, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悴晰。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓慢睡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親膨疏。 傳聞我的和親對象是個殘疾皇子一睁,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350