产品提了这样一个需求——点击按钮查看图片,我们都知道 element-ui 中有点击图片查看大图功能,我们看源码就知道 image 组件运用了内部的 ImageViewer 组件,一看源码我们就知道这个 ImageViewer 组件就是个图片查看器,那么我们就可以直接引入这个 ImageViewer 内部组件,用 button 来触发就能完美的实现我们的功能
element-ui 源码位置:
packages/image/src/main.vue
<template>
<div class="el-image">
<slot v-if="loading" name="placeholder">
<div class="el-image__placeholder"></div>
</slot>
<slot v-else-if="error" name="error">
<div class="el-image__error">{{ t('el.image.error') }}</div>
</slot>
<img
v-else
class="el-image__inner"
v-bind="$attrs"
v-on="$listeners"
@click="clickHandler"
:src="src"
:style="imageStyle"
:class="{ 'el-image__inner--center': alignCenter, 'el-image__preview': preview }">
<template v-if="preview">
⭐️⭐️⭐️<image-viewer :z-index="zIndex" :initial-index="imageIndex" v-if="showViewer" :on-close="closeViewer" :url-list="previewSrcList"/>
</template>
</div>
</template>
<script>
⭐️⭐️⭐️ import ImageViewer from './image-viewer';
import Locale from 'element-ui/src/mixins/locale';
import { on, off, getScrollContainer, isInContainer } from 'element-ui/src/utils/dom';
import { isString, isHtmlElement } from 'element-ui/src/utils/types';
import throttle from 'throttle-debounce/throttle';
const isSupportObjectFit = () => document.documentElement.style.objectFit !== undefined;
const ObjectFit = {
NONE: 'none',
CONTAIN: 'contain',
COVER: 'cover',
FILL: 'fill',
SCALE_DOWN: 'scale-down'
};
let prevOverflow = '';
export default {
name: 'ElImage',
mixins: [Locale],
inheritAttrs: false,
components: {
ImageViewer
},
props: {
src: String,
fit: String,
lazy: Boolean,
scrollContainer: {},
previewSrcList: {
type: Array,
default: () => []
},
zIndex: {
type: Number,
default: 2000
}
},
data() {
return {
loading: true,
error: false,
show: !this.lazy,
imageWidth: 0,
imageHeight: 0,
showViewer: false
};
},
computed: {
imageStyle() {
const { fit } = this;
if (!this.$isServer && fit) {
return isSupportObjectFit()
? { 'object-fit': fit }
: this.getImageStyle(fit);
}
return {};
},
alignCenter() {
return !this.$isServer && !isSupportObjectFit() && this.fit !== ObjectFit.FILL;
},
⭐️⭐️⭐️preview() {
const { previewSrcList } = this;
return Array.isArray(previewSrcList) && previewSrcList.length > 0;
},
imageIndex() {
let previewIndex = 0;
const srcIndex = this.previewSrcList.indexOf(this.src);
if (srcIndex >= 0) {
previewIndex = srcIndex;
}
return previewIndex;
}
},
watch: {
src(val) {
this.show && this.loadImage();
},
show(val) {
val && this.loadImage();
}
},
mounted() {
if (this.lazy) {
this.addLazyLoadListener();
} else {
this.loadImage();
}
},
beforeDestroy() {
this.lazy && this.removeLazyLoadListener();
},
methods: {
loadImage() {
if (this.$isServer) return;
// reset status
this.loading = true;
this.error = false;
const img = new Image();
img.onload = e => this.handleLoad(e, img);
img.onerror = this.handleError.bind(this);
// bind html attrs
// so it can behave consistently
Object.keys(this.$attrs)
.forEach((key) => {
const value = this.$attrs[key];
img.setAttribute(key, value);
});
img.src = this.src;
},
handleLoad(e, img) {
this.imageWidth = img.width;
this.imageHeight = img.height;
this.loading = false;
this.error = false;
},
handleError(e) {
this.loading = false;
this.error = true;
this.$emit('error', e);
},
handleLazyLoad() {
if (isInContainer(this.$el, this._scrollContainer)) {
this.show = true;
this.removeLazyLoadListener();
}
},
addLazyLoadListener() {
if (this.$isServer) return;
const { scrollContainer } = this;
let _scrollContainer = null;
if (isHtmlElement(scrollContainer)) {
_scrollContainer = scrollContainer;
} else if (isString(scrollContainer)) {
_scrollContainer = document.querySelector(scrollContainer);
} else {
_scrollContainer = getScrollContainer(this.$el);
}
if (_scrollContainer) {
this._scrollContainer = _scrollContainer;
this._lazyLoadHandler = throttle(200, this.handleLazyLoad);
on(_scrollContainer, 'scroll', this._lazyLoadHandler);
this.handleLazyLoad();
}
},
removeLazyLoadListener() {
const { _scrollContainer, _lazyLoadHandler } = this;
if (this.$isServer || !_scrollContainer || !_lazyLoadHandler) return;
off(_scrollContainer, 'scroll', _lazyLoadHandler);
this._scrollContainer = null;
this._lazyLoadHandler = null;
},
/**
* simulate object-fit behavior to compatible with IE11 and other browsers which not support object-fit
*/
getImageStyle(fit) {
const { imageWidth, imageHeight } = this;
const {
clientWidth: containerWidth,
clientHeight: containerHeight
} = this.$el;
if (!imageWidth || !imageHeight || !containerWidth || !containerHeight) return {};
const imageAspectRatio = imageWidth / imageHeight;
const containerAspectRatio = containerWidth / containerHeight;
if (fit === ObjectFit.SCALE_DOWN) {
const isSmaller = imageWidth < containerWidth && imageHeight < containerHeight;
fit = isSmaller ? ObjectFit.NONE : ObjectFit.CONTAIN;
}
switch (fit) {
case ObjectFit.NONE:
return { width: 'auto', height: 'auto' };
case ObjectFit.CONTAIN:
return (imageAspectRatio < containerAspectRatio) ? { width: 'auto' } : { height: 'auto' };
case ObjectFit.COVER:
return (imageAspectRatio < containerAspectRatio) ? { height: 'auto' } : { width: 'auto' };
default:
return {};
}
},
⭐️⭐️⭐️clickHandler() {
// don't show viewer when preview is false
if (!this.preview) {
return;
}
// prevent body scroll
prevOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
this.showViewer = true;
},
closeViewer() {
document.body.style.overflow = prevOverflow;
this.showViewer = false;
}
}
};
</script>
上代码:
<template>
<div>
<el-button @click="look">查看</el-button>
<image-viewer v-if="showViewer" :on-close="closeViewer" :url-list="imgUrl" />
</div>
</template>
<script>
// 导入组件
import imageViewer from 'element-ui/packages/image/src/image-viewer'
export default {
components: { imageViewer },
data() {
return {
showViewer: false, // 显示查看器
imgUrl: ['https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg']
}
},
methods: {
look() {
this.showViewer = true
},
// 关闭查看器
closeViewer() {
this.showViewer = false
}
}
}
</script>