• vue后台实现点击图片放大


    需求:
    点击小图可以放大,放大后,通过手势等比例放大缩小、左右切换图、旋转、关闭。由于element-ui版本较低不支持使用图片放大的image组件。
    在这里插入图片描述

    代码
    父组件:

    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    子组件
    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>
          
          <span class="el-image-viewer__btn el-image-viewer__close" @click="hide">
            <i class="el-icon-circle-close">i>
          span>
          
          <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>
          
          <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>
          
          <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456

    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);
          }
        };
      }
    })();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    /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;
        });
      };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    /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]';
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor
    如何保存/同步多架构容器 Docker 镜像
    PwnTheBox 刷题记录crypto篇
    1759E(方案枚举)
    回收站不见了怎么恢复?一招把回收站的图标找回来!
    Lagging issues of Ubuntu 20.04 on VMWare Player(卡顿问题)
    全网独家首发!一份破解大厂面试官千层套路的Spring源码笔记
    计算机网络自顶向下实例
    运维SRE-11 备份服务及备份项目
    java培训技术ModelAttribute注解修饰POJO类型的入参
  • 原文地址:https://blog.csdn.net/guairena/article/details/128114446