<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"
:src="src"
:alt="alt"
:style="{ 'object-fit': fit }">
</div>
</template>
<script>
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';
export default {
name: 'ElImage',
mixins: [Locale],
props: {
// src 图片源,同原生 string
src: String,
// fit 确定图片如何适应容器框,同原生 object-fit string fill / contain / cover / none / scale-down
fit: String,
// lazy 是否开启懒加载 boolean — false
lazy: Boolean,
// scroll-container 开启懒加载后,监听 scroll 事件的容器 string / HTMLElement — 最近一个 overflow 值为 auto 或 scroll 的父元素
scrollContainer: [String, HTMLElement],
// alt 原生 alt string
alt: String
},
data() {
return {
loading: true,
error: false,
show: !this.lazy
};
},
watch: {
// 监听src变化,加载新图片
src: {
handler(val) {
this.show && this.loadImage(val);
},
immediate: true
},
// show变化,加载新图片
show(val) {
val && this.loadImage(this.src);
}
},
mounted() {
// 初始化如果有lazy,执行滚动监听
this.lazy && this.addLazyLoadListener();
},
beforeDestroy() {
this.lazy && this.removeLazyLoadListener();
},
methods: {
// 加载图片
loadImage(val) {
// reset status
this.loading = true;
this.error = false;
const img = new Image();
img.onload = this.handleLoad.bind(this);
img.onerror = this.handleError.bind(this);
img.src = val;
},
// load 图片加载成功触发 (e: Event)
handleLoad(e) {
this.loading = false;
this.$emit('load', e);
},
// error 图片加载失败触发 (e: Error)
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;
// 节流函数,每隔200ms检测一次加载完毕事件移除监听
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;
}
}
};
</script>