Click on the image to enlarge the vue background

Requirements:
Click on the small picture to zoom in. After zooming in, use gestures to zoom in and out, switch pictures left and right, rotate, and close. Due to the low version of element-ui, the image component that uses the picture to enlarge is not supported.
insert image description here

Code
parent component:

<template>
	<div>
		<!-- 放大图 -->
		<el-image-viewer v-if="showImg" :on-close="closeViewer"
                     :src="currItem"
                     :url-list="baseInfo.pictureUrlList"/>
		
		<!-- 小图 -->
		<div class="baseBoxPic">
          <div v-for="(item, index) in baseInfo.pictureUrlList" :key="index" class="picBox">
            <img :src="item" class="litPic" @click="bigImg(item)"/>
          </div>
         </div>
	</div>
</template>

<script>
import ElImageViewer from 'base/image/image-viewer'
export default {
      
      
	data() {
      
      
		return {
      
      
			baseInfo: {
      
      
				pictureUrlList: []
			},
			showImg: false,
     		currItem: ''
		}
	},
	components: {
      
      
   		'ElImageViewer': ElImageViewer
 	},
 	methods: {
      
      
  		// 放大
  		bigImg(e) {
      
      
    		this.currItem = e
    		this.showImg = true
  		},
  		// 关闭
  		closeViewer() {
      
      
    		this.showImg = false
  		},
  	}
}
</script>

Child group
base/image/image-viewer.vue

<template>
  <transition name="viewer-fade">
    <div class="el-image-viewer__wrapper" :style="{ 'z-index': zIndex }">
      <div class="el-image-viewer__mask"></div>
      <!-- CLOSE -->
      <span class="el-image-viewer__btn el-image-viewer__close" @click="hide">
        <i class="el-icon-circle-close"></i>
      </span>
      <!-- ARROW -->
      <template v-if="!isSingle">
        <span class="el-image-viewer__btn el-image-viewer__prev" :class="{ 'is-disabled': !infinite && isFirst }" @click="prev">
          <i class="el-icon-arrow-left" />
        </span>
        <span class="el-image-viewer__btn el-image-viewer__next" :class="{ 'is-disabled': !infinite && isLast }" @click="next">
          <i class="el-icon-arrow-right" />
        </span>
      </template>
      <!-- ACTIONS -->
      <div class="el-image-viewer__btn el-image-viewer__actions">
        <div class="el-image-viewer__actions__inner">
          <i class="el-icon-zoom-out" style="margin-right: 10px;" @click="handleActions('zoomOut')"></i>
          <i class="el-icon-zoom-in" style="margin-right: 10px;" @click="handleActions('zoomIn')"></i>
          <i class="el-icon-refresh" @click="handleActions('clocelise')"></i>
          <i class="el-image-viewer__actions__divider"></i>
          <i :class="mode.icon" @click="toggleMode"></i>
          <i class="el-image-viewer__actions__divider"></i>
          <i class="el-icon-refresh-left" @click="handleActions('anticlocelise')"></i>
          <i class="el-icon-refresh-right" @click="handleActions('clocelise')"></i>
        </div>
      </div>
      <!-- CANVAS -->
      <div class="el-image-viewer__canvas">
        <div v-for="(url, i) in urlList" v-if="i === index">
          <div v-if="isPdf">
            <a target="_blank" :href="currentImg" style="color: #fff; position: absolute;left: 50%; top: 50%">点击新页面查看</a>
          </div>
          <img v-else ref="img" class="el-image-viewer__img" :key="url" :src="currentImg" :style="imgStyle" @load="handleImgLoad" @error="handleImgError" @mousedown="handleMouseDown">
        </div>
      </div>
    </div>
  </transition>
</template>

<script>
import {
      
       on, off } from './dom';
import {
      
       rafThrottle, isFirefox } from './util';
import pdf from 'vue-pdf'

const Mode = {
      
      
  CONTAIN: {
      
      
    name: 'contain',
    icon: 'el-icon-full-screen'
  },
  ORIGINAL: {
      
      
    name: 'original',
    icon: 'el-icon-c-scale-to-original'
  }
};
const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel';
export default {
      
      
  name: 'elImageViewer',
  props: {
      
      
    urlList: {
      
      
      type: Array,
      default: () => []
    },
    zIndex: {
      
      
      type: Number,
      default: 2300
    },
    onSwitch: {
      
      
      type: Function,
      default: () => {
      
       }
    },
    onClose: {
      
      
      type: Function,
      default: () => {
      
       }
    }
  },
  data () {
      
      
    return {
      
      
      index: 0,
      isShow: false,
      infinite: true,
      loading: false,
      mode: Mode.CONTAIN,
      transform: {
      
      
        scale: 1,
        deg: 0,
        offsetX: 0,
        offsetY: 0,
        enableTransition: false
      }
    };
  },
  components: {
      
      
    pdf
  },
  computed: {
      
      
    isSingle () {
      
      
      return this.urlList.length <= 1;
    },
    isFirst () {
      
      
      return this.index === 0;
    },
    isLast () {
      
      
      return this.index === this.urlList.length - 1;
    },
    isPdf() {
      
      
      return this.urlList[this.index] && this.urlList[this.index].toString().indexOf('.pdf') !== -1;
    },
    currentImg () {
      
      
      return this.urlList[this.index]
    },
    isPdf() {
      
      
      return this.urlList[this.index] &&  this.urlList[this.index].toString().indexOf('.pdf')!== -1;
    },
    imgStyle () {
      
      
      const {
      
       scale, deg, offsetX, offsetY, enableTransition } = this.transform;
      const style = {
      
      
        transform: `scale(${ 
        scale}) rotate(${ 
        deg}deg)`,
        transition: enableTransition ? 'transform .3s' : '',
        'margin-left': `${ 
        offsetX}px`,
        'margin-top': `${ 
        offsetY}px`
      };
      if (this.mode === Mode.CONTAIN) {
      
      
        style.maxWidth = style.maxHeight = '100%';
      }
      return style;
    }
  },
  watch: {
      
      
    index: {
      
      
      handler: function (val) {
      
      
        this.reset();
        this.onSwitch(val);
      }
    },
    currentImg (val) {
      
      
      this.$nextTick(_ => {
      
      
        const $img = this.$refs.img[0];
        if (!$img.complete) {
      
      
          this.loading = true;
        }
      });
    }
  },
  methods: {
      
      
    hide () {
      
      
      this.deviceSupportUninstall();
      this.onClose();
    },
    deviceSupportInstall () {
      
      
      this._keyDownHandler = rafThrottle(e => {
      
      
        const keyCode = e.keyCode;
        switch (keyCode) {
      
      
          // ESC
          case 27:
            this.hide();
            break;
          // SPACE
          case 32:
            this.toggleMode();
            break;
          // LEFT_ARROW
          case 37:
            this.prev();
            break;
          // UP_ARROW
          case 38:
            this.handleActions('zoomIn');
            break;
          // RIGHT_ARROW
          case 39:
            this.next();
            break;
          // DOWN_ARROW
          case 40:
            this.handleActions('zoomOut');
            break;
        }
      });
      this._mouseWheelHandler = rafThrottle(e => {
      
      
        const delta = e.wheelDelta ? e.wheelDelta : -e.detail;
        if (delta > 0) {
      
      
          this.handleActions('zoomIn', {
      
      
            zoomRate: 0.015,
            enableTransition: false
          });
        } else {
      
      
          this.handleActions('zoomOut', {
      
      
            zoomRate: 0.015,
            enableTransition: false
          });
        }
      });
      on(document, 'keydown', this._keyDownHandler);
      on(document, mousewheelEventName, this._mouseWheelHandler);
    },
    deviceSupportUninstall () {
      
      
      off(document, 'keydown', this._keyDownHandler);
      off(document, mousewheelEventName, this._mouseWheelHandler);
      this._keyDownHandler = null;
      this._mouseWheelHandler = null;
    },
    handleImgLoad (e) {
      
      
      this.loading = false;
    },
    handleImgError (e) {
      
      
      this.loading = false;
      e.target.alt = '加载失败';
    },
    handleMouseDown (e) {
      
      
      if (this.loading || e.button !== 0) return;
      const {
      
       offsetX, offsetY } = this.transform;
      const startX = e.pageX;
      const startY = e.pageY;
      this._dragHandler = rafThrottle(ev => {
      
      
        this.transform.offsetX = offsetX + ev.pageX - startX;
        this.transform.offsetY = offsetY + ev.pageY - startY;
      });
      on(document, 'mousemove', this._dragHandler);
      on(document, 'mouseup', ev => {
      
      
        off(document, 'mousemove', this._dragHandler);
      });
      e.preventDefault();
    },
    reset () {
      
      
      this.transform = {
      
      
        scale: 1,
        deg: 0,
        offsetX: 0,
        offsetY: 0,
        enableTransition: false
      };
    },
    toggleMode () {
      
      
      if (this.loading) return;
      const modeNames = Object.keys(Mode);
      const modeValues = Object.values(Mode);
      const index = modeValues.indexOf(this.mode);
      const nextIndex = (index + 1) % modeNames.length;
      this.mode = Mode[modeNames[nextIndex]];
      this.reset();
    },
    prev () {
      
      
      if (this.isFirst && !this.infinite) return;
      const len = this.urlList.length;
      this.index = (this.index - 1 + len) % len;
    },
    next () {
      
      
      if (this.isLast && !this.infinite) return;
      const len = this.urlList.length;
      this.index = (this.index + 1) % len;
    },
    handleActions (action, options = {
       
       }) {
      
      
      if (this.loading) return;
      const {
      
       zoomRate, rotateDeg, enableTransition } = {
      
      
        zoomRate: 0.2,
        rotateDeg: 90,
        enableTransition: true,
        ...options
      };
      const {
      
       transform } = this;
      switch (action) {
      
      
        case 'zoomOut':
          if (transform.scale > 0.2) {
      
      
            transform.scale = parseFloat((transform.scale - zoomRate).toFixed(3));
          }
          break;
        case 'zoomIn':
          transform.scale = parseFloat((transform.scale + zoomRate).toFixed(3));
          break;
        case 'clocelise':
          transform.deg += rotateDeg;
          break;
        case 'anticlocelise':
          transform.deg -= rotateDeg;
          break;
      }
      transform.enableTransition = enableTransition;
    }
  },
  mounted () {
      
      
    this.deviceSupportInstall();
  }
};
</script>
<style  scoped>
.el-image__error,
.el-image__inner,
.el-image__placeholder {
      
      
  width: 100%;
  height: 100%;
}

.el-image {
      
      
  position: relative;
  display: inline-block;
  overflow: hidden;
}

.el-image__inner {
      
      
  vertical-align: top;
}

.el-image__inner--center {
      
      
  position: relative;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: block;
}

.el-image__error,
.el-image__placeholder {
      
      
  background: #f5f7fa;
}

.el-image__error {
      
      
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 14px;
  color: #c0c4cc;
  vertical-align: middle;
}

.el-image__preview {
      
      
  cursor: pointer;
}

.el-image-viewer__wrapper {
      
      
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.el-image-viewer__btn {
      
      
  position: absolute;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  opacity: 0.8;
  cursor: pointer;
  box-sizing: border-box;
  user-select: none;
}

.el-image-viewer__close {
      
      
  top: 40px;
  right: 40px;
  width: 40px;
  height: 40px;
  font-size: 40px;
}

.el-image-viewer__canvas {
      
      
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.el-image-viewer__actions {
      
      
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);
  width: 100px;
  height: 44px;
  padding: 0 23px;
  background-color: #606266;
  border-color: #fff;
  border-radius: 22px;
}

.el-image-viewer__actions__inner {
      
      
  width: 100%;
  height: 100%;
  text-align: justify;
  cursor: default;
  font-size: 23px;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: space-around;
}

.el-image-viewer__prev {
      
      
  left: 40px;
}

.el-image-viewer__next,
.el-image-viewer__prev {
      
      
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  font-size: 24px;
  color: #fff;
  background-color: #606266;
  border-color: #fff;
}

.el-image-viewer__next {
      
      
  right: 40px;
  text-indent: 2px;
}

.el-image-viewer__mask {
      
      
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  opacity: 0.5;
  background: #000;
}

.viewer-fade-enter-active {
      
      
  animation: viewer-fade-in 0.3s;
}

.viewer-fade-leave-active {
      
      
  animation: viewer-fade-out 0.3s;
}

@keyframes viewer-fade-in {
      
      
  0% {
      
      
    transform: translate3d(0, -20px, 0);
    opacity: 0;
  }

  to {
      
      
    transform: translateZ(0);
    opacity: 1;
  }
}

@keyframes viewer-fade-out {
      
      
  0% {
      
      
    transform: translateZ(0);
    opacity: 1;
  }

  to {
      
      
    transform: translate3d(0, -20px, 0);
    opacity: 0;
  }
}
</style>

dom.js

/* istanbul ignore next */

import Vue from 'vue';

const isServer = Vue.prototype.$isServer;
const SPECIAL_CHARS_REGEXP = /([:\-_]+(.))/g;
const MOZ_HACK_REGEXP = /^moz([A-Z])/;
const ieVersion = isServer ? 0 : Number(document.documentMode);

/* istanbul ignore next */
export const on = (function () {
    
    
  if (!isServer && document.addEventListener) {
    
    
    return function (element, event, handler) {
    
    
      if (element && event && handler) {
    
    
        element.addEventListener(event, handler, false);
      }
    };
  } else {
    
    
    return function (element, event, handler) {
    
    
      if (element && event && handler) {
    
    
        element.attachEvent('on' + event, handler);
      }
    };
  }
})();

/* istanbul ignore next */
export const off = (function () {
    
    
  if (!isServer && document.removeEventListener) {
    
    
    return function (element, event, handler) {
    
    
      if (element && event) {
    
    
        element.removeEventListener(event, handler, false);
      }
    };
  } else {
    
    
    return function (element, event, handler) {
    
    
      if (element && event) {
    
    
        element.detachEvent('on' + event, handler);
      }
    };
  }
})();

/util.js

import Vue from 'vue';
import {
    
    
  isString,
  isObject
} from './types';

const hasOwnProperty = Object.prototype.hasOwnProperty;

export const isFirefox = function () {
    
    
  return !Vue.prototype.$isServer && !!window.navigator.userAgent.match(/firefox/i);
};

export const autoprefixer = function (style) {
    
    
  if (typeof style !== 'object') return style;
  const rules = ['transform', 'transition', 'animation'];
  const prefixes = ['ms-', 'webkit-'];
  rules.forEach(rule => {
    
    
    const value = style[rule];
    if (rule && value) {
    
    
      prefixes.forEach(prefix => {
    
    
        style[prefix + rule] = value;
      });
    }
  });
  return style;
};

export function rafThrottle(fn) {
    
    
  let locked = false;
  return function (...args) {
    
    
    if (locked) return;
    locked = true;
    window.requestAnimationFrame(_ => {
    
    
      fn.apply(this, args);
      locked = false;
    });
  };
}

/types.js

export function isString(obj) {
    
    
  return Object.prototype.toString.call(obj) === '[object String]';
}
export function isObject(obj) {
    
    
  return Object.prototype.toString.call(obj) === '[object Object]';
}

Guess you like

Origin blog.csdn.net/guairena/article/details/128114446