Vue后台如何实现点击图片放大功能
短信预约 -IT技能 免费直播动态提醒
本文小编为大家详细介绍“Vue后台如何实现点击图片放大功能”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue后台如何实现点击图片放大功能”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
需求
点击小图可以放大,放大后,通过手势等比例放大缩小、左右切换图、旋转、关闭。由于element-ui版本较低不支持使用图片放大的image组件。
代码
父组件
<template><div><!-- 放大图 --><el-image-viewer v-if="showImg" :on-close="closeViewer" :class="lazy" data-src="currItem" :url-list="baseInfo.pictureUrlList"/><!-- 小图 --><div class="baseBoxPic"> <div v-for="(item, index) in baseInfo.pictureUrlList" :key="index" class="picBox"> <img :class="lazy" data-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>
子组件
base/image/image-viewer.vue
<template> <transition name="viewer-fade"> <div class="el-image-viewer__wrapper" :> <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" @click="handleActions('zoomOut')"></i> <i class="el-icon-zoom-in" @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" rel="external nofollow" >点击新页面查看</a> </div> <img v-else ref="img" class="el-image-viewer__img" :key="url" :class="lazy" data-src="currentImg" : @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
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);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); } }; }})();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]';}
读到这里,这篇“Vue后台如何实现点击图片放大功能”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341